universal-plugin 0.3.1 → 0.4.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 +76 -134
- package/package.json +3 -1
- package/plugin.json +1 -1
- package/readme.md +73 -42
- package/skills/doctor/README.md +42 -0
- package/skills/doctor/SKILL.md +123 -0
- package/skills/doctor/scripts/doctor.mjs +196 -0
- package/skills/init/README.md +57 -0
- package/skills/init/SKILL.md +200 -0
- package/skills/{plugin → init}/references/adopt.md +8 -4
- package/skills/init/references/create.md +111 -0
- package/skills/init/references/detection.md +62 -0
- package/skills/init/references/frontmatter.md +65 -0
- package/skills/init/references/standard.md +92 -0
- package/skills/init/references/update.md +31 -0
- package/skills/init/references/vendors/claude-code.md +44 -0
- package/skills/init/references/vendors/codex.md +48 -0
- package/skills/init/references/vendors/copilot-cli.md +45 -0
- package/skills/init/references/vendors/cursor.md +45 -0
- package/skills/init/scripts/init.mjs +11 -0
- 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} +26 -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,111 @@
|
|
|
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
|
+
ln -sf "$(pwd)" ~/.claude/plugins/local/<plugin-name> # Claude Code
|
|
105
|
+
ln -sf "$(pwd)" ~/.cursor/plugins/local/<plugin-name> # Cursor → Developer: Reload Window
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Next
|
|
109
|
+
|
|
110
|
+
Shipping it on npm → `migrate-plugin`. Listing it in a marketplace → `publish-plugin`. Releasing a
|
|
111
|
+
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,92 @@
|
|
|
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
|
+
|
|
42
|
+
`vendors` and `harnesses` are separate on purpose: `vendors` says what to build, `harnesses` says
|
|
43
|
+
what each build gets. A vendor listed in `vendors` with no `harnesses` entry still builds; a
|
|
44
|
+
`harnesses` entry with no `vendors` list builds only while `vendors` is absent.
|
|
45
|
+
|
|
46
|
+
## Directory layout
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
<plugin-root>/
|
|
50
|
+
├── plugin.json ← canonical definition (source of truth)
|
|
51
|
+
├── skills/<name>/SKILL.md
|
|
52
|
+
├── commands/<name>.md
|
|
53
|
+
├── agents/<name>.md
|
|
54
|
+
├── rules/<name>.mdc (Cursor-only guidance)
|
|
55
|
+
├── hooks/hooks.json
|
|
56
|
+
├── .mcp.json
|
|
57
|
+
└── README.md
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`plugin init --scaffold` creates the standard `skills/`, `agents/`, `governances/`, and `commands/`
|
|
61
|
+
directories. Create the rest only when the plugin has content for them.
|
|
62
|
+
|
|
63
|
+
## Components
|
|
64
|
+
|
|
65
|
+
| Component | Field | Directory | Reaches |
|
|
66
|
+
|-----------|-------|-----------|---------|
|
|
67
|
+
| Skills | `skills` | `skills/<name>/SKILL.md` | every vendor |
|
|
68
|
+
| MCP servers | `mcpServers` | `.mcp.json` | every vendor |
|
|
69
|
+
| Commands | `commands` | `commands/<name>.md` | Claude Code, Cursor, Copilot CLI |
|
|
70
|
+
| Agents | `agents` | `agents/<name>.md` | Claude Code, Cursor, Copilot CLI |
|
|
71
|
+
| Hooks | `hooks` | `hooks/hooks.json` | partial — event names differ by case, and the build does not translate them |
|
|
72
|
+
| LSP servers | `lspServers` | `.lsp.json` | Claude Code, Cursor |
|
|
73
|
+
| Rules | `rules` | `rules/<name>.mdc` | Cursor only |
|
|
74
|
+
| Output styles | `outputStyles` | `output-styles/` | Claude Code only |
|
|
75
|
+
|
|
76
|
+
The universal minimum — reaching every runtime with no vendor manifest at all — is
|
|
77
|
+
`skills/<name>/SKILL.md` plus `.mcp.json`. Reach for a narrower component only when the plugin needs
|
|
78
|
+
what only that component does; `governance show plugin-design` is the authority on that choice.
|
|
79
|
+
|
|
80
|
+
## Constraints
|
|
81
|
+
|
|
82
|
+
- **Never hand-edit a derived manifest.** `plugin build` owns `.claude-plugin/plugin.json` and its
|
|
83
|
+
siblings; an edit there survives until the next build and no longer.
|
|
84
|
+
- **Never hand-edit a `version` field.** That move belongs to `/universal-plugin:version`.
|
|
85
|
+
- **The canonical schema is closed.** A vendor-only field cannot be added at the top level; it goes
|
|
86
|
+
under that vendor's `harnesses` entry or nowhere.
|
|
87
|
+
- Root `plugin.json` is not a build artifact. It is both the source of truth and the file Copilot CLI
|
|
88
|
+
reads, so deleting it takes out both.
|
|
89
|
+
|
|
90
|
+
## Next
|
|
91
|
+
|
|
92
|
+
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,44 @@
|
|
|
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
|
+
Claude Code hook events are **PascalCase** (`SessionStart`, `PreToolUse`, `PostToolUse`, `Stop`,
|
|
31
|
+
`UserPromptSubmit`). Cursor and Copilot CLI use camelCase; Codex is PascalCase like Claude Code.
|
|
32
|
+
|
|
33
|
+
**The build does not translate event names.** It copies the `hooks` path through to every derived
|
|
34
|
+
manifest as declared, so one `hooks/hooks.json` cannot currently satisfy both casings. Author hooks
|
|
35
|
+
for the runtimes that share a casing, and say plainly which runtimes a hooks block does not reach
|
|
36
|
+
rather than implying portability the build does not deliver.
|
|
37
|
+
|
|
38
|
+
Source: `.research/hook-event-survey/conclusion.md` (June 2026) — re-verify against vendor docs
|
|
39
|
+
before relying on it.
|
|
40
|
+
|
|
41
|
+
## Leave alone
|
|
42
|
+
|
|
43
|
+
Output styles are Claude Code-only, and hook blocks in `.claude/settings.json` are settings, not
|
|
44
|
+
plugin content. Report them; do not convert them.
|
|
@@ -0,0 +1,48 @@
|
|
|
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. The build does not translate event names —
|
|
48
|
+
see [`claude-code.md`](./claude-code.md).
|
|
@@ -0,0 +1,45 @@
|
|
|
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 hook events are **camelCase**. The build does not translate event names — see
|
|
45
|
+
[`claude-code.md`](./claude-code.md).
|
|
@@ -0,0 +1,45 @@
|
|
|
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`). The build does not translate event names — see
|
|
45
|
+
[`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
|
+
# remove-plugin skill
|
|
2
|
+
|
|
3
|
+
Remove a universal plugin's artifacts — from cleaning a build output to taking the plugin out of a
|
|
4
|
+
project entirely.
|
|
5
|
+
|
|
6
|
+
## Three removals, not one
|
|
7
|
+
|
|
8
|
+
They are not equally reversible, and the skill establishes which one is being asked for before
|
|
9
|
+
deleting anything:
|
|
10
|
+
|
|
11
|
+
| Ask | Removes | Reversible by |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| clean the build output | derived vendor manifests | `plugin build` |
|
|
14
|
+
| drop a vendor | one manifest, and its declaration | re-adding the vendor, then a build |
|
|
15
|
+
| remove the plugin | the canonical manifest and every component | nothing |
|
|
16
|
+
|
|
17
|
+
Only the last is irreversible, and only it needs a confirmation.
|
|
18
|
+
|
|
19
|
+
## What it will not do
|
|
20
|
+
|
|
21
|
+
Root `plugin.json` is never deleted as cleanup. It is the canonical source of truth *and* the
|
|
22
|
+
manifest GitHub Copilot CLI reads, so removing it takes out the source and a live target at once.
|
|
23
|
+
|
|
24
|
+
Dropping a vendor is a manifest edit first: deleting only the file leaves the vendor declared, and
|
|
25
|
+
the next build writes it straight back. That edit routes to `init`.
|
|
26
|
+
|
|
27
|
+
Deleting a published plugin's source does not unpublish it. The skill says so rather than implying
|
|
28
|
+
the removal reached consumers.
|
|
29
|
+
|
|
30
|
+
## Why there is no delete script
|
|
31
|
+
|
|
32
|
+
`plugin build --clean` already removes exactly what the manifest declares and nothing it does not.
|
|
33
|
+
Anything beyond that is a judgment call about authored content, which is the part that should stay
|
|
34
|
+
in front of a human.
|
|
35
|
+
|
|
36
|
+
## References
|
|
37
|
+
|
|
38
|
+
- [Spec](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/spec.md)
|