universal-plugin 0.12.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,36 +1,42 @@
1
1
  # version skill
2
2
 
3
- Move the version a plugin releases under, keeping every file that carries one in sync.
3
+ Stamp the version a plugin releases under into every file that carries one.
4
4
 
5
- ## The two authored numbers
5
+ ## Derive versus stamp
6
6
 
7
- A plugin's version appears in up to five places; only two are authored — the canonical `plugin.json`
8
- and, when `packagePath` is declared, that `package.json`. The vendor manifests, the local marketplace
9
- catalogs, and the `npx`/`upx` pins inside `skills/**/SKILL.md` are all derived.
7
+ `plugin build` derives the vendor manifests from `plugin.json` and never decides a version.
8
+ `plugin version` stamps: one call writes the number into the canonical `plugin.json`, the
9
+ `packagePath` `package.json`, every `npx`/`upx <pkg>@<version>` pin in the skills, `.plugin/pins.json`,
10
+ and each `mcpServers` entry marked `pinToPluginVersion`, then re-derives through `plugin build`.
10
11
 
11
- That is why nothing here is hand-edited: editing one authored file leaves the other stale, and every
12
- derived artifact with it. A consumer's plugin cache is keyed by version, so a content change without
13
- a matching bump is invisible to them.
12
+ That is why nothing here is hand-edited. A consumer's plugin cache is keyed by version, so a content
13
+ change without a matching bump is invisible to them.
14
14
 
15
- ## Two release models
15
+ ## Three sources for the number
16
16
 
17
- The first question the skill asks is whether the repository uses changesets.
17
+ | Source | Call | Used by |
18
+ |---|---|---|
19
+ | Author bump | `plugin version minor` | Plain repositories |
20
+ | Explicit number | `plugin version 1.4.0` | semantic-release (`@semantic-release/exec` `prepareCmd`) |
21
+ | The package's `package.json` | `plugin version --from-package` | changesets, release-please |
18
22
 
19
- - **With changesets** — the number is decided by the release, not by this skill. Add a changeset, let
20
- the release run, and `publish sync-version` carries the released number into the canonical
21
- manifest and re-derives the vendor manifests. With `--no-build`, `plugin build` is the next step.
22
- - **Without** — `plugin version <bump>` is the whole step. `scripts/version.mjs` runs it from the CLI
23
- shipped beside the skill, so nothing is downloaded.
23
+ With `packagePath` declared, a release type is refused: bump the package with the release tool, then
24
+ run `--from-package`. The skill checks for `.changeset/` and `packagePath` before it picks a call.
24
25
 
25
- Running `plugin version` in a changesets repository would decide a number changesets is about to
26
- decide again. The skill checks for `.changeset/` before anything else.
26
+ semantic-release stamps files that exist only in the npm package unless `@semantic-release/git`
27
+ commits them back. Git-sourced installs read `plugin.json` from the repository, so they need that
28
+ commit-back.
29
+
30
+ `plugin bundle` and `publish sync-version` are deprecated aliases and are removed in the next minor
31
+ release.
27
32
 
28
33
  ## The part that is not automatable
29
34
 
30
35
  Which release type to use is a promise to consumers about what broke. The skill offers the semver
31
- reading — breaking → major, new behavior → minor, fix only → patch, adjusted for `0.x` — and asks
36
+ reading (breaking → major, new behavior → minor, fix only → patch, adjusted for `0.x`) and asks
32
37
  rather than guessing.
33
38
 
34
39
  ## References
35
40
 
41
+ - [ADR-0010](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/design/decisions/0010-version-policy.md)
36
42
  - [`plugin version` spec](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/plugin/version/README.md)
@@ -1,54 +1,82 @@
1
1
  ---
2
2
  name: version
3
- description: Use this skill to move a universal agent plugin's version — bump it, set an explicit one, cut a release number, or reconcile a version that drifted between the canonical plugin.json and its npm package.json. Handles both release models: a changesets repository, where the number is decided by the release and carried in with publish sync-version, and a plain repository, where plugin version does the whole move. Trigger on "bump the plugin version", "release a new version of this plugin", "set the plugin version to 1.0.0", "cut a patch release", or "the manifest and package.json versions disagree".
3
+ description: Use this skill to move a universal agent plugin's version — bump it, set an explicit one, cut a release number, or reconcile a version that drifted between the canonical plugin.json and its npm package.json. Handles every release model: a plain repository, where the author picks the number, and a release tool (changesets, release-please, semantic-release) that decides it and hands it to plugin version to stamp everywhere. Trigger on "bump the plugin version", "release a new version of this plugin", "set the plugin version to 1.0.0", "cut a patch release", or "the manifest and package.json versions disagree".
4
4
  ---
5
5
 
6
6
  # Move a plugin's version
7
7
 
8
- Bump or set the version a plugin releases under, keeping every file that carries one in sync.
8
+ Stamp the version a plugin releases under into every file that carries one.
9
9
 
10
- A plugin's version lives in up to five places, but only **two are authored** — the canonical root
11
- `plugin.json` and, when the project declares a `packagePath`, that `package.json`. The per-vendor
12
- manifests, the local marketplace catalogs, and the `npx`/`upx` pins inside `skills/**/SKILL.md` are
13
- all **derived**. So never hand-edit a version: editing one file leaves the other authored file and
14
- every derived artifact stale, and a consumer's plugin cache is keyed by version, so a content change
15
- without a matching bump is invisible to them.
10
+ The seam is **derive versus stamp**. `plugin build` derives the vendor manifests from `plugin.json`
11
+ and never decides a version. `plugin version` is the one command that decides where a number goes: a
12
+ single call writes it into every place, then re-derives through `plugin build`. So never hand-edit a
13
+ version. Editing one file leaves the rest stale, and a consumer's plugin cache is keyed by version, so
14
+ a content change without a matching bump is invisible to them.
16
15
 
17
- ## Step 0 — Does this repo use changesets?
16
+ One call writes:
17
+
18
+ - the canonical `plugin.json`, and the `packagePath` `package.json` when you pass a release type or an
19
+ explicit number;
20
+ - every `npx`/`upx <pkg>@<version>` pin in the text files under the skills directory (SKILL.md, other
21
+ Markdown, json/yaml, and `.js`/`.mjs`/`.cjs`/`.ts`/`.mts`/`.cts`/`.sh`/`.py` scripts). The plugin's
22
+ own package moves to the new version, other pnpm-workspace CLIs to their workspace versions, and
23
+ external packages are left alone. A skill with `metadata.pin-exempt: true` is skipped;
24
+ - `.plugin/pins.json`;
25
+ - the version each compiled skill script (`scripts` in the extension) embeds in its header — only the
26
+ header moves;
27
+ - each authored `mcpServers` entry marked `pinToPluginVersion: true`, inline or in the mcp file. The
28
+ marker stays.
29
+
30
+ ## Step 1 — Pick the source of the number
31
+
32
+ Ask who decides the number. There are three sources, and the release model picks one:
33
+
34
+ | Source | Call | Used by |
35
+ |---|---|---|
36
+ | Author bump | `plugin version minor` | Plain repositories, with no `packagePath` |
37
+ | Explicit number | `plugin version 1.4.0` | semantic-release |
38
+ | The package's `package.json` | `plugin version --from-package` | changesets, release-please |
18
39
 
19
40
  ```bash
20
41
  test -d .changeset && echo "changesets"
42
+ cat .agents/universal-plugin.json # is packagePath declared?
21
43
  ```
22
44
 
23
- **If it does**, the version number is decided by changesets, not by you. Add a changeset and let the
24
- release run — the repo's `version` script should already call `publish sync-version`, which carries
25
- the released number from `package.json` into the canonical manifest and re-derives the vendor
26
- manifests, the same way `plugin version` does:
45
+ With `packagePath` declared, a release type (`patch`, `minor`, `major`, `pre*`) is **refused**: the
46
+ package owns the number. Bump the package with the release tool, then run `--from-package`. An
47
+ explicit number and `--from-package` stay allowed. `--from-package` reads `<packagePath>/package.json`,
48
+ or the plugin root's `package.json` when no `packagePath` is declared, and refuses `0.0.0-development`.
27
49
 
28
- ```bash
29
- npx universal-plugin publish sync-version
30
- ```
50
+ Wire a release tool once; after that the release runs the stamp for you:
51
+
52
+ - **changesets.** Root `version` script: `changeset version && universal-plugin plugin version --from-package --root <plugin>`.
53
+ `init-universal-plugin --npm` writes it.
54
+ - **release-please.** Run `universal-plugin plugin version --from-package` after the release PR
55
+ bumps the package.
56
+ - **semantic-release.** Add `@semantic-release/exec` with
57
+ `prepareCmd: "universal-plugin plugin version ${nextRelease.version}"`.
31
58
 
32
- If the script passes `--no-build`, it must run `plugin build` next (`/universal-plugin:build-plugin`);
33
- otherwise the vendor manifests and `com.github.copilot/` keep the old version, and a consumer's
34
- plugin cache never re-extracts.
59
+ In a changesets repository, add a changeset and let the release run. Do not run `plugin version minor`
60
+ there: it would decide a number changesets is about to decide again.
35
61
 
36
- Do **not** run `plugin version` in a changesets repo — it would decide a number changesets is about
37
- to decide again.
62
+ ### semantic-release must commit the stamp back
38
63
 
39
- **If it does not**, `plugin version` is the whole release-number step. Continue below.
64
+ The stamped files exist only in the npm package unless `@semantic-release/git` commits them back.
65
+ Git-sourced installs (Copilot CLI, GitHub-hosted marketplaces) read `plugin.json` from the repository,
66
+ so for those channels the commit-back is required. Without it they are unsupported. Set `assets` to
67
+ `plugin.json`, the vendor manifests, `skills/**`, `.plugin/pins.json`, and the mcp file.
40
68
 
41
- ## Step 1 — Move the version
69
+ ## Step 2 — Run it
42
70
 
43
71
  ```bash
44
- node scripts/version.mjs <bump>
72
+ node scripts/version.mjs <bump-or-number>
73
+ node scripts/version.mjs --from-package
45
74
  ```
46
75
 
47
- Resolve that path against this skill's own directory; it runs the CLI that shipped beside it, so
48
- nothing is downloaded. `npx universal-plugin plugin version <bump>` is the fallback. The command
49
- never prompts, so it is safe to run unattended once the release type is decided.
50
-
51
- `<bump>` is either a semver release type or an explicit version:
76
+ Resolve that path against this skill's own directory; it runs the project's installed CLI
77
+ (`pnpm exec universal-plugin`), else `npx universal-plugin@0.13.0` pinned to this skill's version.
78
+ `universal-plugin plugin version` on the `PATH` is the fallback. The command never prompts, so it is safe to run unattended once the
79
+ source is decided.
52
80
 
53
81
  | `<bump>` | From `1.2.3` you get |
54
82
  |---|---|
@@ -58,29 +86,29 @@ never prompts, so it is safe to run unattended once the release type is decided.
58
86
  | `prerelease --preid beta` | `1.2.4-beta.0` |
59
87
  | `2.0.0-rc.1` (explicit) | `2.0.0-rc.1` |
60
88
 
61
- Useful flags:
62
-
63
89
  | Flag | Effect |
64
90
  |---|---|
91
+ | `--from-package` | Take the version the package's `package.json` already holds |
65
92
  | `--preid <id>` | Prerelease identifier for the `pre*` release types |
66
- | `--dry-run` | Print the plan, write nothing — run this first when unsure |
93
+ | `--dry-run` | Print the plan, write nothing. Run this first when unsure |
67
94
  | `--force` | Allow a target that does not advance on the current version |
68
- | `--no-build` | Skip re-deriving the vendor manifests (you will run `plugin build` yourself) |
69
- | `--format json` | Machine-readable result: `from`, `to`, `written` |
95
+ | `--no-build` | Skip re-deriving the vendor manifests (run `plugin build` yourself) |
96
+ | `--format json` | Machine-readable result |
97
+ | `--root <path>` | Plugin root directory |
70
98
 
71
- **Which release type?** That is the user's call, not the command's and not yours to guess — a
72
- version is a promise to consumers about what broke. If the user has not said, ask, and offer the
73
- semver reading of the change: breaking → `major`, new behavior → `minor`, fix only → `patch` (on a
74
- `0.x` plugin, breaking → `minor` and everything else → `patch`).
99
+ **Which release type?** In a plain repository that is the user's call, not the command's and not yours
100
+ to guess. A version is a promise to consumers about what broke. If the user has not said, ask, and
101
+ offer the semver reading of the change: breaking → `major`, new behavior → `minor`, fix only →
102
+ `patch` (on a `0.x` plugin, breaking → `minor` and everything else → `patch`).
75
103
 
76
- ## Step 2 — Confirm what moved
104
+ ## Step 3 — Confirm what moved
77
105
 
78
106
  The command reports every file it wrote and every manifest it re-derived. Expect the canonical
79
- manifest, the `packagePath` `package.json` if one is declared, and one derived manifest per declared
80
- harness:
107
+ manifest, the `packagePath` `package.json` for a release type or explicit number, the pinned skill
108
+ files, `.plugin/pins.json`, and one derived manifest per declared harness:
81
109
 
82
110
  ```bash
83
- node scripts/version.mjs patch --format json
111
+ node scripts/version.mjs --from-package --format json
84
112
  ```
85
113
 
86
114
  Default stdout is TOON, which is the machine contract this CLI documents; `--format json` is there
@@ -99,15 +127,19 @@ Every guard resolves before the first write, so a failed run leaves the tree unt
99
127
  | no version to bump from | the manifest has never carried a `version` | pass an explicit version (`plugin version 0.1.0`) to set the first one |
100
128
  | an unknown version or release type | the argument is neither a release type nor valid semver | use one of the values in the table above |
101
129
  | a target that does not advance | the requested version is not greater than the current one | pick a higher version, or pass `--force` if the user genuinely wants to move backward |
130
+ | a release type under `packagePath` | the package owns the number | bump the package with the release tool, then `plugin version --from-package` |
131
+ | `0.0.0-development` from `--from-package` | semantic-release's placeholder, not a release | pass the explicit number instead |
102
132
  | a missing `package.json` at `packagePath` | `.agents/universal-plugin.json` names a package directory that does not exist | fix `packagePath`, or create the package |
103
133
 
104
134
  ## Do not
105
135
 
106
- - **Hand-edit a `version` field.** Two authored files and every derived artifact fall out of sync.
136
+ - **Hand-edit a `version` field.** The other places fall out of sync.
107
137
  - **Write a derived manifest.** `.claude-plugin/plugin.json` and its siblings belong to
108
138
  `plugin build`; the bump already re-derived them.
109
- - **Run `npm version`.** It knows only `package.json` and leaves the canonical manifest — the actual
110
- source of truth — stale.
139
+ - **Run `npm version`.** It knows only `package.json` and leaves the canonical manifest and the
140
+ skill pins stale.
141
+ - **Use the deprecated commands.** `plugin bundle` and `publish sync-version` print a warning and are
142
+ removed in the next minor release. `sync-version` now runs `plugin version --from-package`.
111
143
 
112
144
  ## Related skills
113
145
 
@@ -115,11 +147,12 @@ Every guard resolves before the first write, so a failed run leaves the tree unt
115
147
  |------|-------|
116
148
  | Create, adopt, or change what the plugin declares | `init-universal-plugin` |
117
149
  | Re-derive the vendor manifests by hand | `build-plugin` |
118
- | Check whether the two authored versions agree | `doctor-universal-plugin` |
150
+ | Check whether every place agrees (`unstamped`, `version-drift`) | `doctor-universal-plugin` |
119
151
  | Add a changeset for the change being released | `add-changeset` |
120
152
  | Refresh the repository's own marketplace catalogs after a bump | `marketplace` |
121
153
  | List the released plugin in the shared marketplace | `publish-plugin` |
122
154
 
123
155
  ## References
124
156
 
157
+ - [ADR-0010](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/design/decisions/0010-version-policy.md), the derive/stamp seam
125
158
  - Spec: [`plugin/version/`](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/plugin/version/README.md)
@@ -1,11 +1,52 @@
1
1
  #!/usr/bin/env node
2
- // Runs `universal-plugin plugin version` from the CLI that ships beside this skill, so a release
3
- // number never depends on a network fetch or on which version `npx` happens to resolve.
4
- import { dirname, join } from 'node:path'
5
- import { fileURLToPath } from 'node:url'
6
-
7
- // <package>/skills/<skill>/scripts/version.mjs: four levels up is the package root.
8
- const packageRoot = dirname(dirname(dirname(dirname(fileURLToPath(import.meta.url)))))
9
-
10
- process.argv.splice(2, 0, 'plugin', 'version')
11
- await import(join(packageRoot, 'bin', 'universal-plugin.mjs'))
2
+ // Compiled by `universal-plugin plugin build` from src/skill-scripts/run-cli.ts. Edit the source, not this file.
3
+ globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.13.0","entry":"skills/version/scripts/version.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
4
+ import { spawnSync } from "node:child_process";
5
+ //#region src/compile/scripts.ts
6
+ /** The global the header assigns the script data to. Framework-neutral: a script's own code, or the
7
+ * library it inlines, reads it; universal-plugin depends on neither. */
8
+ const SCRIPT_GLOBAL = "__SKILL_SCRIPT__";
9
+ //#endregion
10
+ //#region src/skill-scripts/cli-runner.ts
11
+ /** The data `plugin build` embedded in this compiled script. */
12
+ function skillScript() {
13
+ const data = globalThis[SCRIPT_GLOBAL];
14
+ if (data === void 0) throw new Error("this script runs only as compiled by `plugin build`");
15
+ return data;
16
+ }
17
+ /** Runs the universal-plugin CLI. A skill may be copied away from its package, so it never reaches
18
+ * for a CLI beside it: the project's installed CLI first, then the one pinned to this script's
19
+ * version, which `plugin version` stamps. */
20
+ function runCli(args, options = {}) {
21
+ const { bin, package: pkg, version } = skillScript();
22
+ const name = bin ?? "universal-plugin";
23
+ if (spawnSync("pnpm", [
24
+ "exec",
25
+ name,
26
+ "--version"
27
+ ], { encoding: "utf8" }).status === 0) return spawnSync("pnpm", [
28
+ "exec",
29
+ name,
30
+ ...args
31
+ ], {
32
+ encoding: "utf8",
33
+ ...options
34
+ });
35
+ const pinned = `${pkg ?? "universal-plugin"}@${version ?? "latest"}`;
36
+ return spawnSync("npx", [pinned, ...args], {
37
+ encoding: "utf8",
38
+ ...options
39
+ });
40
+ }
41
+ //#endregion
42
+ //#region src/skill-scripts/run-cli.ts
43
+ const script = skillScript();
44
+ const command = Object.entries(script.commands).find(([, entry]) => entry === script.entry)?.[0];
45
+ if (command === void 0) {
46
+ process.stderr.write(`${script.entry} declares no command in the scripts map\n`);
47
+ process.exit(1);
48
+ }
49
+ const result = runCli([...command.split(" "), ...process.argv.slice(2)], { stdio: "inherit" });
50
+ process.exit(result.status ?? 1);
51
+ //#endregion
52
+ export {};