universal-plugin 0.3.1 → 0.5.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/LICENSE +21 -0
- package/dist/cli.mjs +1262 -375
- package/dist/data/vendors.json +12 -0
- package/dist/run.mjs +4 -4
- package/governances/plugin-design.md +42 -1
- package/package.json +3 -1
- package/plugin.json +1 -1
- package/readme.md +80 -42
- package/skills/doctor/README.md +42 -0
- package/skills/doctor/SKILL.md +144 -0
- package/skills/doctor/scripts/doctor.mjs +273 -0
- package/skills/init/README.md +57 -0
- package/skills/init/SKILL.md +219 -0
- package/skills/{plugin → init}/references/adopt.md +8 -4
- package/skills/init/references/create.md +122 -0
- package/skills/init/references/detection.md +62 -0
- package/skills/init/references/frontmatter.md +65 -0
- package/skills/init/references/standard.md +93 -0
- package/skills/init/references/update.md +31 -0
- package/skills/init/references/vendors/claude-code.md +76 -0
- package/skills/init/references/vendors/codex.md +56 -0
- package/skills/init/references/vendors/copilot-cli.md +53 -0
- package/skills/init/references/vendors/cursor.md +52 -0
- package/skills/init/scripts/init.mjs +11 -0
- package/skills/marketplace/README.md +38 -0
- package/skills/marketplace/SKILL.md +170 -0
- package/skills/marketplace/references/runtimes.md +104 -0
- package/skills/marketplace/scripts/install-docs.mjs +115 -0
- package/skills/marketplace/scripts/marketplace.mjs +11 -0
- package/skills/publish-plugin/SKILL.md +10 -8
- package/skills/publish-plugin/references/vendor-requirements.md +13 -10
- package/skills/remove-plugin/README.md +38 -0
- package/skills/remove-plugin/SKILL.md +87 -0
- package/skills/version/README.md +36 -0
- package/skills/{plugin/references/version.md → version/SKILL.md} +27 -5
- package/skills/version/scripts/version.mjs +11 -0
- package/skills/plugin/README.md +0 -37
- package/skills/plugin/SKILL.md +0 -105
- package/skills/plugin/references/create.md +0 -163
- package/skills/plugin/references/delete.md +0 -23
- package/skills/plugin/references/inspect.md +0 -21
- package/skills/plugin/references/update.md +0 -26
- /package/skills/{plugin → init}/assets/templates/agent.md +0 -0
- /package/skills/{plugin → init}/assets/templates/command.md +0 -0
- /package/skills/{plugin → init}/assets/templates/hooks.json +0 -0
- /package/skills/{plugin → init}/assets/templates/plugin.json +0 -0
- /package/skills/{plugin → init}/assets/templates/setup-command.md +0 -0
- /package/skills/{plugin → init}/assets/templates/skill.md +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Create a universal plugin
|
|
2
|
+
|
|
3
|
+
Scaffold a new plugin from one canonical `plugin.json` and derive a manifest per chosen vendor.
|
|
4
|
+
|
|
5
|
+
## Step 1 — Gather plugin identity
|
|
6
|
+
|
|
7
|
+
Ask for what is missing. Every field lands at the canonical top level.
|
|
8
|
+
|
|
9
|
+
| Field | Required | Notes |
|
|
10
|
+
|-------|----------|-------|
|
|
11
|
+
| `name` | Yes | kebab-case, 1–64 chars, `a-z 0-9 - .` only |
|
|
12
|
+
| `description` | Recommended | one sentence; **required** when targeting Codex |
|
|
13
|
+
| `version` | If publishing | semver; **required** when targeting Codex |
|
|
14
|
+
| `author.name` | Recommended | person or org name |
|
|
15
|
+
| `homepage` | Optional | docs or landing page URL |
|
|
16
|
+
| `repository` | Optional | source repo URL |
|
|
17
|
+
| `license` | Optional | SPDX identifier, e.g. `MIT` |
|
|
18
|
+
| `keywords` | Optional | discovery tags; array of strings |
|
|
19
|
+
|
|
20
|
+
## Step 2 — Choose vendor targets
|
|
21
|
+
|
|
22
|
+
Ask which runtimes to support; default to all four if the user is unsure. Each choice becomes an
|
|
23
|
+
entry in both `vendors` and `harnesses` under
|
|
24
|
+
`extensions["org.cyberuni.universal-plugin"]`.
|
|
25
|
+
|
|
26
|
+
| Vendor ID | Derived manifest | Read before enabling |
|
|
27
|
+
|-----------|------------------|----------------------|
|
|
28
|
+
| `claude-code` | `.claude-plugin/plugin.json` | [`vendors/claude-code.md`](./vendors/claude-code.md) |
|
|
29
|
+
| `cursor` | `.cursor-plugin/plugin.json` | [`vendors/cursor.md`](./vendors/cursor.md) |
|
|
30
|
+
| `codex` | `.codex-plugin/plugin.json` | [`vendors/codex.md`](./vendors/codex.md) |
|
|
31
|
+
| `copilot-cli` | none — reads root `plugin.json` | [`vendors/copilot-cli.md`](./vendors/copilot-cli.md) |
|
|
32
|
+
|
|
33
|
+
## Step 3 — Choose components
|
|
34
|
+
|
|
35
|
+
Infer from context; ask only if ambiguous. [`standard.md`](./standard.md) has the component table and
|
|
36
|
+
the layout they go in; `governance show plugin-design` decides which component a given need calls
|
|
37
|
+
for. The universal minimum is `skills/<name>/SKILL.md` plus `.mcp.json`.
|
|
38
|
+
|
|
39
|
+
## Step 4 — Scaffold
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
node scripts/init.mjs --name <plugin-name> --vendor claude-code --vendor cursor --scaffold
|
|
43
|
+
```
|
|
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
|
|
50
|
+
metadata and the `harnesses` overrides by hand afterwards; [`standard.md`](./standard.md) shows the
|
|
51
|
+
finished shape.
|
|
52
|
+
|
|
53
|
+
Then create the component files from `../assets/templates/`:
|
|
54
|
+
|
|
55
|
+
| File to create | Template |
|
|
56
|
+
|----------------|----------|
|
|
57
|
+
| `skills/<name>/SKILL.md` | `assets/templates/skill.md` |
|
|
58
|
+
| `commands/<name>.md` | `assets/templates/command.md` |
|
|
59
|
+
| `agents/<name>.md` | `assets/templates/agent.md` |
|
|
60
|
+
| `hooks/hooks.json` | `assets/templates/hooks.json` |
|
|
61
|
+
| `commands/setup.md` (only with `rules/`) | `assets/templates/setup-command.md` |
|
|
62
|
+
|
|
63
|
+
## Step 5 — Fill in the harness overrides
|
|
64
|
+
|
|
65
|
+
Only fields one runtime understands go here; shared metadata stays at the top level. See each
|
|
66
|
+
`vendors/<vendor>.md` for what that vendor accepts, and note that a `copilot-cli` entry has no
|
|
67
|
+
delivery path at all.
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
"harnesses": {
|
|
71
|
+
"claude-code": {},
|
|
72
|
+
"cursor": { "publisher": "<org>", "category": "<category>", "tags": ["<tag>"] },
|
|
73
|
+
"codex": { "interface": { "displayName": "<Human Name>", "category": "<category>" } },
|
|
74
|
+
"copilot-cli": {}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Step 6 — Audit the skills
|
|
79
|
+
|
|
80
|
+
Run the mechanical validator from the aced **improve-skill** skill:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
node "<path to aced improve-skill>/scripts/validate.mts" --path skills/<skill-name>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Fix every CRITICAL finding, then invoke the **audit-skill** skill for the full review.
|
|
87
|
+
[`frontmatter.md`](./frontmatter.md) covers what has to hold across runtimes.
|
|
88
|
+
|
|
89
|
+
## Step 7 — Build
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx universal-plugin plugin build
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Useful flags: `--dry-run` to see the plan, `--verbose` for field-by-field decisions, `--vendor <id>`
|
|
96
|
+
for one target, `--clean` to delete derived manifests first.
|
|
97
|
+
|
|
98
|
+
Read the warnings. An unknown vendor id, an undeliverable Copilot override, and a failed Codex prompt
|
|
99
|
+
write all surface there rather than as errors.
|
|
100
|
+
|
|
101
|
+
## Step 8 — Install locally to test
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npx universal-plugin plugin install
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
It installs into every runtime the manifest declares, linking where the runtime follows a symlink
|
|
108
|
+
out of the tree and copying where it does not, and it prints the reload each one now needs — a
|
|
109
|
+
restart for Claude Code, **Developer: Reload Window** for Cursor. `--list` shows where it would go
|
|
110
|
+
without writing; `--vendor <id>` narrows it; `plugin uninstall` removes it again.
|
|
111
|
+
|
|
112
|
+
Codex and Copilot CLI scan no local plugin directory, so they report as `unsupported`. Reach those
|
|
113
|
+
through a repository-local marketplace — `publish-plugin`.
|
|
114
|
+
|
|
115
|
+
Do not hand-write a symlink for this. The recipe that circulated for it named
|
|
116
|
+
`~/.claude/plugins/local/`, which does not exist, and a symlink into Cursor's local directory is
|
|
117
|
+
rejected by Cursor's own scan.
|
|
118
|
+
|
|
119
|
+
## Next
|
|
120
|
+
|
|
121
|
+
Shipping it on npm → `migrate-plugin`. Listing it in a marketplace → `publish-plugin`. Releasing a
|
|
122
|
+
number → `/universal-plugin:version`.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Detection
|
|
2
|
+
|
|
3
|
+
What to look for during the survey, and where each finding belongs.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
ls -d .claude-plugin .cursor-plugin .codex-plugin .github/plugin .plugin 2>/dev/null
|
|
7
|
+
test -f plugin.json && cat plugin.json
|
|
8
|
+
find . -name SKILL.md -not -path '*/node_modules/*' -not -path './.git/*'
|
|
9
|
+
ls .mcp.json .lsp.json hooks/ commands/ agents/ rules/ output-styles/ 2>/dev/null
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
| What you find | What it means | Bucket |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| root `plugin.json` with `$schema` on `agent-plugins.org` **and** an `extensions` object | already on the open standard | canonical |
|
|
15
|
+
| `.claude-plugin/`, `.cursor-plugin/`, `.codex-plugin/` manifest **below** a canonical root | build output | derived |
|
|
16
|
+
| a vendor manifest with **no** canonical root `plugin.json` | a vendor-specific plugin | adoptable |
|
|
17
|
+
| root `plugin.json` with neither `$schema` nor `extensions` | a legacy single-vendor manifest now sitting on the canonical path | adoptable |
|
|
18
|
+
| publicly-shipped skills and no manifest of any kind | skills shipped without a plugin | adoptable |
|
|
19
|
+
| `harnesses["copilot-cli"]` carrying fields | no delivery path — the canonical schema is closed | undeliverable |
|
|
20
|
+
| `.claude/skills/`, `.agents/skills/`, `.cursor/rules/` | the project's own tooling | not a plugin |
|
|
21
|
+
| `.github/plugin/plugin.json` | a path older builds wrote; shadowed by root and no longer generated | stale, safe to delete |
|
|
22
|
+
|
|
23
|
+
For every vendor manifest found, record its path and **every field it sets**. Adoption reproduces
|
|
24
|
+
all of it, and the Phase 5 diff is checked field by field.
|
|
25
|
+
|
|
26
|
+
## Which skills count as public
|
|
27
|
+
|
|
28
|
+
Only a skill the project *distributes* belongs to a plugin.
|
|
29
|
+
|
|
30
|
+
| Location | Public? |
|
|
31
|
+
|----------|---------|
|
|
32
|
+
| `skills/<name>/SKILL.md` at the plugin root | Yes |
|
|
33
|
+
| `<package>/skills/<name>/SKILL.md` where `package.json` `files` ships it | Yes |
|
|
34
|
+
| `.claude/skills/`, `.agents/skills/`, `.cursor/rules/` | **No** — repo-private tooling |
|
|
35
|
+
|
|
36
|
+
If the only skills are in private locations, say nothing about adoption. A repository that configures
|
|
37
|
+
its own agents is not a plugin waiting to happen; `buddy-agent-harness:init` is the skill for that
|
|
38
|
+
side of the line.
|
|
39
|
+
|
|
40
|
+
## Making the adoption offer
|
|
41
|
+
|
|
42
|
+
State what you found, what adoption buys, and let the user decline:
|
|
43
|
+
|
|
44
|
+
> This project has a Claude Code plugin manifest but no canonical `plugin.json`. I can convert it to
|
|
45
|
+
> the open Agent Plugins Specification, so one manifest drives Cursor, Codex, and Copilot CLI too —
|
|
46
|
+
> Claude Code keeps working exactly as it does now. Want me to?
|
|
47
|
+
|
|
48
|
+
Offer once. If the user declines, or their request was already something specific and unrelated
|
|
49
|
+
(inspecting status, removing manifests), drop it and do what they asked.
|
|
50
|
+
|
|
51
|
+
## Choosing vendors
|
|
52
|
+
|
|
53
|
+
Ask which runtimes the plugin targets; default to all four when the user is unsure. Enabling a vendor
|
|
54
|
+
costs a derived manifest and nothing else — except Codex, which additionally requires `version` and
|
|
55
|
+
`description` on the canonical manifest and fails the build without them.
|
|
56
|
+
|
|
57
|
+
Enabling Copilot CLI writes no file at all. Say so, rather than letting the user read a missing
|
|
58
|
+
manifest as a broken build.
|
|
59
|
+
|
|
60
|
+
Detecting a vendor directory means there is configuration to reconcile. It does not by itself mean
|
|
61
|
+
the user wants that vendor maintained — say which vendors you are enabling and why, and let them
|
|
62
|
+
correct the part that is actually variable.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Skill frontmatter across runtimes
|
|
2
|
+
|
|
3
|
+
A plugin's skills are the one component every runtime reads, so their frontmatter is where
|
|
4
|
+
cross-vendor behavior is won or lost. Each runtime parses the fields it knows and silently drops the
|
|
5
|
+
rest.
|
|
6
|
+
|
|
7
|
+
## Required, everywhere
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
---
|
|
11
|
+
name: release-checklist
|
|
12
|
+
description: Runs the release checklist. Use when cutting a release or publishing a package version.
|
|
13
|
+
---
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- `name` — 1–64 characters, lowercase letters, digits, hyphens. **Match the parent directory name**;
|
|
17
|
+
Claude Code resolves the command from the directory and treats `name` as a label, so matching them
|
|
18
|
+
removes the discrepancy.
|
|
19
|
+
- `description` — say what the skill does *and* when to use it. This is the only text most runtimes
|
|
20
|
+
see when deciding whether to load it.
|
|
21
|
+
|
|
22
|
+
Two failures actually cost you the skill: a missing `description`, and YAML that does not parse. The
|
|
23
|
+
usual cause of the second is an unquoted colon — quote any description containing one.
|
|
24
|
+
|
|
25
|
+
## `invocation-policy` — the field this build acts on
|
|
26
|
+
|
|
27
|
+
universal-plugin reads `invocation-policy` from each `SKILL.md` and projects it per vendor:
|
|
28
|
+
|
|
29
|
+
| Value | Meaning | What the build does |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| `both` (default) | user- and model-invocable | nothing |
|
|
32
|
+
| `user` | explicit invocation only | writes `disable-model-invocation: true` into the skill's frontmatter |
|
|
33
|
+
| `model` | model-invocable only, not user-facing | writes `user-invocable: false`, and emits no Codex prompt |
|
|
34
|
+
|
|
35
|
+
Two consequences worth knowing before you run a build:
|
|
36
|
+
|
|
37
|
+
- **The build rewrites the authored `SKILL.md`** to carry those flags. That file is both source and
|
|
38
|
+
artifact for this one field. Expect it in the diff; it is not a stray edit.
|
|
39
|
+
- Any value other than `user`, `model`, or `both` **fails the build** rather than being ignored.
|
|
40
|
+
|
|
41
|
+
## Which runtime understands which field
|
|
42
|
+
|
|
43
|
+
| Field | Recognized by |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `name`, `description` | all |
|
|
46
|
+
| `license`, `metadata`, `compatibility` | accepted broadly, largely ignored |
|
|
47
|
+
| `allowed-tools` | most |
|
|
48
|
+
| `disable-model-invocation` | Claude Code, Cursor |
|
|
49
|
+
| `context: fork`, `agent:` | Claude Code only |
|
|
50
|
+
| `paths`, legacy `globs` | Cursor only |
|
|
51
|
+
| `model` | Copilot CLI only |
|
|
52
|
+
| `argument-hint`, `arguments` | Claude Code only |
|
|
53
|
+
|
|
54
|
+
`argument-hint` and `arguments` are the one group that fails loudly rather than quietly: claude.ai
|
|
55
|
+
uploads and the Skills API reject fields outside the standard set. A skill that takes arguments and
|
|
56
|
+
also ships through those paths cannot use them — and does not need to, because no runtime but Claude
|
|
57
|
+
Code substitutes anything anyway. Claude Code appends what the caller typed as `ARGUMENTS: <value>`
|
|
58
|
+
when the body has no `$ARGUMENTS`, so a body that says how to read the invocation works everywhere,
|
|
59
|
+
while a `$ARGUMENTS` placeholder resolves on one runtime and stays literal on the rest.
|
|
60
|
+
|
|
61
|
+
## The rule that follows
|
|
62
|
+
|
|
63
|
+
Anything that must hold on every runtime belongs in the Markdown body, not only in a vendor-specific
|
|
64
|
+
field. The body is the one part every runtime reads. Treat vendor frontmatter as an optimization
|
|
65
|
+
layered on instructions that already work without it.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# The canonical baseline
|
|
2
|
+
|
|
3
|
+
What every universal plugin has, before any vendor-specific work.
|
|
4
|
+
|
|
5
|
+
## The one authored file
|
|
6
|
+
|
|
7
|
+
Root `plugin.json`, on the Agent Plugins Specification v1.0.0 schema. Everything else a vendor reads
|
|
8
|
+
is derived from it.
|
|
9
|
+
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
13
|
+
"name": "my-plugin",
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"description": "One sentence saying what the plugin does.",
|
|
16
|
+
"author": { "name": "<author>" },
|
|
17
|
+
"extensions": {
|
|
18
|
+
"org.cyberuni.universal-plugin": {
|
|
19
|
+
"vendors": ["claude-code", "cursor", "codex", "copilot-cli"],
|
|
20
|
+
"skills": "./skills/",
|
|
21
|
+
"harnesses": {
|
|
22
|
+
"claude-code": {},
|
|
23
|
+
"cursor": {},
|
|
24
|
+
"codex": {},
|
|
25
|
+
"copilot-cli": {}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The spec's top level holds the shared metadata. Everything universal-plugin needs to run a build
|
|
33
|
+
lives under one namespaced key, `extensions["org.cyberuni.universal-plugin"]`:
|
|
34
|
+
|
|
35
|
+
| Key | Holds |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| `vendors` | the build targets. When absent, the `harnesses` keys are the targets |
|
|
38
|
+
| `harnesses` | per-vendor overrides, keyed by vendor id. `{}` opts in with no overrides |
|
|
39
|
+
| `packagePath` | the npm package whose `package.json` carries the same version |
|
|
40
|
+
| component paths (`skills`, `commands`, `agents`, `hooks`, …) | where each component lives |
|
|
41
|
+
| `dependencies` | the plugins this plugin needs. Only Claude Code reads them; see [`vendors/claude-code.md`](./vendors/claude-code.md) |
|
|
42
|
+
|
|
43
|
+
`vendors` and `harnesses` are separate on purpose: `vendors` says what to build, `harnesses` says
|
|
44
|
+
what each build gets. A vendor listed in `vendors` with no `harnesses` entry still builds; a
|
|
45
|
+
`harnesses` entry with no `vendors` list builds only while `vendors` is absent.
|
|
46
|
+
|
|
47
|
+
## Directory layout
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
<plugin-root>/
|
|
51
|
+
├── plugin.json ← canonical definition (source of truth)
|
|
52
|
+
├── skills/<name>/SKILL.md
|
|
53
|
+
├── commands/<name>.md
|
|
54
|
+
├── agents/<name>.md
|
|
55
|
+
├── rules/<name>.mdc (Cursor-only guidance)
|
|
56
|
+
├── hooks/hooks.json
|
|
57
|
+
├── .mcp.json
|
|
58
|
+
└── README.md
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`plugin init --scaffold` creates the standard `skills/`, `agents/`, `governances/`, and `commands/`
|
|
62
|
+
directories. Create the rest only when the plugin has content for them.
|
|
63
|
+
|
|
64
|
+
## Components
|
|
65
|
+
|
|
66
|
+
| Component | Field | Directory | Reaches |
|
|
67
|
+
|-----------|-------|-----------|---------|
|
|
68
|
+
| Skills | `skills` | `skills/<name>/SKILL.md` | every vendor |
|
|
69
|
+
| MCP servers | `mcpServers` | `.mcp.json` | every vendor |
|
|
70
|
+
| Commands | `commands` | `commands/<name>.md` | Claude Code, Cursor, Copilot CLI |
|
|
71
|
+
| Agents | `agents` | `agents/<name>.md` | Claude Code, Cursor, Copilot CLI |
|
|
72
|
+
| Hooks | `hooks` | `hooks/hooks.json` | every vendor — authored PascalCase, translated per vendor by the build; handler types vary |
|
|
73
|
+
| LSP servers | `lspServers` | `.lsp.json` | Claude Code, Cursor |
|
|
74
|
+
| Rules | `rules` | `rules/<name>.mdc` | Cursor only |
|
|
75
|
+
| Output styles | `outputStyles` | `output-styles/` | Claude Code only |
|
|
76
|
+
|
|
77
|
+
The universal minimum — reaching every runtime with no vendor manifest at all — is
|
|
78
|
+
`skills/<name>/SKILL.md` plus `.mcp.json`. Reach for a narrower component only when the plugin needs
|
|
79
|
+
what only that component does; `governance show plugin-design` is the authority on that choice.
|
|
80
|
+
|
|
81
|
+
## Constraints
|
|
82
|
+
|
|
83
|
+
- **Never hand-edit a derived manifest.** `plugin build` owns `.claude-plugin/plugin.json` and its
|
|
84
|
+
siblings; an edit there survives until the next build and no longer.
|
|
85
|
+
- **Never hand-edit a `version` field.** That move belongs to `/universal-plugin:version`.
|
|
86
|
+
- **The canonical schema is closed.** A vendor-only field cannot be added at the top level; it goes
|
|
87
|
+
under that vendor's `harnesses` entry or nowhere.
|
|
88
|
+
- Root `plugin.json` is not a build artifact. It is both the source of truth and the file Copilot CLI
|
|
89
|
+
reads, so deleting it takes out both.
|
|
90
|
+
|
|
91
|
+
## Next
|
|
92
|
+
|
|
93
|
+
Read only the `vendors/<vendor>.md` files for the runtimes being enabled. `SKILL.md` has the table.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Update a universal plugin
|
|
2
|
+
|
|
3
|
+
Change which vendors or components an existing plugin declares. Every path ends with a rebuild:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx universal-plugin plugin build
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Add a vendor
|
|
10
|
+
|
|
11
|
+
1. Add the vendor id to `extensions["org.cyberuni.universal-plugin"].vendors` in root
|
|
12
|
+
`plugin.json`, and add its key to `extensions["org.cyberuni.universal-plugin"].harnesses`.
|
|
13
|
+
2. Populate vendor-specific fields — see that vendor's [`vendors/<vendor>.md`](./vendors/).
|
|
14
|
+
3. If the vendor requires extra fields (`codex`: `version`, `description`), add them to the canonical
|
|
15
|
+
top level first — the build fails loudly without them.
|
|
16
|
+
4. Rebuild for the new vendor: `plugin build --vendor <id>`.
|
|
17
|
+
|
|
18
|
+
## Remove a vendor
|
|
19
|
+
|
|
20
|
+
1. Remove the vendor id from `extensions["org.cyberuni.universal-plugin"].vendors` and its key from
|
|
21
|
+
`harnesses`.
|
|
22
|
+
2. Delete the generated manifest at its output path. `copilot-cli` has none — removing it is a
|
|
23
|
+
manifest edit and nothing else.
|
|
24
|
+
|
|
25
|
+
## Add or remove a component
|
|
26
|
+
|
|
27
|
+
1. Add or remove the component field under `extensions["org.cyberuni.universal-plugin"]` in root
|
|
28
|
+
`plugin.json` (e.g. `"commands": "./commands/"`). [`standard.md`](./standard.md) has the component
|
|
29
|
+
table and which runtimes each one reaches.
|
|
30
|
+
2. Scaffold or delete the corresponding files.
|
|
31
|
+
3. Rebuild to regenerate all vendor manifests.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Claude Code
|
|
2
|
+
|
|
3
|
+
Reads `.claude-plugin/plugin.json`. **The build derives it** — never hand-edit it.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx universal-plugin plugin build --vendor claude-code
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## What lands in the derived manifest
|
|
10
|
+
|
|
11
|
+
The shared metadata from the canonical top level, plus the component paths, plus whatever
|
|
12
|
+
`extensions["org.cyberuni.universal-plugin"].harnesses["claude-code"]` sets. `$schema`, `extensions`,
|
|
13
|
+
`vendors`, `packagePath`, and `harnesses` are universal-plugin's own orchestration — they never
|
|
14
|
+
appear in a vendor manifest.
|
|
15
|
+
|
|
16
|
+
An empty `"claude-code": {}` is the normal case: it opts into the build with no overrides.
|
|
17
|
+
|
|
18
|
+
## Extra requirements
|
|
19
|
+
|
|
20
|
+
None beyond `name`. Claude Code loads a manifest that carries only the shared metadata.
|
|
21
|
+
|
|
22
|
+
## Skills
|
|
23
|
+
|
|
24
|
+
Claude Code reads the skills the manifest's `skills` path names. The build additionally rewrites each
|
|
25
|
+
`SKILL.md`'s frontmatter when the skill declares `invocation-policy` — `disable-model-invocation:
|
|
26
|
+
true` for `user`, `user-invocable: false` for `model`. See [`../frontmatter.md`](../frontmatter.md).
|
|
27
|
+
|
|
28
|
+
## Hooks
|
|
29
|
+
|
|
30
|
+
Author hooks once, in canonical form: **PascalCase** event names (`SessionStart`, `PreToolUse`,
|
|
31
|
+
`PostToolUse`, `Stop`, `UserPromptSubmit`) over Claude Code's matcher-group shape. That is what the
|
|
32
|
+
canonical schema admits, and `plugin build` derives the rest (ADR-0011).
|
|
33
|
+
|
|
34
|
+
Claude Code and Codex read that form as authored. Copilot CLI accepts it too — PascalCase selects its
|
|
35
|
+
Claude-compatible payload format. Cursor is the one vendor translated: it gets
|
|
36
|
+
`.cursor-plugin/hooks.json` with camelCase events, `"version": 1`, and each matcher group flattened
|
|
37
|
+
into one entry per handler, and its derived manifest points there.
|
|
38
|
+
|
|
39
|
+
**A handler type the vendor cannot run is dropped, and the build warns.** Claude Code runs `command`,
|
|
40
|
+
`http`, `prompt`, and `agent`; Codex runs `command` only; Cursor runs `command` and `prompt`; Copilot
|
|
41
|
+
CLI runs `command`, `http`, and `prompt`. Read the warnings — a plugin whose only `SessionStart`
|
|
42
|
+
handler is `http` reaches Claude Code and Copilot CLI and nothing else. Copilot CLI reads the
|
|
43
|
+
canonical file directly, so its unsupported handlers are reported as ignored at runtime rather than
|
|
44
|
+
dropped from a derived file.
|
|
45
|
+
|
|
46
|
+
Source: `.research/hook-event-survey/conclusion.md` (re-verified August 2026) — re-verify against
|
|
47
|
+
vendor docs before relying on it.
|
|
48
|
+
|
|
49
|
+
## Dependencies
|
|
50
|
+
|
|
51
|
+
Claude Code is the only runtime that reads a plugin dependency, and it acts on one: it installs a
|
|
52
|
+
missing dependency, enables it alongside the plugin that needs it, prunes it once nothing needs it,
|
|
53
|
+
and refuses to load a plugin whose declared range the installed version does not satisfy. Declare it
|
|
54
|
+
once, canonically, under `extensions["org.cyberuni.universal-plugin"].dependencies` — not under
|
|
55
|
+
`harnesses["claude-code"]` (ADR-0013):
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
"dependencies": ["cyber-asana", { "name": "cyber-notion", "marketplace": "cyberuni", "version": "^0.9.0" }]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
A bare name resolves against the declaring plugin's own marketplace; `marketplace` picks another one,
|
|
62
|
+
which the root marketplace must have allowed. Put a range in the object form — a range written as
|
|
63
|
+
`"cyber-asana@^0.9.0"` is accepted and then discarded by the runtime, and the build warns and names
|
|
64
|
+
the object to write instead.
|
|
65
|
+
|
|
66
|
+
Cursor, Codex, and Copilot CLI read no such field. The build leaves it out of their manifests and
|
|
67
|
+
warns; the build stays green. A plugin that loads there without its dependency is worth a line in
|
|
68
|
+
your README.
|
|
69
|
+
|
|
70
|
+
Source: `.research/plugin-schema/` (re-verified August 2026 against Claude Code 2.1.235) — re-verify
|
|
71
|
+
against vendor docs before relying on it.
|
|
72
|
+
|
|
73
|
+
## Leave alone
|
|
74
|
+
|
|
75
|
+
Output styles are Claude Code-only, and hook blocks in `.claude/settings.json` are settings, not
|
|
76
|
+
plugin content. Report them; do not convert them.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Codex
|
|
2
|
+
|
|
3
|
+
Reads `.codex-plugin/plugin.json`. **The build derives it** — never hand-edit it.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx universal-plugin plugin build --vendor codex
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Extra requirements — the build enforces these
|
|
10
|
+
|
|
11
|
+
Targeting Codex requires `version` **and** `description` on the canonical manifest. Without either,
|
|
12
|
+
`plugin build` fails loudly and writes nothing:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
plugin.json validation failed:
|
|
16
|
+
- description is required when targeting codex
|
|
17
|
+
- version is required when targeting codex
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The check is scoped to the vendors actually being built, so a Codex block that is not a selected
|
|
21
|
+
target never blocks a build of the others.
|
|
22
|
+
|
|
23
|
+
## Vendor-specific fields
|
|
24
|
+
|
|
25
|
+
Codex's presentation metadata goes under its `harnesses` entry:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
"codex": {
|
|
29
|
+
"interface": {
|
|
30
|
+
"displayName": "<Human Name>",
|
|
31
|
+
"category": "<category>"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Skills
|
|
37
|
+
|
|
38
|
+
For every skill that is not `invocation-policy: model`, the build also writes
|
|
39
|
+
`~/.codex/prompts/<name>.md` — the skill body, as a Codex prompt.
|
|
40
|
+
|
|
41
|
+
Two things follow. It writes **outside the repository**, into the current machine's home directory,
|
|
42
|
+
so it is not part of the plugin's tracked output and does not travel with a clone. And it is
|
|
43
|
+
**best-effort**: a failure there becomes a build warning, not a failed build. Read the warnings.
|
|
44
|
+
|
|
45
|
+
## Hooks
|
|
46
|
+
|
|
47
|
+
Codex hook events are **PascalCase**, like Claude Code's, so the canonical file reaches Codex as
|
|
48
|
+
authored. Codex runs `command` handlers only — an `http`, `prompt`, or `agent` handler is dropped
|
|
49
|
+
from `.codex-plugin/hooks.json` with a warning. See [`claude-code.md`](./claude-code.md).
|
|
50
|
+
|
|
51
|
+
## Dependencies
|
|
52
|
+
|
|
53
|
+
Codex reads no plugin dependency. A declaration is left out of `.codex-plugin/plugin.json` with a
|
|
54
|
+
build warning — deliberately, because the validator Codex ships for its plugin ingestion contract
|
|
55
|
+
rejects any field outside its allowlist, and one unaccepted key fails the whole manifest. See
|
|
56
|
+
[`claude-code.md`](./claude-code.md).
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# GitHub Copilot CLI
|
|
2
|
+
|
|
3
|
+
Reads the **canonical root `plugin.json` directly**. The build derives nothing for it and writes no
|
|
4
|
+
file — `plugin build` reports it with status `canonical`, which is success, not a skipped target.
|
|
5
|
+
|
|
6
|
+
## Why nothing is derived
|
|
7
|
+
|
|
8
|
+
Copilot CLI searches four paths and takes the first match:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
.plugin/plugin.json → plugin.json → .github/plugin/plugin.json → .claude-plugin/plugin.json
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Root `plugin.json` — the canonical manifest — is second, so it always shadows the two below it. It
|
|
15
|
+
has consumed Open Plugin Spec v1 manifests since v1.0.74, so it already serves the canonical manifest
|
|
16
|
+
as-is.
|
|
17
|
+
|
|
18
|
+
Earlier builds wrote `.github/plugin/plugin.json`. That path loses to root by construction and was
|
|
19
|
+
never read; a leftover copy is stale and safe to delete.
|
|
20
|
+
|
|
21
|
+
## Vendor-specific fields cannot be delivered
|
|
22
|
+
|
|
23
|
+
A `harnesses["copilot-cli"]` entry has nowhere to go. The canonical schema is closed
|
|
24
|
+
(`additionalProperties: false`), so a Copilot-only field cannot ride along in root, and there is no
|
|
25
|
+
derived file to put it in. The build warns:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
harnesses.copilot-cli sets category, tags, but copilot-cli reads the canonical plugin.json
|
|
29
|
+
directly — these fields are not delivered
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Treat that warning as a decision to make, not noise: either the field belongs to a vendor that has a
|
|
33
|
+
derived manifest, or it does not ship. Do not invent a path for it.
|
|
34
|
+
|
|
35
|
+
## Do not
|
|
36
|
+
|
|
37
|
+
- **Do not delete root `plugin.json` to "clean up" a Copilot target.** It is the source of truth and
|
|
38
|
+
the Copilot manifest at once.
|
|
39
|
+
- Do not write `.plugin/plugin.json`. It outranks root, so it would silently shadow the canonical
|
|
40
|
+
manifest with a copy nothing regenerates.
|
|
41
|
+
|
|
42
|
+
## Hooks
|
|
43
|
+
|
|
44
|
+
Copilot CLI accepts **either casing**, and the casing selects the payload format: PascalCase gets the
|
|
45
|
+
Claude-compatible format, so the canonical file reaches Copilot CLI unchanged. Because Copilot CLI
|
|
46
|
+
reads that file directly, the build derives nothing for it — an `agent` handler is reported as ignored
|
|
47
|
+
at runtime rather than dropped. See [`claude-code.md`](./claude-code.md).
|
|
48
|
+
|
|
49
|
+
## Dependencies
|
|
50
|
+
|
|
51
|
+
Copilot CLI reads no plugin dependency. Because it reads the canonical manifest directly, there is no
|
|
52
|
+
derived file to leave the declaration out of — it sits under `extensions`, which Copilot CLI ignores,
|
|
53
|
+
and the build reports it as ignored at runtime. See [`claude-code.md`](./claude-code.md).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Cursor
|
|
2
|
+
|
|
3
|
+
Reads `.cursor-plugin/plugin.json`. **The build derives it** — never hand-edit it.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx universal-plugin plugin build --vendor cursor
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Extra requirements
|
|
10
|
+
|
|
11
|
+
None beyond `name`.
|
|
12
|
+
|
|
13
|
+
## Vendor-specific fields
|
|
14
|
+
|
|
15
|
+
Cursor's catalog metadata goes under its `harnesses` entry, not at the canonical top level:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
"cursor": {
|
|
19
|
+
"publisher": "<org>",
|
|
20
|
+
"category": "<category>",
|
|
21
|
+
"tags": ["<tag>"]
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Skills
|
|
26
|
+
|
|
27
|
+
Cursor reads `SKILL.md` straight from the path the manifest's `skills` field names, and lets the user
|
|
28
|
+
invoke a skill by typing `/` and searching for it. **The build derives no per-skill artifact for
|
|
29
|
+
Cursor** — a mirrored `.cursor/commands/*.md` would be a second copy of the same body. Explicit-only
|
|
30
|
+
invocation is expressed natively through `disable-model-invocation`, which the build already writes
|
|
31
|
+
into the shared `SKILL.md`.
|
|
32
|
+
|
|
33
|
+
## Rules are Cursor-only
|
|
34
|
+
|
|
35
|
+
`rules/<name>.mdc` reaches Cursor and nothing else. Reach for a rule only when the plugin genuinely
|
|
36
|
+
needs always-on guidance in Cursor; anything a task can load on demand belongs in a skill, where every
|
|
37
|
+
runtime sees it. `governance show plugin-design` is the authority on that call.
|
|
38
|
+
|
|
39
|
+
`.mdc` and `.md` are not interchangeable, and path-scoping has no equivalent in the other runtimes —
|
|
40
|
+
never generate rules from a skill or a skill from a rule.
|
|
41
|
+
|
|
42
|
+
## Hooks
|
|
43
|
+
|
|
44
|
+
Cursor hook events are **camelCase** (`sessionStart`), and Cursor's hooks file differs in shape as
|
|
45
|
+
well as casing. The build derives `.cursor-plugin/hooks.json` from the canonical file — never author
|
|
46
|
+
it by hand. Cursor runs `command` and `prompt` handlers; an `http` or `agent` handler is dropped with
|
|
47
|
+
a warning. See [`claude-code.md`](./claude-code.md).
|
|
48
|
+
|
|
49
|
+
## Dependencies
|
|
50
|
+
|
|
51
|
+
Cursor reads no plugin dependency. A declaration is left out of `.cursor-plugin/plugin.json` with a
|
|
52
|
+
build warning. See [`claude-code.md`](./claude-code.md).
|
|
@@ -0,0 +1,11 @@
|
|
|
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'))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# marketplace skill
|
|
2
|
+
|
|
3
|
+
Make a repository installable on its own terms: generate the marketplace catalogs the runtimes read,
|
|
4
|
+
then write the README section that tells users what to type.
|
|
5
|
+
|
|
6
|
+
## What it does
|
|
7
|
+
|
|
8
|
+
`marketplace init` discovers the plugins under `plugins/` and derives one catalog per selected
|
|
9
|
+
runtime. This skill picks the targets with the user, runs the generation, verifies it, and offers
|
|
10
|
+
the install documentation that goes with it.
|
|
11
|
+
|
|
12
|
+
Nothing is published. The catalogs sit in the repository until someone adds it as a marketplace.
|
|
13
|
+
|
|
14
|
+
## Support is uneven, and the skill says so
|
|
15
|
+
|
|
16
|
+
| Runtime | Reality |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| Claude Code | a catalog plus two documented commands; works end to end |
|
|
19
|
+
| GitHub Copilot CLI | same, with `copilot plugin marketplace add` |
|
|
20
|
+
| Codex | a catalog plus `codex plugin marketplace add` and `codex plugin add`, both shipped and undocumented; works end to end |
|
|
21
|
+
| Cursor | a catalog Cursor reads, but no command that adds it locally; users get it through a team marketplace an admin imports |
|
|
22
|
+
|
|
23
|
+
`references/runtimes.md` is the only source of install commands, and every command in it carries an
|
|
24
|
+
evidence ID. That constraint exists because the obvious way to write an install section is to copy
|
|
25
|
+
one from another project's README, and two of the four commands in the README that prompted this
|
|
26
|
+
skill are not in any vendor documentation.
|
|
27
|
+
|
|
28
|
+
## The README half
|
|
29
|
+
|
|
30
|
+
`scripts/install-docs.mjs` reads the catalogs on disk and emits the section as JSON, so the
|
|
31
|
+
marketplace name, the plugin names, and the repository slug come from the repository rather than
|
|
32
|
+
from a model retyping them. The skill asks before editing the README, because it is the user's
|
|
33
|
+
document.
|
|
34
|
+
|
|
35
|
+
## References
|
|
36
|
+
|
|
37
|
+
- [Research: local marketplaces](https://github.com/cyberuni/universal-plugin/blob/main/.research/local-marketplaces/conclusion.md)
|
|
38
|
+
- [`marketplace init` spec](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/marketplace/init/README.md)
|