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.
@@ -116,6 +116,16 @@ no approval; report those rather than asking.
116
116
  One case needs asking even when nothing is overwritten: two vendor manifests that disagree on a
117
117
  shared field. A silent pick there is a silent behavior change for one runtime.
118
118
 
119
+ An npm-shipped plugin (`--npm`) in a changesets repository gets one more plan line. When the
120
+ repository root carries `.changeset/` and its `package.json` `version` script is exactly
121
+ `changeset version`, `plugin init --npm` rewrites that script to
122
+ `changeset version && universal-plugin plugin version --from-package --root <plugin-root>` and adds
123
+ `universal-plugin` to the root `devDependencies`. Without it the first Version Packages PR moves
124
+ `package.json` and leaves the plugin version behind. Show the new script and the devDependency in the
125
+ plan, and ask before running init: it rewrites the user's root `package.json`. If the user declines,
126
+ run init without `--npm` and wire `files` by hand. A custom `version` script is never rewritten;
127
+ init prints the line to append, so relay it and let the user place it.
128
+
119
129
  ## 4. Apply
120
130
 
121
131
  Route by what Phase 2 found. Read exactly the reference for the work at hand — do not load all six.
@@ -130,7 +140,7 @@ Three asks leave this skill rather than routing inside it. Hand them over instea
130
140
  route: **`doctor-universal-plugin`** reports what is declared, built, stale, or drifting; **`version`** moves the
131
141
  number; **`remove-plugin`** deletes derived manifests or the plugin itself.
132
142
 
133
- Every route ends in the same two commands. Scaffold with the CLI that shipped beside this skill:
143
+ Every route ends in the same two commands. Scaffold with this skill's launcher, which runs the project's installed CLI or the pinned `npx` one:
134
144
 
135
145
  ```bash
136
146
  node scripts/init.mjs --name <plugin-name> --vendor claude-code --vendor cursor
@@ -163,7 +173,8 @@ marketplace name, the owner, and every other entry alone, so it is safe over a c
163
173
  edited. Generating catalogs for a repository that already holds several plugins, and writing the
164
174
  README install section, is the `marketplace` skill's job.
165
175
 
166
- `plugin init` writes a **minimal** manifest — `$schema`, `name`, and the vendor list. It never reads
176
+ `plugin init` writes a **minimal** manifest — `$schema`, `name`, the vendor list, and, with `--npm`,
177
+ the package's `version`. It never reads
167
178
  an existing vendor manifest, so shared metadata and per-vendor overrides are carried in by hand
168
179
  afterwards, per the reference you routed to.
169
180
 
@@ -42,11 +42,14 @@ for. The universal minimum is `skills/<name>/SKILL.md` plus `.mcp.json`.
42
42
  node scripts/init.mjs --name <plugin-name> --vendor claude-code --vendor cursor --scaffold
43
43
  ```
44
44
 
45
- Resolve `scripts/init.mjs` against this skill's directory; it runs the CLI that shipped beside it.
46
- `npx universal-plugin plugin init` is the fallback. Add `--npm` when an npm package ships the plugin,
47
- which also wires that `package.json`'s `files` to carry the derived manifests.
48
-
49
- `init` writes a minimal manifest — `$schema`, `name`, and the vendor list. Fill in the Step 1
45
+ Resolve `scripts/init.mjs` against this skill's directory; it runs the project's installed CLI, else
46
+ the pinned `npx universal-plugin@0.13.0`. Add `--npm` when an npm package ships the plugin,
47
+ which also wires that `package.json`'s `files` to carry the derived manifests, copies its `version`
48
+ into the manifest, and, in a changesets repository, wires the root `version` script to run
49
+ `plugin version --from-package` (SKILL.md Phase 3 asks before that rewrite).
50
+
51
+ `init` writes a minimal manifest — `$schema`, `name`, the vendor list, and the package `version`
52
+ under `--npm`. Fill in the Step 1
50
53
  metadata and the `harnesses` overrides by hand afterwards; [`standard.md`](./standard.md) shows the
51
54
  finished shape.
52
55
 
@@ -146,13 +146,13 @@ The namespace **replaces** the root rather than supplementing it, and an explici
146
146
  the manifest does **not** opt back into root loading. The move landed in **1.0.80-0** and the
147
147
  runtime's own changelog labels it breaking. Evidence and its confidence, including which kinds were
148
148
  proven by experiment:
149
- [`.research/copilot-spec-mode-namespace/`](../../../../../../.research/copilot-spec-mode-namespace/conclusion.md).
149
+ [`.research/copilot-spec-mode-namespace/`](https://github.com/cyberuni/universal-plugin/blob/main/.research/copilot-spec-mode-namespace/conclusion.md).
150
150
  The published CLI plugin reference still says spec support is "additive on top of standard plugin
151
151
  loading" and documents only the root layout — it is stale; do not build against it.
152
152
 
153
153
  ### What the build derives
154
154
 
155
- `plugin build` derives the tree ([ADR-0015](../../../../.agents/spec/design/decisions/0015-copilot-spec-mode-namespace.md)),
155
+ `plugin build` derives the tree ([ADR-0015](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/design/decisions/0015-copilot-spec-mode-namespace.md)),
156
156
  so **authoring stays at the canonical locations**:
157
157
 
158
158
  - `agents`, `commands` and `rules` are copied, resolved through the same `pathValue` contract
@@ -1,11 +1,52 @@
1
1
  #!/usr/bin/env node
2
- // Runs `universal-plugin plugin init` from the CLI that ships beside this skill, so a scaffold
3
- // 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/init.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', 'init')
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/init-universal-plugin/scripts/init.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 {};
@@ -1,11 +1,52 @@
1
1
  #!/usr/bin/env node
2
- // Runs `universal-plugin marketplace add` from the CLI that ships beside this skill, so listing a
3
- // plugin 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/add.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, 'marketplace', 'add')
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/marketplace/scripts/add.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 {};
@@ -1,115 +1,119 @@
1
1
  #!/usr/bin/env node
2
- // Derives README install instructions from the marketplace catalogs a repository already carries.
3
- // Every command emitted here is traceable to vendor documentation; see
4
- // .research/local-marketplaces/evidence.md. Do not add a command without an evidence row.
5
- import { execFileSync } from 'node:child_process'
6
- import * as fs from 'node:fs'
7
- import * as path from 'node:path'
8
-
9
- const argv = process.argv.slice(2)
10
- const rootFlag = argv.indexOf('--root')
11
- const root = path.resolve(rootFlag === -1 ? process.cwd() : (argv[rootFlag + 1] ?? process.cwd()))
12
- const slugFlag = argv.indexOf('--repo')
13
-
2
+ // Compiled by `universal-plugin plugin build` from src/skill-scripts/install-docs.ts. Edit the source, not this file.
3
+ globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.13.0","entry":"skills/marketplace/scripts/install-docs.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 { execFileSync } from "node:child_process";
5
+ import * as fs from "node:fs";
6
+ import * as path from "node:path";
7
+ //#region src/skill-scripts/install-docs.ts
8
+ const argv = process.argv.slice(2);
9
+ const rootFlag = argv.indexOf("--root");
10
+ const root = path.resolve(rootFlag === -1 ? process.cwd() : argv[rootFlag + 1] ?? process.cwd());
11
+ const slugFlag = argv.indexOf("--repo");
14
12
  const readJson = (file) => {
15
13
  try {
16
- return JSON.parse(fs.readFileSync(file, 'utf8'))
14
+ return JSON.parse(fs.readFileSync(file, "utf8"));
17
15
  } catch {
18
- return null
16
+ return null;
19
17
  }
20
- }
21
-
18
+ };
22
19
  /** owner/repo, from the flag, the manifest's repository field, or the git remote. */
23
20
  function resolveSlug() {
24
- if (slugFlag !== -1 && argv[slugFlag + 1]) return argv[slugFlag + 1]
25
- const manifest = readJson(path.join(root, 'plugin.json'))
26
- const repo = typeof manifest?.repository === 'string' ? manifest.repository : manifest?.repository?.url
27
- const fromManifest = repo?.match(/github\.com[/:]([^/]+\/[^/.]+)/)?.[1]
28
- if (fromManifest) return fromManifest
21
+ if (slugFlag !== -1 && argv[slugFlag + 1]) return argv[slugFlag + 1];
22
+ const manifest = readJson(path.join(root, "plugin.json"));
23
+ const fromManifest = (typeof manifest?.repository === "string" ? manifest.repository : manifest?.repository?.url)?.match(/github\.com[/:]([^/]+\/[^/.]+)/)?.[1];
24
+ if (fromManifest) return fromManifest;
29
25
  try {
30
- const remote = execFileSync('git', ['-C', root, 'remote', 'get-url', 'origin'], {
31
- encoding: 'utf8',
32
- stdio: ['ignore', 'pipe', 'ignore'],
33
- })
34
- return remote.match(/github\.com[/:]([^/]+\/[^/.\s]+)/)?.[1] ?? null
26
+ return execFileSync("git", [
27
+ "-C",
28
+ root,
29
+ "remote",
30
+ "get-url",
31
+ "origin"
32
+ ], {
33
+ encoding: "utf8",
34
+ stdio: [
35
+ "ignore",
36
+ "pipe",
37
+ "ignore"
38
+ ]
39
+ }).match(/github\.com[/:]([^/]+\/[^/.\s]+)/)?.[1] ?? null;
35
40
  } catch {
36
- return null
41
+ return null;
37
42
  }
38
43
  }
39
-
40
- const slug = resolveSlug()
41
- const repoRef = slug ?? '<owner>/<repo>'
42
-
43
- const claude = readJson(path.join(root, '.claude-plugin/marketplace.json'))
44
- const copilot = readJson(path.join(root, '.github/plugin/marketplace.json'))
45
- // Codex reads its own catalog and the Claude one. Either makes a repository installable from Codex;
46
- // prefer its own when both exist. See references/runtimes.md (E-CODEX-M10).
47
- const codexOwn = readJson(path.join(root, '.agents/plugins/marketplace.json'))
48
- const codex = codexOwn ?? claude
49
- const cursor = readJson(path.join(root, '.cursor-plugin/marketplace.json'))
50
-
51
- const targets = []
52
- const sections = []
53
-
54
- const pluginNames = (catalog) => (catalog?.plugins ?? []).map((p) => p.name).filter(Boolean)
55
-
44
+ const slug = resolveSlug();
45
+ const repoRef = slug ?? "<owner>/<repo>";
46
+ const claude = readJson(path.join(root, ".claude-plugin/marketplace.json"));
47
+ const copilot = readJson(path.join(root, ".github/plugin/marketplace.json"));
48
+ const codex = readJson(path.join(root, ".agents/plugins/marketplace.json")) ?? claude;
49
+ const cursor = readJson(path.join(root, ".cursor-plugin/marketplace.json"));
50
+ const targets = [];
51
+ const sections = [];
52
+ const pluginNames = (catalog) => (catalog?.plugins ?? []).map((p) => p.name).filter(Boolean);
56
53
  if (claude) {
57
- const names = pluginNames(claude)
58
- const installs = (names.length > 0 ? names : ['<plugin>']).map((n) => `/plugin install ${n}@${claude.name}`)
59
- targets.push('claude-code')
60
- sections.push(['**Claude Code**', '', '```', `/plugin marketplace add ${repoRef}`, ...installs, '```'].join('\n'))
54
+ const names = pluginNames(claude);
55
+ const installs = (names.length > 0 ? names : ["<plugin>"]).map((n) => `/plugin install ${n}@${claude.name}`);
56
+ targets.push("claude-code");
57
+ sections.push([
58
+ "**Claude Code**",
59
+ "",
60
+ "```",
61
+ `/plugin marketplace add ${repoRef}`,
62
+ ...installs,
63
+ "```"
64
+ ].join("\n"));
61
65
  }
62
-
63
66
  if (copilot) {
64
- const names = pluginNames(copilot)
65
- const installs = (names.length > 0 ? names : ['<plugin>']).map((n) => `copilot plugin install ${n}@${copilot.name}`)
66
- targets.push('copilot-cli')
67
- sections.push(
68
- ['**GitHub Copilot CLI**', '', '```', `copilot plugin marketplace add ${repoRef}`, ...installs, '```'].join('\n'),
69
- )
67
+ const names = pluginNames(copilot);
68
+ const installs = (names.length > 0 ? names : ["<plugin>"]).map((n) => `copilot plugin install ${n}@${copilot.name}`);
69
+ targets.push("copilot-cli");
70
+ sections.push([
71
+ "**GitHub Copilot CLI**",
72
+ "",
73
+ "```",
74
+ `copilot plugin marketplace add ${repoRef}`,
75
+ ...installs,
76
+ "```"
77
+ ].join("\n"));
70
78
  }
71
-
72
79
  if (codex) {
73
- const names = pluginNames(codex)
74
- // Codex installs with `plugin add`; Copilot CLI installs with `plugin install`. Not interchangeable.
75
- const installs = (names.length > 0 ? names : ['<plugin>']).map((n) => `codex plugin add ${n}@${codex.name}`)
76
- targets.push('codex')
77
- sections.push(
78
- [
79
- '**Codex**',
80
- '',
81
- '```',
82
- `codex plugin marketplace add ${repoRef}`,
83
- ...installs,
84
- '```',
85
- '',
86
- 'Start a new session before using the plugin.',
87
- ].join('\n'),
88
- )
80
+ const names = pluginNames(codex);
81
+ const installs = (names.length > 0 ? names : ["<plugin>"]).map((n) => `codex plugin add ${n}@${codex.name}`);
82
+ targets.push("codex");
83
+ sections.push([
84
+ "**Codex**",
85
+ "",
86
+ "```",
87
+ `codex plugin marketplace add ${repoRef}`,
88
+ ...installs,
89
+ "```",
90
+ "",
91
+ "Start a new session before using the plugin."
92
+ ].join("\n"));
89
93
  }
90
-
91
94
  if (cursor) {
92
- targets.push('cursor')
93
- sections.push(
94
- [
95
- '**Cursor**',
96
- '',
97
- 'This repository carries a Cursor catalog, but Cursor has no command that adds it from a',
98
- 'local path: a team admin imports the repository from the Cursor dashboard. To try the plugin',
99
- 'before then, run `npx universal-plugin plugin install --vendor cursor` in a clone, then',
100
- 'reload the window.',
101
- ].join('\n'),
102
- )
95
+ targets.push("cursor");
96
+ sections.push([
97
+ "**Cursor**",
98
+ "",
99
+ "This repository carries a Cursor catalog, but Cursor has no command that adds it from a",
100
+ "local path: a team admin imports the repository from the Cursor dashboard. To try the plugin",
101
+ "before then, run `npx universal-plugin plugin install --vendor cursor` in a clone, then",
102
+ "reload the window."
103
+ ].join("\n"));
103
104
  }
104
-
105
- const markdown = sections.length === 0 ? '' : ['## Install', '', sections.join('\n\n'), ''].join('\n')
106
-
107
- process.stdout.write(
108
- `${JSON.stringify({
109
- root,
110
- repo: slug,
111
- repoResolved: slug !== null,
112
- targets,
113
- markdown,
114
- })}\n`,
115
- )
105
+ const markdown = sections.length === 0 ? "" : [
106
+ "## Install",
107
+ "",
108
+ sections.join("\n\n"),
109
+ ""
110
+ ].join("\n");
111
+ process.stdout.write(`${JSON.stringify({
112
+ root,
113
+ repo: slug,
114
+ repoResolved: slug !== null,
115
+ targets,
116
+ markdown
117
+ })}\n`);
118
+ //#endregion
119
+ export {};
@@ -1,11 +1,52 @@
1
1
  #!/usr/bin/env node
2
- // Runs `universal-plugin marketplace init` from the CLI that ships beside this skill, so catalog
3
- // generation 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/marketplace.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, 'marketplace', 'init')
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/marketplace/scripts/marketplace.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 {};
@@ -1,11 +1,52 @@
1
1
  #!/usr/bin/env node
2
- // Runs `universal-plugin marketplace validate` from the CLI that ships beside this skill, so the
3
- // check 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/validate.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, 'marketplace', 'validate')
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/marketplace/scripts/validate.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 {};
@@ -174,7 +174,7 @@ as a follow-up instead of doing it inside this migration.
174
174
  Point every version and build step at the new plugin root:
175
175
 
176
176
  - Set the manifest extension's `packagePath` to `"."`.
177
- - `universal-plugin publish sync-version --root <pkg>` reads `packagePath` from
177
+ - `universal-plugin plugin version --from-package --root <pkg>` reads `packagePath` from
178
178
  `<pkg>/.agents/universal-plugin.json`, not from the manifest extension. With
179
179
  no `packagePath` it reads `<pkg>/package.json`, so a package that holds its
180
180
  own plugin needs no config file for it. If the repository instead runs its
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: upgrade-plugin
3
3
  description: Use this skill when upgrading pinned `npx universal-plugin@<version>` calls across a project to a newer version.
4
+ metadata:
5
+ pin-exempt: true
4
6
  ---
5
7
 
6
8
  # Upgrade Universal Plugin