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.
Files changed (51) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/.cursor-plugin/plugin.json +1 -1
  4. package/LICENSE +21 -0
  5. package/dist/cli.mjs +1262 -375
  6. package/dist/data/vendors.json +12 -0
  7. package/dist/run.mjs +4 -4
  8. package/governances/plugin-design.md +42 -1
  9. package/package.json +3 -1
  10. package/plugin.json +1 -1
  11. package/readme.md +80 -42
  12. package/skills/doctor/README.md +42 -0
  13. package/skills/doctor/SKILL.md +144 -0
  14. package/skills/doctor/scripts/doctor.mjs +273 -0
  15. package/skills/init/README.md +57 -0
  16. package/skills/init/SKILL.md +219 -0
  17. package/skills/{plugin → init}/references/adopt.md +8 -4
  18. package/skills/init/references/create.md +122 -0
  19. package/skills/init/references/detection.md +62 -0
  20. package/skills/init/references/frontmatter.md +65 -0
  21. package/skills/init/references/standard.md +93 -0
  22. package/skills/init/references/update.md +31 -0
  23. package/skills/init/references/vendors/claude-code.md +76 -0
  24. package/skills/init/references/vendors/codex.md +56 -0
  25. package/skills/init/references/vendors/copilot-cli.md +53 -0
  26. package/skills/init/references/vendors/cursor.md +52 -0
  27. package/skills/init/scripts/init.mjs +11 -0
  28. package/skills/marketplace/README.md +38 -0
  29. package/skills/marketplace/SKILL.md +170 -0
  30. package/skills/marketplace/references/runtimes.md +104 -0
  31. package/skills/marketplace/scripts/install-docs.mjs +115 -0
  32. package/skills/marketplace/scripts/marketplace.mjs +11 -0
  33. package/skills/publish-plugin/SKILL.md +10 -8
  34. package/skills/publish-plugin/references/vendor-requirements.md +13 -10
  35. package/skills/remove-plugin/README.md +38 -0
  36. package/skills/remove-plugin/SKILL.md +87 -0
  37. package/skills/version/README.md +36 -0
  38. package/skills/{plugin/references/version.md → version/SKILL.md} +27 -5
  39. package/skills/version/scripts/version.mjs +11 -0
  40. package/skills/plugin/README.md +0 -37
  41. package/skills/plugin/SKILL.md +0 -105
  42. package/skills/plugin/references/create.md +0 -163
  43. package/skills/plugin/references/delete.md +0 -23
  44. package/skills/plugin/references/inspect.md +0 -21
  45. package/skills/plugin/references/update.md +0 -26
  46. /package/skills/{plugin → init}/assets/templates/agent.md +0 -0
  47. /package/skills/{plugin → init}/assets/templates/command.md +0 -0
  48. /package/skills/{plugin → init}/assets/templates/hooks.json +0 -0
  49. /package/skills/{plugin → init}/assets/templates/plugin.json +0 -0
  50. /package/skills/{plugin → init}/assets/templates/setup-command.md +0 -0
  51. /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)