universal-plugin 0.6.0 → 0.8.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/bin/upx.mjs +12 -5
- package/com.github.copilot/agents/agentskills-specialist.agent.md +132 -0
- package/dist/cli.mjs +5806 -261
- package/governances/plugin-design.md +14 -0
- package/package.json +4 -1
- package/plugin.json +1 -1
- package/readme.md +12 -5
- package/schema/README.md +10 -0
- package/schema/claude-code-marketplace.json +1939 -0
- package/schema/extension.schema.json +842 -0
- package/skills/adopt-upx/README.md +4 -4
- package/skills/adopt-upx/SKILL.md +4 -4
- package/skills/doctor/README.md +3 -4
- package/skills/doctor/SKILL.md +44 -14
- package/skills/doctor/scripts/doctor.mjs +145 -37
- package/skills/{init → init-universal-plugin}/README.md +5 -2
- package/skills/{init → init-universal-plugin}/SKILL.md +38 -13
- package/skills/init-universal-plugin/references/adopt.md +206 -0
- package/skills/{init → init-universal-plugin}/references/detection.md +8 -2
- package/skills/{init → init-universal-plugin}/references/standard.md +5 -1
- package/skills/{init → init-universal-plugin}/references/vendors/claude-code.md +1 -1
- package/skills/init-universal-plugin/references/vendors/copilot-cli.md +201 -0
- package/skills/marketplace/SKILL.md +1 -1
- package/skills/migrate-plugin/SKILL.md +140 -26
- package/skills/migrate-plugin/evals/evals.json +6 -0
- package/skills/migrate-plugin/evals/trigger-queries.json +18 -0
- package/skills/publish-plugin/README.md +35 -0
- package/skills/publish-plugin/SKILL.md +118 -19
- package/skills/publish-plugin/evals/evals.json +12 -0
- package/skills/remove-plugin/README.md +1 -1
- package/skills/remove-plugin/SKILL.md +2 -2
- package/skills/version/SKILL.md +2 -2
- package/dist/run.mjs +0 -271
- package/skills/init/references/adopt.md +0 -118
- package/skills/init/references/vendors/copilot-cli.md +0 -53
- /package/skills/{init → init-universal-plugin}/assets/templates/agent.md +0 -0
- /package/skills/{init → init-universal-plugin}/assets/templates/command.md +0 -0
- /package/skills/{init → init-universal-plugin}/assets/templates/hooks.json +0 -0
- /package/skills/{init → init-universal-plugin}/assets/templates/plugin.json +0 -0
- /package/skills/{init → init-universal-plugin}/assets/templates/setup-command.md +0 -0
- /package/skills/{init → init-universal-plugin}/assets/templates/skill.md +0 -0
- /package/skills/{init → init-universal-plugin}/references/create.md +0 -0
- /package/skills/{init → init-universal-plugin}/references/frontmatter.md +0 -0
- /package/skills/{init → init-universal-plugin}/references/update.md +0 -0
- /package/skills/{init → init-universal-plugin}/references/vendors/codex.md +0 -0
- /package/skills/{init → init-universal-plugin}/references/vendors/cursor.md +0 -0
- /package/skills/{init → init-universal-plugin}/scripts/init.mjs +0 -0
|
@@ -6,13 +6,13 @@ Rewrites `npx <pkg>@<version>` references in `SKILL.md` files to a caret range o
|
|
|
6
6
|
|
|
7
7
|
## When to use
|
|
8
8
|
|
|
9
|
-
When you want a project's skills to call CLIs via `upx` instead of `npx`, for the
|
|
10
|
-
on repeated invocations (local-first resolution vs. `npx`'s
|
|
9
|
+
When you want a project's skills to call CLIs via `upx` instead of `npx`, for the speed win
|
|
10
|
+
on repeated invocations (local-first resolution vs. `npx`'s registry+spawn cost per call, even
|
|
11
11
|
cached).
|
|
12
12
|
|
|
13
13
|
## What it does
|
|
14
14
|
|
|
15
|
-
1. Confirms `upx` is installed (`npm i -g
|
|
15
|
+
1. Confirms `upx` is installed (`npm i -g @repobuddy/upx`) and on PATH.
|
|
16
16
|
2. Rewrites `npx <pkg>@<concrete-semver>` → `upx <pkg>@^<major>` (or `^0.<minor>` for a 0.x pin)
|
|
17
17
|
across a chosen scope:
|
|
18
18
|
- one specific skill (a path)
|
|
@@ -29,7 +29,7 @@ cached).
|
|
|
29
29
|
## Tradeoff
|
|
30
30
|
|
|
31
31
|
A rewritten skill depends on `upx` being on PATH. `npx` ships with every npm install; `upx` only
|
|
32
|
-
exists after `npm i -g
|
|
32
|
+
exists after `npm i -g @repobuddy/upx`. This is an opt-in migration, not a safe default.
|
|
33
33
|
|
|
34
34
|
## Install
|
|
35
35
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: adopt-upx
|
|
3
|
-
description: Use this skill when the user wants to make their skills use upx — the fast local-first package runner shipped
|
|
3
|
+
description: Use this skill when the user wants to make their skills use upx — the fast local-first package runner shipped as @repobuddy/upx. Trigger on phrases like "make my skills use upx", "adopt the upx runner", "speed up npx calls", "switch to upx", or "rewrite npx pins to upx". Rewrites `npx <pkg>@<version>` references to a caret range on `upx` (`^<major>`, or `^0.<minor>` for a 0.x pin) across one skill, a named set, or every skill in the project.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Adopt upx
|
|
@@ -11,7 +11,7 @@ fast local-first runner shipped by `universal-plugin` (see the package
|
|
|
11
11
|
|
|
12
12
|
## When to use
|
|
13
13
|
|
|
14
|
-
The user wants their project's skills to shell out via `upx` instead of `npx`, for the
|
|
14
|
+
The user wants their project's skills to shell out via `upx` instead of `npx`, for the speed
|
|
15
15
|
win on repeated calls. This is an opt-in migration, not a default — see Tradeoff below before
|
|
16
16
|
running it broadly.
|
|
17
17
|
|
|
@@ -20,7 +20,7 @@ running it broadly.
|
|
|
20
20
|
Install the runner:
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
npm i -g
|
|
23
|
+
npm i -g @repobuddy/upx
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
This puts the `upx` bin on PATH. Verify:
|
|
@@ -94,7 +94,7 @@ a no-op (there's no `npx` left to match), so it's safe to run again after adding
|
|
|
94
94
|
|
|
95
95
|
A skill rewritten to `upx` now depends on the `upx` bin being on that environment's PATH.
|
|
96
96
|
`npx` always ships with npm — every Node environment has it. `upx` does not — it only exists after
|
|
97
|
-
`npm i -g
|
|
97
|
+
`npm i -g @repobuddy/upx`. So this is a deliberate opt-in for environments where
|
|
98
98
|
`universal-plugin` is installed globally, not a safe-by-default swap.
|
|
99
99
|
|
|
100
100
|
Mitigating factor: `upx` itself falls back to plain `npx` on a miss (no local/global install
|
package/skills/doctor/README.md
CHANGED
|
@@ -16,7 +16,7 @@ The skill supplies the judgment around it: which finding matters, and which skil
|
|
|
16
16
|
|
|
17
17
|
## It never repairs
|
|
18
18
|
|
|
19
|
-
Every finding names the skill that fixes it — `init` for anything that rewrites the manifest,
|
|
19
|
+
Every finding names the skill that fixes it — `init-universal-plugin` for anything that rewrites the manifest,
|
|
20
20
|
`version` for the release number, `remove-plugin` for artifacts. A repair can overwrite a manifest
|
|
21
21
|
the user maintains, and that judgment belongs to the skill that owns the write.
|
|
22
22
|
|
|
@@ -29,9 +29,8 @@ The checks are deterministic: same tree, same findings. The one check that is no
|
|
|
29
29
|
definitive staleness test — rebuild on a clean tree and read the diff — because it writes. The skill
|
|
30
30
|
reports that one as a repair for the user to run.
|
|
31
31
|
|
|
32
|
-
Manifest validation is
|
|
33
|
-
|
|
34
|
-
competing with it.
|
|
32
|
+
Manifest validation is a CLI capability (`plugin validate`). This script stays a thin composition
|
|
33
|
+
on purpose, so it folds into that command rather than competing with it.
|
|
35
34
|
|
|
36
35
|
## Boundaries
|
|
37
36
|
|
package/skills/doctor/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: doctor
|
|
3
|
-
description: Use this skill to diagnose a universal agent plugin — when a runtime loads none of the plugin's skills, when a vendor manifest is missing or looks out of date after a pull,
|
|
3
|
+
description: Use this skill to diagnose a universal agent plugin — when `plugin build` reports "built 0" or "nothing to build", when it warns "No vendors declared in harnesses", when a repository still carries `.plugin/plugin.json` or a top-level `vendorExtensions` block after upgrading universal-plugin across a major, when a released version never reached the vendor manifests, when a runtime loads none of the plugin's skills, when a vendor manifest is missing or looks out of date after a pull, or when checking whether what the canonical plugin.json declares still matches what is on disk for Claude Code, Cursor, Codex, and GitHub Copilot CLI. Trigger on "is my plugin set up right", "why isn't my plugin loading", "the build says built 0", "why did nothing get built", "check the plugin", "are the vendor manifests current", or "what does this plugin declare".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Plugin Doctor
|
|
@@ -21,6 +21,9 @@ node scripts/doctor.mjs
|
|
|
21
21
|
|
|
22
22
|
Resolve that path against this skill's own directory. It runs the CLI that shipped beside it against
|
|
23
23
|
the current working directory, so nothing is downloaded; add `--root <path>` to diagnose elsewhere.
|
|
24
|
+
Add `--marketplace-root <path>` (repeatable) to also check a **separately-cloned** shared marketplace
|
|
25
|
+
repository (e.g. a local clone of `cyberuni/marketplace`) — see
|
|
26
|
+
[Catalogs are checked at the repository root](#catalogs-are-checked-at-the-repository-root).
|
|
24
27
|
It never prompts and never writes, so it is safe to run unattended.
|
|
25
28
|
|
|
26
29
|
Stdout is one JSON object — that is the contract to read, not the CLI's own terminal output:
|
|
@@ -43,13 +46,20 @@ Read `vendors[].status` literally:
|
|
|
43
46
|
|
|
44
47
|
| Status | Means |
|
|
45
48
|
| --- | --- |
|
|
46
|
-
| `built` | the build writes this vendor's manifest |
|
|
49
|
+
| `built` | the build writes this vendor's output. For `copilot-cli` that output is the `com.github.copilot/` component tree, not a manifest |
|
|
47
50
|
| `canonical` | the vendor reads root `plugin.json`; **no file is written, and that is correct** |
|
|
48
51
|
| `skipped` | an unknown vendor id — a typo in `vendors` |
|
|
49
52
|
| `failed` | the write itself failed; the finding names why |
|
|
50
53
|
|
|
51
|
-
`copilot-cli` reporting `canonical`
|
|
52
|
-
|
|
54
|
+
`copilot-cli` reporting `canonical` is a healthy plugin, not a missing build — root `plugin.json`
|
|
55
|
+
serves it, and a plugin declaring no agents, commands, rules, hooks, or LSP servers has nothing else
|
|
56
|
+
to derive. Never report it as a fault.
|
|
57
|
+
|
|
58
|
+
A plugin that **does** declare those reports `copilot-cli` as `built` at `com.github.copilot/`
|
|
59
|
+
instead. Declaring the canonical `$schema` moves them there: Copilot CLI stops reading them from the
|
|
60
|
+
plugin root entirely, so a root-only layout loads none of them and says nothing
|
|
61
|
+
([ADR-0015](../../.agents/spec/design/decisions/0015-copilot-spec-mode-namespace.md)). That is what
|
|
62
|
+
`copilot-root-components` reports.
|
|
53
63
|
|
|
54
64
|
If `node` is unavailable, read `scripts/doctor.mjs` and apply the same checks by hand: it composes
|
|
55
65
|
`universal-plugin plugin build --dry-run --format json` with filesystem facts that build cannot see.
|
|
@@ -60,23 +70,27 @@ Each `code` below is what the script emits.
|
|
|
60
70
|
|
|
61
71
|
| Finding | What it means | Repair |
|
|
62
72
|
| --- | --- | --- |
|
|
63
|
-
| `no-manifest` | no root `plugin.json` — this is not a plugin yet | `/universal-plugin:init` |
|
|
64
|
-
| `legacy-manifest` | root `plugin.json` with neither `$schema` nor `extensions` — a single-vendor manifest on the canonical path | `/universal-plugin:init`, adopt route |
|
|
65
|
-
| `vendor-only` | a vendor manifest with no canonical manifest above it | `/universal-plugin:init`, adopt route |
|
|
73
|
+
| `no-manifest` | no root `plugin.json` — this is not a plugin yet | `/universal-plugin:init-universal-plugin` |
|
|
74
|
+
| `legacy-manifest` | root `plugin.json` with neither `$schema` nor `extensions` — a single-vendor manifest on the canonical path | `/universal-plugin:init-universal-plugin`, adopt route |
|
|
75
|
+
| `vendor-only` | a vendor manifest with no canonical manifest above it | `/universal-plugin:init-universal-plugin`, adopt route |
|
|
66
76
|
| `unbuilt` | a declared vendor whose output path holds no file — that runtime sees no plugin | `universal-plugin plugin build` |
|
|
67
77
|
| `stale` | a derived manifest older than `plugin.json` | `universal-plugin plugin build` |
|
|
68
78
|
| `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 |
|
|
69
79
|
| `unknown-vendor` | a `vendors` entry no build target matches; reported as `skipped` plus a warning | fix the id in `plugin.json` |
|
|
70
|
-
| `undeliverable-override` | `harnesses["copilot-cli"]` sets fields that reach nothing | `/universal-plugin:init`, update route — move them to a vendor that has a derived manifest, or drop them |
|
|
80
|
+
| `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 |
|
|
71
81
|
| `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 |
|
|
72
82
|
| `version-drift` | the `packagePath` `package.json` and the canonical manifest carry different versions | `/universal-plugin:version` |
|
|
73
83
|
| `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` |
|
|
84
|
+
| `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 plugin build` |
|
|
74
85
|
| `stale-github-plugin` | a leftover `.github/plugin/plugin.json` from an older build — shadowed by root and no longer generated | `/universal-plugin:remove-plugin` |
|
|
75
86
|
| `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` |
|
|
76
|
-
| `no-vendors` | no vendor is declared, so the build writes nothing and no runtime reads the plugin | `/universal-plugin:init`, update route |
|
|
87
|
+
| `no-vendors` | no vendor is declared, so the build writes nothing and no runtime reads the plugin. On a repository still on the pre-0.6 layout 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 on the pre-0.6 layout, else update route |
|
|
77
88
|
| `package-path-missing` | `packagePath` names a directory with no readable `package.json` | fix `packagePath`, or create the package |
|
|
89
|
+
| `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 |
|
|
90
|
+
| `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 |
|
|
78
91
|
| `unparsable-manifest` | root `plugin.json` is not valid JSON | fix the syntax error |
|
|
79
|
-
| `invalid-catalog` | a marketplace catalog at the repository root is not a shape its runtime loads — it is found, read, and refused at install time, in the user's terminal | `/universal-plugin:marketplace` |
|
|
92
|
+
| `invalid-catalog` | a marketplace catalog — at the repository root, or at a `--marketplace-root` clone — is not a shape its runtime loads — it is found, read, and refused at install time, in the user's terminal | `/universal-plugin:marketplace` |
|
|
93
|
+
| `marketplace-root-missing` | a `--marketplace-root` path does not exist, so it could not be checked at all | clone the marketplace repository, or fix the path |
|
|
80
94
|
|
|
81
95
|
## Catalogs are checked at the repository root
|
|
82
96
|
|
|
@@ -88,6 +102,20 @@ The detail names the key at fault, so hand it to `/universal-plugin:marketplace`
|
|
|
88
102
|
entry's fields are derived from the plugin's `plugin.json`, and the catalog's own `name` and `owner`
|
|
89
103
|
are authored in the catalog — which half is at fault decides where the repair goes.
|
|
90
104
|
|
|
105
|
+
A **shared** marketplace repository (e.g. `cyberuni/marketplace`) is a repository of its own — a bad
|
|
106
|
+
entry that reached it by another path (a hand-edited entry, a PR from a different tool, a curator
|
|
107
|
+
edit) is invisible to a `doctor` run inside any plugin's own repo, because that run never sees the
|
|
108
|
+
shared repository at all. Clone it separately and name the clone explicitly:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
node scripts/doctor.mjs --marketplace-root ../marketplace
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Pass `--marketplace-root` once per clone to check more than one. Each invalid entry it finds is still
|
|
115
|
+
reported as `invalid-catalog`, with the clone's path in the detail so it reads apart from the plugin
|
|
116
|
+
repo's own catalogs; a path that does not exist is `marketplace-root-missing` rather than a silent
|
|
117
|
+
skip.
|
|
118
|
+
|
|
91
119
|
## Checking staleness properly
|
|
92
120
|
|
|
93
121
|
The `stale` finding is an mtime comparison, which catches the common case and nothing more. It cannot
|
|
@@ -108,8 +136,8 @@ user can run, or ask before running it yourself.
|
|
|
108
136
|
|
|
109
137
|
## Version drift
|
|
110
138
|
|
|
111
|
-
Two files carry an authored version: the canonical `plugin.json`, and the `package.json` at
|
|
112
|
-
`
|
|
139
|
+
Two files carry an authored version: the canonical `plugin.json`, and the `package.json` at the
|
|
140
|
+
`packagePath` the CLI reports (`config get --key packagePath`), resolved from the plugin root. The script compares
|
|
113
141
|
them and emits `version-drift`.
|
|
114
142
|
|
|
115
143
|
They diverge when someone ran `npm version`, or when changesets released a number that never flowed
|
|
@@ -140,7 +168,9 @@ is meant to ship — content that is still being worked on is not a finding to a
|
|
|
140
168
|
- **Never repair.** Report the finding and name the skill that owns it.
|
|
141
169
|
- **Never hand-edit a derived manifest to make a finding go away.** The next build overwrites it and
|
|
142
170
|
the finding comes back.
|
|
143
|
-
- Do not report `copilot-cli` writing no
|
|
171
|
+
- Do not report `copilot-cli` writing no **manifest** as a fault. It reads the canonical manifest
|
|
172
|
+
directly. Its **components** are a separate question — a `copilot-root-components` finding is a real
|
|
173
|
+
fault, and `canonical` is only healthy for a plugin that declares none of the moved kinds.
|
|
144
174
|
- Do not treat repo-private agent configuration (`.claude/skills/`, `.agents/skills/`) as part of the
|
|
145
175
|
plugin. Diagnosing a repository's own skill wiring is `buddy-agent-harness:doctor`.
|
|
146
176
|
|
|
@@ -148,7 +178,7 @@ is meant to ship — content that is still being worked on is not a finding to a
|
|
|
148
178
|
|
|
149
179
|
| Task | Skill |
|
|
150
180
|
|------|-------|
|
|
151
|
-
| Create, adopt, or change what the plugin declares | `init` |
|
|
181
|
+
| Create, adopt, or change what the plugin declares | `init-universal-plugin` |
|
|
152
182
|
| Move the plugin's version | `version` |
|
|
153
183
|
| Remove derived manifests, or the plugin itself | `remove-plugin` |
|
|
154
184
|
| Generate the repository's own marketplace catalogs | `marketplace` |
|
|
@@ -15,6 +15,9 @@ const argv = process.argv.slice(2)
|
|
|
15
15
|
const verbose = argv.includes('--verbose')
|
|
16
16
|
const rootFlag = argv.indexOf('--root')
|
|
17
17
|
const root = path.resolve(rootFlag === -1 ? process.cwd() : (argv[rootFlag + 1] ?? process.cwd()))
|
|
18
|
+
// A shared marketplace repository (e.g. cyberuni/marketplace) is cloned separately from any plugin
|
|
19
|
+
// repo, so it is named explicitly rather than discovered — repeatable, one clone per flag.
|
|
20
|
+
const marketplaceRoots = argv.flatMap((arg, i) => (arg === '--marketplace-root' ? [argv[i + 1]] : [])).filter(Boolean)
|
|
18
21
|
|
|
19
22
|
const findings = []
|
|
20
23
|
const add = (code, severity, detail, repair) => findings.push({ code, severity, detail, repair })
|
|
@@ -42,27 +45,56 @@ if (manifest === null) {
|
|
|
42
45
|
'vendor-only',
|
|
43
46
|
'high',
|
|
44
47
|
`vendor manifests with no canonical manifest: ${orphans.join(', ')}`,
|
|
45
|
-
'/universal-plugin:init, adopt route',
|
|
48
|
+
'/universal-plugin:init-universal-plugin, adopt route',
|
|
46
49
|
)
|
|
47
50
|
} else {
|
|
48
|
-
add(
|
|
51
|
+
add(
|
|
52
|
+
'no-manifest',
|
|
53
|
+
'high',
|
|
54
|
+
'no root plugin.json — this is not a plugin yet',
|
|
55
|
+
'/universal-plugin:init-universal-plugin',
|
|
56
|
+
)
|
|
49
57
|
}
|
|
50
58
|
report({ vendors: [] })
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
const ext = manifest.extensions?.[UP_NAMESPACE] ?? null
|
|
54
62
|
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
// The shipped CLI, preferring the one this skill ships with. Every question the CLI can answer is asked
|
|
64
|
+
// of it rather than re-derived here, so this script cannot drift from the commands it diagnoses.
|
|
65
|
+
const bin = path.join(packageRoot, 'bin', 'universal-plugin.mjs')
|
|
66
|
+
const runCli = (...args) =>
|
|
67
|
+
fs.existsSync(bin)
|
|
68
|
+
? spawnSync(process.execPath, [bin, ...args], { encoding: 'utf8' })
|
|
69
|
+
: spawnSync('npx', ['universal-plugin', ...args], { encoding: 'utf8' })
|
|
70
|
+
|
|
71
|
+
// `packagePath` is the CLI's own config. It is asked of the CLI (`config get --key packagePath`), the
|
|
72
|
+
// same reader `plugin version` and `publish sync-version` use, so a plugin the CLI treats as
|
|
73
|
+
// npm-shipping is one this script treats the same way (issue #79). `undefined` means the CLI could not
|
|
74
|
+
// answer — too old to read the key — and every check that depends on it is skipped rather than guessed.
|
|
59
75
|
const packagePath = readPackagePath()
|
|
76
|
+
if (packagePath === undefined) {
|
|
77
|
+
add(
|
|
78
|
+
'package-path-unknown',
|
|
79
|
+
'low',
|
|
80
|
+
'the CLI could not report packagePath, so version-drift and unreleased-content were not checked',
|
|
81
|
+
'upgrade universal-plugin',
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
if (ext !== null && Object.hasOwn(ext, 'packagePath')) {
|
|
85
|
+
add(
|
|
86
|
+
'misplaced-package-path',
|
|
87
|
+
'high',
|
|
88
|
+
`plugin.json declares extensions["${UP_NAMESPACE}"].packagePath, which the CLI never reads${packagePath === null ? ' — this plugin is treated as not shipping to npm' : ''}`,
|
|
89
|
+
'move packagePath to .agents/universal-plugin.json, relative to the plugin root',
|
|
90
|
+
)
|
|
91
|
+
}
|
|
60
92
|
if (!manifest.$schema?.includes('agent-plugins.org') || ext === null) {
|
|
61
93
|
add(
|
|
62
94
|
'legacy-manifest',
|
|
63
95
|
'high',
|
|
64
96
|
'root plugin.json carries no $schema on agent-plugins.org or no extensions block',
|
|
65
|
-
'/universal-plugin:init, adopt route',
|
|
97
|
+
'/universal-plugin:init-universal-plugin, adopt route',
|
|
66
98
|
)
|
|
67
99
|
}
|
|
68
100
|
|
|
@@ -84,15 +116,59 @@ if (fs.existsSync(path.join(root, '.github/plugin/plugin.json'))) {
|
|
|
84
116
|
)
|
|
85
117
|
}
|
|
86
118
|
|
|
119
|
+
// Copilot CLI's spec mode reads its native components ONLY under com.github.copilot/ — declaring the
|
|
120
|
+
// canonical $schema moves them out of the plugin root (ADR-0015). A plugin that ships them at the
|
|
121
|
+
// root and has no namespace copy loads none of them on Copilot CLI, and nothing at runtime says so.
|
|
122
|
+
// Checked from the filesystem rather than from the build result, so it still fires on a repository
|
|
123
|
+
// whose build cannot run.
|
|
124
|
+
const COPILOT_NAMESPACE = 'com.github.copilot'
|
|
125
|
+
const declaredTargets = ext?.vendors ?? Object.keys(ext?.harnesses ?? {})
|
|
126
|
+
if (declaredTargets.includes('copilot-cli')) {
|
|
127
|
+
const nsDir = path.join(root, COPILOT_NAMESPACE)
|
|
128
|
+
const declaredPaths = (value, fallback) => {
|
|
129
|
+
if (value === undefined) return fallback === null ? [] : [fallback]
|
|
130
|
+
if (typeof value === 'string') return [value]
|
|
131
|
+
if (Array.isArray(value)) return value.filter((entry) => typeof entry === 'string')
|
|
132
|
+
if (value && Array.isArray(value.paths)) return value.paths.filter((entry) => typeof entry === 'string')
|
|
133
|
+
return []
|
|
134
|
+
}
|
|
135
|
+
const hasContent = (rel) => {
|
|
136
|
+
const abs = path.join(root, rel)
|
|
137
|
+
return fs.existsSync(abs) && fs.statSync(abs).isDirectory() && fs.readdirSync(abs).length > 0
|
|
138
|
+
}
|
|
139
|
+
const missing = []
|
|
140
|
+
// The same defaults the build derives from. Passing null here would make the check fire only for a
|
|
141
|
+
// manifest that names the path — and the manifest that names none is the common one.
|
|
142
|
+
for (const [kind, defaultPath] of [
|
|
143
|
+
['agents', './agents/'],
|
|
144
|
+
['commands', './commands/'],
|
|
145
|
+
['rules', './rules/'],
|
|
146
|
+
]) {
|
|
147
|
+
const roots = declaredPaths(ext?.[kind], defaultPath)
|
|
148
|
+
if (roots.some(hasContent) && !hasContent(path.join(COPILOT_NAMESPACE, kind))) missing.push(kind)
|
|
149
|
+
}
|
|
150
|
+
// hooks is a single file, declared as a path or inline in the manifest, and carries a default the
|
|
151
|
+
// manifest need not declare at all.
|
|
152
|
+
const inlineHooks = ext?.hooks !== null && typeof ext?.hooks === 'object' && 'hooks' in ext.hooks
|
|
153
|
+
const hookSources = inlineHooks ? [] : declaredPaths(ext?.hooks, './hooks/hooks.json')
|
|
154
|
+
const hasHooks = inlineHooks || hookSources.some((rel) => fs.existsSync(path.join(root, rel)))
|
|
155
|
+
if (hasHooks && !fs.existsSync(path.join(nsDir, 'hooks', 'hooks.json'))) missing.push('hooks')
|
|
156
|
+
const lspSources = declaredPaths(ext?.lspServers, null)
|
|
157
|
+
if (lspSources.some((rel) => fs.existsSync(path.join(root, rel))) && !fs.existsSync(path.join(nsDir, 'lsp.json'))) {
|
|
158
|
+
missing.push('lspServers')
|
|
159
|
+
}
|
|
160
|
+
if (missing.length > 0) {
|
|
161
|
+
add(
|
|
162
|
+
'copilot-root-components',
|
|
163
|
+
'high',
|
|
164
|
+
`${missing.join(', ')} sit at the plugin root with no copy under ${COPILOT_NAMESPACE}/ — Copilot CLI reads them only from there once the canonical $schema is declared, so it loads none of them`,
|
|
165
|
+
'universal-plugin plugin build',
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
87
170
|
// Ask the shipped CLI what it would write, without writing it.
|
|
88
|
-
const
|
|
89
|
-
const cli = fs.existsSync(bin)
|
|
90
|
-
? spawnSync(process.execPath, [bin, 'plugin', 'build', '--dry-run', '--format', 'json', '--root', root], {
|
|
91
|
-
encoding: 'utf8',
|
|
92
|
-
})
|
|
93
|
-
: spawnSync('npx', ['universal-plugin', 'plugin', 'build', '--dry-run', '--format', 'json', '--root', root], {
|
|
94
|
-
encoding: 'utf8',
|
|
95
|
-
})
|
|
171
|
+
const cli = runCli('plugin', 'build', '--dry-run', '--format', 'json', '--root', root)
|
|
96
172
|
|
|
97
173
|
const vendors = []
|
|
98
174
|
const build = readJson_stdout(cli.stdout)
|
|
@@ -106,6 +182,17 @@ if (build === null) {
|
|
|
106
182
|
'codex is targeted without version or description — the build writes nothing at all, for any vendor',
|
|
107
183
|
'add both to the canonical top level',
|
|
108
184
|
)
|
|
185
|
+
} else if (findings.some((f) => f.code === 'legacy-manifest' || f.code === 'shadowing-manifest')) {
|
|
186
|
+
// The build stops rather than reporting a definitive empty state when the layout explains the
|
|
187
|
+
// empty result (issue #61). That is still the no-vendors diagnosis — the layout findings above
|
|
188
|
+
// name the signals, and this names the consequence. Keyed on those findings rather than on the
|
|
189
|
+
// build's message, so the two never have to agree on wording.
|
|
190
|
+
add(
|
|
191
|
+
'no-vendors',
|
|
192
|
+
'medium',
|
|
193
|
+
'no vendor is declared — the project is on the pre-0.6 layout, so the build derives nothing and no runtime reads this plugin',
|
|
194
|
+
'/universal-plugin:init-universal-plugin, adopt route',
|
|
195
|
+
)
|
|
109
196
|
} else {
|
|
110
197
|
add(
|
|
111
198
|
'build-failed',
|
|
@@ -137,13 +224,13 @@ if (build === null) {
|
|
|
137
224
|
}
|
|
138
225
|
for (const warning of build.warnings ?? []) {
|
|
139
226
|
if (/not delivered/.test(warning)) {
|
|
140
|
-
add('undeliverable-override', 'medium', warning, '/universal-plugin:init, update route')
|
|
227
|
+
add('undeliverable-override', 'medium', warning, '/universal-plugin:init-universal-plugin, update route')
|
|
141
228
|
} else if (/No vendors declared/.test(warning)) {
|
|
142
229
|
add(
|
|
143
230
|
'no-vendors',
|
|
144
231
|
'medium',
|
|
145
232
|
'no vendor is declared — the build writes nothing, so no runtime reads this plugin',
|
|
146
|
-
'/universal-plugin:init, update route',
|
|
233
|
+
'/universal-plugin:init-universal-plugin, update route',
|
|
147
234
|
)
|
|
148
235
|
} else if (/^Unknown vendor/.test(warning)) {
|
|
149
236
|
add('unknown-vendor', 'medium', warning, 'fix the vendor id in plugin.json')
|
|
@@ -154,7 +241,7 @@ if (build === null) {
|
|
|
154
241
|
}
|
|
155
242
|
|
|
156
243
|
// Version drift between the two authored numbers.
|
|
157
|
-
if (packagePath
|
|
244
|
+
if (typeof packagePath === 'string') {
|
|
158
245
|
const pkgPath = path.join(root, packagePath, 'package.json')
|
|
159
246
|
const pkg = readJson(pkgPath)
|
|
160
247
|
if (pkg === null) {
|
|
@@ -202,31 +289,45 @@ if (manifest.version !== undefined && packagePath === null) {
|
|
|
202
289
|
// The marketplace catalogs a user installs from. They sit at the *repository* root, above a plugin in
|
|
203
290
|
// a monorepo, and each is read by its runtime at install time — a catalog whose shape that runtime
|
|
204
291
|
// refuses fails in the user's terminal, not here. The shipped CLI owns the rules; this only asks.
|
|
205
|
-
|
|
292
|
+
// A shared marketplace repository (--marketplace-root) is a separate clone this plugin's repo root
|
|
293
|
+
// cannot see, so it is checked the same way but reported with the clone's path attached.
|
|
294
|
+
const missingMarketplaceRoots = marketplaceRoots.filter((r) => !fs.existsSync(path.resolve(r)))
|
|
295
|
+
for (const r of missingMarketplaceRoots) {
|
|
296
|
+
add(
|
|
297
|
+
'marketplace-root-missing',
|
|
298
|
+
'high',
|
|
299
|
+
`--marketplace-root ${r} does not exist`,
|
|
300
|
+
'clone the marketplace repository, or fix the path',
|
|
301
|
+
)
|
|
302
|
+
}
|
|
303
|
+
const existingMarketplaceRoots = marketplaceRoots.filter((r) => fs.existsSync(path.resolve(r)))
|
|
304
|
+
|
|
305
|
+
for (const row of invalidCatalogs(existingMarketplaceRoots)) {
|
|
306
|
+
const label = row.own ? row.path : `${row.catalogRoot}/${row.path}`
|
|
206
307
|
add(
|
|
207
308
|
'invalid-catalog',
|
|
208
309
|
'high',
|
|
209
|
-
`${
|
|
310
|
+
`${label} is not a shape its runtime loads: ${row.issues.map((issue) => `${issue.path} ${issue.message}`).join('; ')}`,
|
|
210
311
|
'/universal-plugin:marketplace',
|
|
211
312
|
)
|
|
212
313
|
}
|
|
213
314
|
|
|
214
315
|
report({ vendors })
|
|
215
316
|
|
|
216
|
-
/** Every catalog the repository
|
|
217
|
-
* read, when the CLI is too old to answer, or
|
|
218
|
-
*
|
|
219
|
-
|
|
317
|
+
/** Every catalog the repository — and any explicitly named marketplace clone — carries that its
|
|
318
|
+
* runtime would refuse. Empty when there is nothing to read, when the CLI is too old to answer, or
|
|
319
|
+
* when every catalog is fine — a missing catalog is not a fault, and this reports no opinion on
|
|
320
|
+
* which ones a repository ought to carry. */
|
|
321
|
+
function invalidCatalogs(extraRoots) {
|
|
220
322
|
const catalogRoot = git('rev-parse', '--show-toplevel') ?? root
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return Array.isArray(rows) ? rows.filter((row) => row.status === 'invalid') : []
|
|
323
|
+
const targets = [{ catalogRoot, own: true }, ...extraRoots.map((r) => ({ catalogRoot: path.resolve(r), own: false }))]
|
|
324
|
+
return targets.flatMap(({ catalogRoot, own }) => {
|
|
325
|
+
const result = runCli('marketplace', 'validate', '--format', 'json', '--root', catalogRoot)
|
|
326
|
+
const rows = readJson_stdout(result.stdout)
|
|
327
|
+
return Array.isArray(rows)
|
|
328
|
+
? rows.filter((row) => row.status === 'invalid').map((row) => ({ ...row, catalogRoot, own }))
|
|
329
|
+
: []
|
|
330
|
+
})
|
|
230
331
|
}
|
|
231
332
|
|
|
232
333
|
/** Runs git inside `root`, returning its stdout or `null` — a non-zero status, a missing git, and a
|
|
@@ -293,9 +394,16 @@ function report({ vendors }) {
|
|
|
293
394
|
process.exit(0)
|
|
294
395
|
}
|
|
295
396
|
|
|
296
|
-
/** Where the npm package that ships this plugin lives,
|
|
297
|
-
*
|
|
397
|
+
/** Where the npm package that ships this plugin lives, relative to the plugin root, as the CLI reads
|
|
398
|
+
* it; `null` when the plugin ships to no package (the author-picks release model of ADR-0010 §2);
|
|
399
|
+
* `undefined` when the CLI could not answer. */
|
|
298
400
|
function readPackagePath() {
|
|
299
|
-
const
|
|
300
|
-
|
|
401
|
+
const result = runCli('config', 'get', '--key', 'packagePath', '--format', 'json', '--root', root)
|
|
402
|
+
if (result.status !== 0) return undefined
|
|
403
|
+
try {
|
|
404
|
+
const declared = JSON.parse(result.stdout)
|
|
405
|
+
return typeof declared === 'string' || declared === null ? declared : undefined
|
|
406
|
+
} catch {
|
|
407
|
+
return undefined
|
|
408
|
+
}
|
|
301
409
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# init skill
|
|
1
|
+
# init-universal-plugin skill
|
|
2
2
|
|
|
3
3
|
Give a project one canonical `plugin.json` on the [Agent Plugins
|
|
4
4
|
Specification](https://agent-plugins.org), then derive the manifest each runtime expects — Claude
|
|
@@ -53,5 +53,8 @@ touch CI, repository settings, or unrelated project files.
|
|
|
53
53
|
## References
|
|
54
54
|
|
|
55
55
|
- [Spec](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/spec.md)
|
|
56
|
-
- [
|
|
56
|
+
- [Manifest schema (Agent Plugins Specification v1.0.0)](https://agent-plugins.org/schemas/1.0.0/plugin.schema.json)
|
|
57
|
+
- [Extension schema](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/schema/extension.schema.json)
|
|
58
|
+
— the body of `extensions["org.cyberuni.universal-plugin"]`, also shipped in the package at
|
|
59
|
+
`schema/extension.schema.json`
|
|
57
60
|
- [Examples](https://github.com/cyberuni/universal-plugin/tree/main/examples)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: init
|
|
2
|
+
name: init-universal-plugin
|
|
3
3
|
description: Use this skill to create or change a universal agent plugin — scaffold a new one, adopt an existing vendor-specific plugin or already-shipped skills onto the open Agent Plugins Specification, or add and remove vendors and components on the canonical plugin.json that drives Claude Code, Cursor, Codex, and GitHub Copilot CLI. Trigger on "init a plugin here", "make my Claude Code plugin work in Cursor", "convert this to the open plugin standard", "turn these skills into a plugin", "add Codex support", or "add a hooks component".
|
|
4
4
|
argument-hint: '[--name <name>] [--vendor <id>] [--scaffold] [--npm] [--no-marketplace] [--force]'
|
|
5
5
|
---
|
|
@@ -10,10 +10,18 @@ Give a project one canonical plugin manifest — a root `plugin.json` on the Age
|
|
|
10
10
|
Specification — and derive from it the manifest each runtime expects.
|
|
11
11
|
|
|
12
12
|
Most of the manifest is shared. The divergence is small and asymmetric: Copilot CLI reads the
|
|
13
|
-
canonical `plugin.json` directly and gets no derived
|
|
13
|
+
canonical `plugin.json` directly and gets no derived *manifest*, while Claude Code, Cursor, and
|
|
14
14
|
Codex each read their own path. Keep that asymmetry in mind — this is a consolidation job, not a
|
|
15
15
|
copy-everywhere job.
|
|
16
16
|
|
|
17
|
+
That asymmetry does not extend to components. Declaring the canonical `$schema` puts a plugin in
|
|
18
|
+
Open Plugin Spec mode, and Copilot CLI then reads its **native** components — agents, commands,
|
|
19
|
+
rules, hooks, LSP servers — from a `com.github.copilot/` directory rather than the plugin root.
|
|
20
|
+
Authoring still happens at the canonical locations: `plugin build` derives that directory
|
|
21
|
+
([ADR-0015](../../.agents/spec/design/decisions/0015-copilot-spec-mode-namespace.md)), so do not tell
|
|
22
|
+
an author to move `agents/` into it — the root copy is what the other three vendors derive from. See
|
|
23
|
+
[`references/vendors/copilot-cli.md`](./references/vendors/copilot-cli.md).
|
|
24
|
+
|
|
17
25
|
`references/standard.md` defines the baseline every plugin gets. Read the vendor file for each
|
|
18
26
|
runtime you are enabling, and only those.
|
|
19
27
|
|
|
@@ -22,7 +30,7 @@ runtime you are enabling, and only those.
|
|
|
22
30
|
| Claude Code | `.claude-plugin/plugin.json` | none | `references/vendors/claude-code.md` |
|
|
23
31
|
| Cursor | `.cursor-plugin/plugin.json` | none | `references/vendors/cursor.md` |
|
|
24
32
|
| Codex | `.codex-plugin/plugin.json` | `version`, `description` | `references/vendors/codex.md` |
|
|
25
|
-
| GitHub Copilot CLI | none — reads root `plugin.json` |
|
|
33
|
+
| GitHub Copilot CLI | none — reads root `plugin.json` | native components derived under `com.github.copilot/` ([ADR-0015](../../.agents/spec/design/decisions/0015-copilot-spec-mode-namespace.md)) | `references/vendors/copilot-cli.md` |
|
|
26
34
|
|
|
27
35
|
This skill owns the **authoring** side: the plugin a project ships. Setting a repository up to
|
|
28
36
|
*consume* skills — the `.agents/skills/` layout, `AGENTS.md`, per-harness bridges — is
|
|
@@ -40,7 +48,7 @@ It is the authoritative source for which component to reach for and which anti-p
|
|
|
40
48
|
|
|
41
49
|
## Arguments
|
|
42
50
|
|
|
43
|
-
An invocation may carry the CLI's own flags: `/universal-plugin:init --name my-plugin --scaffold --npm`.
|
|
51
|
+
An invocation may carry the CLI's own flags: `/universal-plugin:init-universal-plugin --name my-plugin --scaffold --npm`.
|
|
44
52
|
|
|
45
53
|
Read them from the invocation itself rather than from a placeholder. Claude Code appends what the
|
|
46
54
|
caller typed as `ARGUMENTS: <value>`, and Codex substitutes nothing at all, so on every runtime the
|
|
@@ -78,9 +86,12 @@ Sort each finding into exactly one bucket:
|
|
|
78
86
|
- **adoptable** — a hand-written vendor manifest with no canonical manifest above it, a legacy root
|
|
79
87
|
`plugin.json` carrying neither `$schema` nor `extensions`, or publicly-shipped skills with no
|
|
80
88
|
manifest at all. These become canonical content in Phase 4.
|
|
81
|
-
- **undeliverable** — a vendor-specific field with no delivery path,
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
- **undeliverable** — a vendor-specific field with no delivery path, because Copilot CLI reads the
|
|
90
|
+
canonical manifest directly and has no derived file of its own. Two shapes reach this bucket: a
|
|
91
|
+
`harnesses["copilot-cli"]` override, which the build warns about; and a non-spec field on a
|
|
92
|
+
**legacy root `plugin.json`** — `category`, `tags` — which nothing warns about at all, because the
|
|
93
|
+
correct adoption never writes a `harnesses` entry for it. Report every one by name; do not invent
|
|
94
|
+
a home for it.
|
|
84
95
|
- **not a plugin** — repo-private agent configuration (`.claude/skills/`, `.agents/skills/`,
|
|
85
96
|
`.cursor/rules/`). It is this project's own tooling, not something it distributes. Say nothing
|
|
86
97
|
about packaging it.
|
|
@@ -95,6 +106,11 @@ hand-written vendor manifests become generated artifacts, which vendors will be
|
|
|
95
106
|
canonical metadata will say (show `name`, `version`, and `description` verbatim), and what is being
|
|
96
107
|
left alone and why.
|
|
97
108
|
|
|
109
|
+
An adoption also presents **every non-spec field it is about to drop, by name and value** — the
|
|
110
|
+
undeliverable bucket from Phase 2. Nothing downstream warns about those, so this is the only notice
|
|
111
|
+
the user gets. [`references/adopt.md`](./references/adopt.md) Step 2 has the command that enumerates
|
|
112
|
+
them.
|
|
113
|
+
|
|
98
114
|
Get explicit approval before any step that deletes, replaces, or rewrites a user-authored file —
|
|
99
115
|
adoption always crosses that line, because it turns manifests the user maintains into build output.
|
|
100
116
|
Creating a missing `plugin.json`, a missing component directory, or a missing skill scaffold needs
|
|
@@ -145,7 +161,7 @@ and read back any line it printed on stderr — a repository with no author, no
|
|
|
145
161
|
remote gets no catalog, because every runtime requires an owner. `--no-marketplace` skips the step.
|
|
146
162
|
|
|
147
163
|
The catalog is named after the repository, `<owner>-<repo>-local`, not after the plugin: it lists
|
|
148
|
-
every plugin the repository develops. Re-running `init` folds the entry back in and leaves the
|
|
164
|
+
every plugin the repository develops. Re-running `init-universal-plugin` folds the entry back in and leaves the
|
|
149
165
|
marketplace name, the owner, and every other entry alone, so it is safe over a catalog someone
|
|
150
166
|
edited. Generating catalogs for a repository that already holds several plugins, and writing the
|
|
151
167
|
README install section, is the `marketplace` skill's job.
|
|
@@ -167,11 +183,16 @@ override both surface there and both are silent capability loss if ignored.
|
|
|
167
183
|
When the work was an adoption, the proof is the diff:
|
|
168
184
|
|
|
169
185
|
```bash
|
|
170
|
-
git diff -- .claude-plugin .cursor-plugin .codex-plugin .github/plugin
|
|
186
|
+
git diff -- plugin.json .claude-plugin .cursor-plugin .codex-plugin .github/plugin
|
|
171
187
|
```
|
|
172
188
|
|
|
173
|
-
|
|
174
|
-
|
|
189
|
+
From the four derived paths, expect only formatting and key-order churn. Root `plugin.json` is
|
|
190
|
+
different and is in the list on purpose: it shows real hunks — the `$schema`/`extensions` rewrite —
|
|
191
|
+
and under the legacy layout it *was* Copilot CLI's manifest, so it is the one file where a field can
|
|
192
|
+
vanish unnoticed. Read those hunks rather than skimming them.
|
|
193
|
+
|
|
194
|
+
Any field that disappeared is a regression, not a cleanup — trace it back to the shared metadata, to
|
|
195
|
+
that vendor's `harnesses` entry, or to the drop list you reported in Phase 3, before shipping.
|
|
175
196
|
|
|
176
197
|
Audit each skill the plugin ships, per [`references/create.md`](./references/create.md) Step 6.
|
|
177
198
|
Report what was created, adopted, derived, and left alone. For a fuller read of the plugin's state
|
|
@@ -186,7 +207,9 @@ This skill is not a formatter. If the project has one, run it over the written f
|
|
|
186
207
|
- **Never hand-edit a `version` field.** Two authored files and every derived artifact fall out of
|
|
187
208
|
sync. The `version` skill owns that move.
|
|
188
209
|
- **Adoption is lossless by contract.** Every vendor that worked before must still work after, and
|
|
189
|
-
the Phase 5 diff is the check that proves it.
|
|
210
|
+
the Phase 5 diff — root `plugin.json` included — is the check that proves it. Where a field
|
|
211
|
+
genuinely cannot be carried, losing it is a decision the user makes, not one the adoption makes
|
|
212
|
+
quietly: name it first.
|
|
190
213
|
- **Do not package repo-private agent configuration.** A `.claude/skills/` directory is the project's
|
|
191
214
|
own tooling; offering to publish it is wrong.
|
|
192
215
|
- **Do not convert vendor settings without a documented mapping.** Hooks have one: author them in
|
|
@@ -215,5 +238,7 @@ This skill is not a formatter. If the project has one, run it over the written f
|
|
|
215
238
|
|
|
216
239
|
- Governance: `npx universal-plugin governance show plugin-design`
|
|
217
240
|
- Spec: https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/spec.md
|
|
218
|
-
-
|
|
241
|
+
- Manifest schema (Agent Plugins Specification v1.0.0): https://agent-plugins.org/schemas/1.0.0/plugin.schema.json
|
|
242
|
+
- Extension schema (the body of `extensions["org.cyberuni.universal-plugin"]`), shipped in the package at
|
|
243
|
+
`schema/extension.schema.json`: https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/schema/extension.schema.json
|
|
219
244
|
- Examples: https://github.com/cyberuni/universal-plugin/tree/main/examples
|