universal-plugin 0.12.1 → 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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/agents/agentskills-specialist.md +4 -16
- package/com.github.copilot/agents/agentskills-specialist.agent.md +4 -16
- package/dist/cli.mjs +5140 -4033
- package/package.json +3 -2
- package/plugin.json +30 -1
- package/readme.md +7 -6
- package/references/plugin-design.md +21 -9
- package/schema/extension.schema.json +31 -0
- package/skills/adopt-upx/SKILL.md +4 -2
- package/skills/adopt-upx/scripts/rewrite-upx.mjs +92 -129
- package/skills/build-plugin/SKILL.md +20 -11
- package/skills/build-plugin/scripts/build.mjs +51 -10
- package/skills/doctor-universal-plugin/SKILL.md +43 -13
- package/skills/doctor-universal-plugin/scripts/doctor.mjs +249 -365
- package/skills/init-universal-plugin/SKILL.md +18 -7
- package/skills/init-universal-plugin/references/create.md +8 -5
- package/skills/init-universal-plugin/references/vendors/copilot-cli.md +2 -2
- package/skills/init-universal-plugin/scripts/init.mjs +51 -10
- package/skills/marketplace/SKILL.md +1 -1
- package/skills/marketplace/scripts/add.mjs +51 -10
- package/skills/marketplace/scripts/install-docs.mjs +100 -96
- package/skills/marketplace/scripts/marketplace.mjs +51 -10
- package/skills/marketplace/scripts/validate.mjs +51 -10
- package/skills/migrate-plugin/SKILL.md +1 -1
- package/skills/remove-plugin/SKILL.md +1 -1
- package/skills/upgrade-plugin/SKILL.md +2 -0
- package/skills/version/README.md +24 -18
- package/skills/version/SKILL.md +79 -46
- package/skills/version/scripts/version.mjs +51 -10
|
@@ -1,11 +1,52 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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/build-plugin/scripts/build.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 {};
|
|
@@ -19,8 +19,9 @@ and that judgment belongs to the skill that owns the write.
|
|
|
19
19
|
node scripts/doctor.mjs
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
Resolve that path against this skill's own directory. It
|
|
23
|
-
|
|
22
|
+
Resolve that path against this skill's own directory. It asks the project's installed CLI
|
|
23
|
+
(`pnpm exec universal-plugin`), else the CLI pinned to this skill's version (`npx`), about the
|
|
24
|
+
current working directory; add `--root <path>` to diagnose elsewhere.
|
|
24
25
|
Add `--marketplace-root <path>` (repeatable) to also check a **separately-cloned** shared marketplace
|
|
25
26
|
repository (e.g. a local clone of `cyberuni/marketplace`) — see
|
|
26
27
|
[Catalogs are checked at the repository root](#catalogs-are-checked-at-the-repository-root).
|
|
@@ -58,7 +59,7 @@ to derive. Never report it as a fault.
|
|
|
58
59
|
A plugin that **does** declare those reports `copilot-cli` as `built` at `com.github.copilot/`
|
|
59
60
|
instead. Declaring the canonical `$schema` moves them there: Copilot CLI stops reading them from the
|
|
60
61
|
plugin root entirely, so a root-only layout loads none of them and says nothing
|
|
61
|
-
([ADR-0015](
|
|
62
|
+
([ADR-0015](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/design/decisions/0015-copilot-spec-mode-namespace.md)). That is what
|
|
62
63
|
`copilot-root-components` reports.
|
|
63
64
|
|
|
64
65
|
If `node` is unavailable, read `scripts/doctor.mjs` and apply the same checks by hand: it composes
|
|
@@ -78,14 +79,19 @@ Each `code` below is what the script emits.
|
|
|
78
79
|
| `hand-edited` | a derived manifest that `build` would rewrite — the edit is already lost, it just has not been overwritten yet | move the field to the canonical manifest or to `harnesses.<vendor>`, then rebuild |
|
|
79
80
|
| `unknown-vendor` | a `vendors` entry no build target matches; reported as `skipped` plus a warning | fix the id in `plugin.json` |
|
|
80
81
|
| `unsupported-component` | a component path on the shared extension names a component one targeted vendor has none of (`agents` for Codex, `rules` for Claude Code, …); the build leaves it out of that vendor's manifest | declare the path under `harnesses.<vendor>` for the vendors that read it, or leave it — nothing is lost |
|
|
82
|
+
| `stale-script` | a compiled skill script (`scripts` in the extension) is missing, or no longer matches what its source compiles to. A script that differs only in its embedded version is `version-drift` instead, never both | `/universal-plugin:build-plugin`, then commit the compiled script |
|
|
83
|
+
| `script-compile-failed` | a declared script entry could not be compiled into one self-contained file — usually an import that does not resolve from the plugin root | fix the entry, then `/universal-plugin:build-plugin` |
|
|
84
|
+
| `script-over-budget` | a compiled skill script is over the 150 KB budget | have the skill call the installed CLI instead: `pnpm exec <bin>` with a pinned `npx` fallback |
|
|
85
|
+
| `skill-link-escapes` | a relative Markdown link in a skill climbs out of the skill folder (`../../readme.md`). pnpm links or copies each skill folder on its own, and a plugin cache keeps only what ships, so the link resolves to nothing there | copy the file into the skill and link it relatively, or link it by URL |
|
|
81
86
|
| `undeliverable-override` | `harnesses["copilot-cli"]` sets fields that reach nothing | `/universal-plugin:init-universal-plugin`, update route — move them to a vendor that has a derived manifest, or drop them |
|
|
82
87
|
| `codex-fields-missing` | Codex is targeted without `version` or `description`; the build fails and writes **nothing at all**, including for the other vendors | add both to the canonical top level |
|
|
83
|
-
| `version-drift` | the
|
|
88
|
+
| `version-drift` | one file carries a different version from the release's number — the canonical manifest, a derived vendor manifest, a catalog entry, a skill pin, a marked `mcpServers` entry, or the version a compiled skill script embeds; one finding per file, named by path | `/universal-plugin:version` (for a package-sourced drift, `universal-plugin plugin version --from-package`); then [offer the CI check](#offer-the-check-to-ci) |
|
|
89
|
+
| `unstamped` | a place holds `0.0.0-development`, semantic-release's placeholder. The release has not stamped it yet, so it is not drift (severity low) | in `prepareCmd`, `plugin version ${nextRelease.version}`, and commit the stamped files back with `@semantic-release/git`; see `/universal-plugin:version` |
|
|
84
90
|
| `unreleased-content` | shipped content was committed after the commit that set the current version — a consumer keyed on that version never re-extracts it | `/universal-plugin:version` |
|
|
85
91
|
| `copilot-root-components` | agents, commands, rules, hooks, or LSP servers sit at the plugin root with no copy under `com.github.copilot/` — Copilot CLI reads them only from there in spec mode, so it loads none of them, silently | `/universal-plugin:build-plugin` |
|
|
86
92
|
| `stale-github-plugin` | a leftover `.github/plugin/plugin.json` from an older build — shadowed by root and no longer generated | `/universal-plugin:remove-plugin` |
|
|
87
93
|
| `shadowing-manifest` | a `.plugin/plugin.json` exists — it outranks root in Copilot CLI's search order and silently shadows the canonical manifest | `/universal-plugin:remove-plugin` |
|
|
88
|
-
| `no-vendors` | no vendor is declared, so the build writes nothing and no runtime reads the plugin.
|
|
94
|
+
| `no-vendors` | no vendor is declared, so the build writes nothing and no runtime reads the plugin. When the repository carries `vendorExtensions` or a `.plugin/plugin.json`, the build stops rather than reporting an empty result, and the detail says so — read it beside `legacy-manifest` and `shadowing-manifest`, which name the signals | `/universal-plugin:init-universal-plugin`, adopt route when `vendorExtensions` or `.plugin/plugin.json` is present, else update route |
|
|
89
95
|
| `package-path-missing` | `packagePath` names a directory with no readable `package.json` | fix `packagePath`, or create the package |
|
|
90
96
|
| `package-path-unknown` | the CLI could not report `packagePath` (a version too old to read it), so `version-drift` and `unreleased-content` were skipped rather than guessed | upgrade universal-plugin |
|
|
91
97
|
| `misplaced-package-path` | `plugin.json` declares `packagePath` under `extensions["org.cyberuni.universal-plugin"]`, where the CLI never reads it — the plugin is silently treated as not shipping to npm | move it to `.agents/universal-plugin.json`, relative to the plugin root |
|
|
@@ -137,20 +143,44 @@ user can run, or ask before running it yourself.
|
|
|
137
143
|
|
|
138
144
|
## Version drift
|
|
139
145
|
|
|
140
|
-
|
|
141
|
-
`packagePath` the CLI reports (`config get --key packagePath`), resolved from the plugin root.
|
|
142
|
-
them
|
|
146
|
+
A version lives in five places. Two are authored: the canonical `plugin.json`, and the `package.json`
|
|
147
|
+
at the `packagePath` the CLI reports (`config get --key packagePath`), resolved from the plugin root.
|
|
148
|
+
The rest are stamped from them by `plugin version`: the vendor manifests, this plugin's entry in each
|
|
149
|
+
repository catalog, the `npx`/`upx <package>@<version>` pins in the skills, and each `mcpServers`
|
|
150
|
+
entry marked `pinToPluginVersion`.
|
|
143
151
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
152
|
+
The script asks `universal-plugin publish check-version` for the comparison and emits one
|
|
153
|
+
`version-drift` per file that disagrees. The reference is the release's number: the `package.json`
|
|
154
|
+
when `packagePath` is declared, else `plugin.json`. Pins are compared only when there is a package to
|
|
155
|
+
pin, and a pin-exempt skill or a pin that is not a version (`<version>`, `latest`) is skipped. A place
|
|
156
|
+
holding `0.0.0-development` is reported as `unstamped`, not as `version-drift`.
|
|
157
|
+
|
|
158
|
+
Files diverge when someone ran `npm version`, or when a release tool (changesets, release-please,
|
|
159
|
+
semantic-release) moved `package.json` and nothing stamped the number on. All of it is
|
|
160
|
+
`/universal-plugin:version`'s to fix — never patch one file by hand to match another.
|
|
161
|
+
|
|
162
|
+
### Offer the check to CI
|
|
163
|
+
|
|
164
|
+
`doctor` is advisory and always exits 0. `publish check-version` is the same comparison, read-only,
|
|
165
|
+
and exits 1 on drift, so a repository can fail the release PR instead of publishing a plugin that
|
|
166
|
+
claims the old version. Once the version findings are reported, check whether the repository
|
|
167
|
+
already runs it: search its `package.json` scripts and `.github/workflows/` for `check-version`. If
|
|
168
|
+
it does not, offer to add it — ask first, because both are the user's files:
|
|
169
|
+
|
|
170
|
+
- a `verify` (or `check`) script step: `universal-plugin publish check-version --root <plugin-root>`;
|
|
171
|
+
- or a CI step after install on pull requests, for the release PR a changesets, release-please, or
|
|
172
|
+
semantic-release bot opens.
|
|
173
|
+
|
|
174
|
+
Use the locked `universal-plugin` from the repository's devDependencies when it has one; otherwise
|
|
175
|
+
`npx universal-plugin@0.13.0`, pinned. This check matters most where the release tool picks the
|
|
176
|
+
number, because nothing else runs between that bump and publish.
|
|
147
177
|
|
|
148
178
|
## Unreleased content
|
|
149
179
|
|
|
150
180
|
A runtime keys its plugin cache on the version, not on content: Claude Code resolves the version,
|
|
151
181
|
finds it unchanged, and reports *"already at the latest version"* without re-extracting. So content
|
|
152
182
|
pushed without a bump reaches nobody who already installed the plugin, and neither side is told
|
|
153
|
-
([ADR-0010](
|
|
183
|
+
([ADR-0010](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/design/decisions/0010-version-policy.md) §6).
|
|
154
184
|
|
|
155
185
|
The script compares the shipped paths — the canonical manifest, the skills directory, `agents/`,
|
|
156
186
|
`governances/`, `references/`, `mcp.json` — against the commit that set the version the manifest carries now, and
|
|
@@ -158,7 +188,7 @@ emits `unreleased-content` for anything committed since. Uncommitted work is not
|
|
|
158
188
|
shipped.
|
|
159
189
|
|
|
160
190
|
Two cases are deliberately silent. A plugin that declares `packagePath` is skipped, because there the
|
|
161
|
-
release picks the number (ADR-0010 §2) and content waiting ahead of the last released version is the
|
|
191
|
+
release picks the number ([ADR-0010](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/design/decisions/0010-version-policy.md) §2) and content waiting ahead of the last released version is the
|
|
162
192
|
normal state of a branch. A tree with no git history is skipped rather than guessed at.
|
|
163
193
|
|
|
164
194
|
The repair is the bump, and it belongs to `/universal-plugin:version`. Judge first whether the change
|