universal-plugin 0.4.0 → 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.
@@ -0,0 +1,170 @@
1
+ ---
2
+ name: marketplace
3
+ description: Use this skill to let people install a plugin straight from its own repository — generate the local marketplace catalogs Claude Code, Codex, GitHub Copilot CLI, and Cursor read, and write the README install section that tells users what to type. Trigger on "set up a local marketplace", "let users install this from my repo", "generate marketplace catalogs", "add install instructions to the README", "how do people install this plugin", or "make this repo installable".
4
+ argument-hint: '[--claude] [--codex] [--copilot] [--cursor] [--dry-run] [--force]'
5
+ ---
6
+
7
+ # Local marketplace
8
+
9
+ A repository can carry its own catalog, so a user adds the repository as a marketplace and installs
10
+ from it. No service, no submission, no account.
11
+
12
+ All four runtimes read such a catalog. Three of them let a user add it; Cursor's reaches users when
13
+ an admin imports the repository as a team marketplace.
14
+
15
+ | Runtime | Catalog it reads | What the user types |
16
+ | --- | --- | --- |
17
+ | Claude Code | `.claude-plugin/marketplace.json` | `/plugin marketplace add`, then `/plugin install` |
18
+ | Codex | `.agents/plugins/marketplace.json`, or the Claude path | `codex plugin marketplace add`, then `codex plugin add` |
19
+ | GitHub Copilot CLI | `.github/plugin/marketplace.json`, or the Claude path | `copilot plugin marketplace add`, then `copilot plugin install` |
20
+ | Cursor | `.cursor-plugin/marketplace.json` | nothing; an admin imports the repository as a team marketplace |
21
+
22
+ Read `references/runtimes.md` before writing any command into a README, and treat that file as the
23
+ only source of install commands. The trap that lives there: Codex installs with `plugin add` where
24
+ Copilot CLI uses `plugin install`.
25
+
26
+ ## Workflow
27
+
28
+ ### 1. Find what there is to list
29
+
30
+ ```bash
31
+ ls plugins/*/plugin.json 2>/dev/null
32
+ test -f plugin.json && cat plugin.json
33
+ ```
34
+
35
+ `plugin init --vendor <id>` already registers the plugin it scaffolds in these catalogs, so a
36
+ repository that has run it carries an entry before this skill starts. Read what is there first: this
37
+ command regenerates a catalog from what it discovers, which is the whole repository rather than one
38
+ plugin.
39
+
40
+ A catalog lists plugins found at `<scan-root>/<plugin-dir>/plugin.json`, which defaults to
41
+ `plugins/`. Pass `--plugin-scan-dir <dir>` when the repository keeps them elsewhere. A repository
42
+ whose only plugin sits at its root has nothing to discover; say so rather than generating an empty
43
+ catalog.
44
+
45
+ Discovery reads a plugin's `name` and nothing else. A missing or malformed `name` stops the command
46
+ before any write.
47
+
48
+ ### 2. Choose targets with the user
49
+
50
+ Name the runtimes and what each one gets, using the table above. With no target flags the command
51
+ selects all four.
52
+
53
+ One catalog can serve two runtimes when the user wants fewer files: Codex reads the Claude catalog
54
+ too, so `--claude` alone covers both. The reverse does not hold, because Claude Code rejects the
55
+ Codex catalog for its missing `owner`. Generating both is the default, so each is idiomatic for its
56
+ runtime.
57
+
58
+ ### 3. Generate
59
+
60
+ ```bash
61
+ node scripts/marketplace.mjs --claude --copilot --dry-run
62
+ ```
63
+
64
+ Resolve that path against this skill's own directory; `npx universal-plugin marketplace init` is the
65
+ fallback. Run `--dry-run` first and show the plan. The command never prompts.
66
+
67
+ Then generate for real. Selected targets compose as a union, so name every target you want each run.
68
+
69
+ | Status | Means |
70
+ | --- | --- |
71
+ | `generated` | written |
72
+ | `unchanged` | already correct, byte differences in key order and whitespace ignored |
73
+ | `planned` | `--dry-run` only |
74
+ | `empty` | nothing discovered for this target |
75
+
76
+ A selected artifact that differs from what would be generated stops the whole run. That is the
77
+ command protecting a hand-edited catalog. Read the difference, then re-run with `--force` only once
78
+ you know what it discards.
79
+
80
+ ### 4. Offer the README section
81
+
82
+ Ask before writing. A README is the user's document, and this is an edit to it, not a new file.
83
+
84
+ ```bash
85
+ node scripts/install-docs.mjs
86
+ ```
87
+
88
+ Stdout is one JSON object: `targets`, `repo`, and `markdown`. Insert `markdown` verbatim. It carries
89
+ a section per generated catalog, built from the marketplace name and plugin names actually on disk.
90
+
91
+ Check `repoResolved` first. When it is `false` the repository slug could not be found and the
92
+ snippet contains `<owner>/<repo>`; ask the user for the slug and re-run with `--repo <owner>/<repo>`
93
+ rather than leaving a placeholder in their README.
94
+
95
+ If the README already has an install section, show the difference and let the user choose. Do not
96
+ append a second one.
97
+
98
+ ### 5. Verify
99
+
100
+ Re-run the generator and confirm every selected target reports `unchanged`. Confirm each catalog
101
+ path exists. State plainly that nothing was published: these files sit in the repository until a
102
+ user adds it as a marketplace.
103
+
104
+ For Claude Code, check that each plugin `source` is a `./`-prefixed path that exists. Sources resolve
105
+ against the directory containing `.claude-plugin/`, and they do not resolve at all for a user who
106
+ adds the marketplace by direct URL to the JSON file.
107
+
108
+ A local path is the cheapest end-to-end proof:
109
+
110
+ ```bash
111
+ codex plugin marketplace add "$(pwd)"
112
+ codex plugin list
113
+ codex plugin marketplace remove <marketplace-name>
114
+ ```
115
+
116
+ `codex plugin list` prints the manifest path it read, which is what tells you the catalog was found
117
+ rather than merely present. Offer this check rather than running it unasked: it writes to the user's
118
+ Codex config. Remove what you added.
119
+
120
+ ## Local development against Codex
121
+
122
+ Codex installs a **copy** of the plugin at
123
+ `~/.codex/plugins/cache/<marketplace>/<plugin>/<version>`, where the version is the one the plugin's
124
+ own manifest carries. Editing the plugin's files does not reach that copy.
125
+
126
+ After changing packaged files, install again and start a new session:
127
+
128
+ ```bash
129
+ codex plugin add <plugin>@<marketplace>
130
+ ```
131
+
132
+ That one command is the whole refresh: re-running it at the same version overwrites the cached copy
133
+ with the current source, so neither `codex plugin remove` nor a version bump is needed. Codex reads
134
+ the cache when a session starts, so the session you are in keeps the old copy — start a new one.
135
+
136
+ The catalog entry's version is derived from the canonical manifest, so a version move updates both
137
+ (`/universal-plugin:version` owns that). Codex itself does
138
+ not read the entry's version; keeping it true is this project's policy, so the catalog never states
139
+ a version the plugin does not have.
140
+
141
+ ## Rules
142
+
143
+ - **Never publish a command that is not in `references/runtimes.md`.** An install command that fails
144
+ is worse than no install section. Widely-copied README snippets are not sources.
145
+ - **Name every catalog `marketplace.json`.** Codex discovers a catalog by that filename inside a
146
+ supported directory. A file named anything else is invisible to it, whatever directory holds it.
147
+ - **Do not write a Cursor install command.** Cursor has no command that adds a repository catalog;
148
+ a developer tests through `~/.cursor/plugins/local/<name>` and users get the plugin through a team
149
+ marketplace an admin imports.
150
+ - **Ask before editing the README**, and before `--force` replaces a catalog the user may have
151
+ hand-edited.
152
+ - This command publishes nothing and registers nothing. Say so in the report; a user who believes
153
+ they have published will not understand why nobody can install.
154
+ - Listing a plugin in the shared `cyberuni/marketplace` repository is a different job: use
155
+ `publish-plugin`.
156
+
157
+ ## Related skills
158
+
159
+ | Task | Skill |
160
+ |------|-------|
161
+ | Create or change the plugin being listed | `init` |
162
+ | Check that the plugin's own manifests are current | `doctor` |
163
+ | Move the version users will install | `version` |
164
+ | Submit to the shared marketplace repository instead | `publish-plugin` |
165
+
166
+ ## References
167
+
168
+ - `references/runtimes.md` — per-runtime install commands and their sources
169
+ - [Research conclusion](https://github.com/cyberuni/universal-plugin/blob/main/.research/local-marketplaces/conclusion.md)
170
+ - [`marketplace init` spec](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/marketplace/init/README.md)
@@ -0,0 +1,104 @@
1
+ # Install commands per runtime
2
+
3
+ The only source of install commands for this skill. Every entry traces to vendor documentation
4
+ reviewed on 2026-08-18 and recorded in `.research/local-marketplaces/evidence.md`. A command not
5
+ listed here does not go into a README.
6
+
7
+ ## Claude Code — documented, complete
8
+
9
+ Catalog: `.claude-plugin/marketplace.json` at the repository root (E-CC-M1).
10
+
11
+ ```
12
+ /plugin marketplace add <owner>/<repo>
13
+ /plugin install <plugin>@<marketplace-name>
14
+ ```
15
+
16
+ `<marketplace-name>` is the `name` field inside `marketplace.json`, not the repository name (E-CC-M2).
17
+
18
+ Both verbs also exist outside the session as `claude plugin marketplace add` and
19
+ `claude plugin install`. In a non-interactive shell, `--yes` accepts the command string an install
20
+ prints (E-CC-M3). Users refresh a catalog with `/plugin marketplace update`.
21
+
22
+ Plugin `source` paths resolve against the marketplace root, the directory holding `.claude-plugin/`.
23
+ They do not resolve when a user adds the marketplace by direct URL to the JSON file, so a repository
24
+ distributing by URL must use a non-relative plugin source (E-CC-M4).
25
+
26
+ Auto-update is off by default for third-party marketplaces, so a user who wants background refresh
27
+ enables it once from `/plugin` → Marketplaces. That is a user-side setting; this skill does not
28
+ change it.
29
+
30
+ ## GitHub Copilot CLI — documented, complete
31
+
32
+ Catalog: `.github/plugin/marketplace.json`. Copilot CLI also accepts `marketplace.json` at the root,
33
+ in `.plugin/`, or in `.claude-plugin/` (E-COPILOT-M3).
34
+
35
+ ```
36
+ copilot plugin marketplace add <owner>/<repo>
37
+ copilot plugin install <plugin>@<marketplace-name>
38
+ ```
39
+
40
+ The marketplace's own `name` becomes its registration key, and a user cannot choose a local alias
41
+ (E-COPILOT-M1). `copilot plugin install` also accepts `OWNER/REPO`, `OWNER/REPO:PATH`, a Git URL, or
42
+ a local path, so a plugin is installable from a repository even without a catalog (E-COPILOT-M2).
43
+
44
+ ## Codex — works, and reads two paths
45
+
46
+ Catalog: `.agents/plugins/marketplace.json`, which `marketplace init --codex` writes. Codex also
47
+ reads `.claude-plugin/marketplace.json`, so a repository that generates only the Claude catalog is
48
+ installable from Codex too (E-CODEX-M10, E-CODEX-M11).
49
+
50
+ ```
51
+ codex plugin marketplace add <owner>/<repo>
52
+ codex plugin add <plugin>@<marketplace-name>
53
+ ```
54
+
55
+ `marketplace add` also accepts a local path, `owner/repo@ref`, an HTTPS Git URL, or an SSH Git URL,
56
+ with `list`, `upgrade`, and `remove` beside it (E-CODEX-M3).
57
+
58
+ **Codex installs with `plugin add`, not `plugin install`.** Copilot CLI is the opposite. Do not
59
+ carry one runtime's verb over to the other.
60
+
61
+ Neither verb appears in Codex's published documentation. Both are in the shipped CLI, verified on
62
+ codex-cli 0.147.0.
63
+
64
+ **Discovery is by filename.** Codex looks for `marketplace.json` inside a supported directory. These
65
+ are read: `.claude-plugin/`, `.agents/plugins/`. These are not, with the correct filename in each:
66
+ `.codex-plugin/`, `.plugin/`, `.github/plugin/`, the repository root (E-CODEX-M10).
67
+
68
+ The two catalogs are not interchangeable in content. The Codex one carries `interface`, `policy`,
69
+ `category`, and an object `source`; Codex accepts the Claude shape as well, but Claude Code rejects
70
+ the Codex shape for its missing `owner` (E-CC-M5, E-CODEX-M9). Where a repository wants one file
71
+ instead of two, that file is the Claude one.
72
+
73
+ Codex copies an install to `~/.codex/plugins/cache/<marketplace>/<plugin>/<version>`, keyed by the
74
+ version the **plugin's own manifest** carries rather than the catalog entry's (E-CODEX-M13,
75
+ E-CODEX-M15). A source edit is invisible until the plugin is installed again and a new session
76
+ starts; `codex plugin add` on its own is the reinstall, because re-running it at the same version
77
+ overwrites the cached copy (E-CODEX-M14). A catalog entry that declares no version installs normally
78
+ (E-CODEX-M16).
79
+
80
+ ## Cursor — a catalog it reads, but no command to install from one
81
+
82
+ Cursor reads a repository-local catalog: `.cursor-plugin/marketplace.json` or
83
+ `.claude-plugin/marketplace.json` (E-CUR-M5), so the Claude catalog covers it. The documented shape
84
+ is close to Claude Code's — `name`, an object `owner` carrying `name` and an optional `email`, and
85
+ `plugins` whose `source` is a path inside the repository (E-CUR-M6). `marketplace init --cursor`
86
+ writes it.
87
+
88
+ Nothing on the command line consumes one. `cursor-agent` has no plugin subcommand (E-CUR-M2), and
89
+ users install from the Customize sidebar, sourced from Cursor's reviewed marketplace,
90
+ cursor.directory, or a team marketplace (E-CUR-M1). A repository catalog reaches users when an admin
91
+ imports it: Dashboard → Plugins → Team Marketplaces → Add Marketplace → Import from Repo, after
92
+ which Auto Refresh tracks the branch the marketplace is configured against (E-CUR-M7). So generate
93
+ the file and write no Cursor install command.
94
+
95
+ For local development, `universal-plugin plugin install` copies the plugin into
96
+ `~/.cursor/plugins/local/<name>`; reload the window afterwards. It copies rather than symlinks
97
+ because Cursor's scan resolves each symlink and rejects a target outside that directory (E-CUR-M4),
98
+ which is what the `ln -sfn "$(pwd)"` recipe that used to sit here ran into.
99
+
100
+ ## When these decay
101
+
102
+ A vendor command is a claim with a shelf life. Re-verify against the vendor page before relying on
103
+ an entry that looks stale, and update the evidence file in the same change. The recheck triggers are
104
+ listed in `.research/local-marketplaces/conclusion.md`.
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ // Derives README install instructions from the marketplace catalogs a repository already carries.
3
+ // Every command emitted here is traceable to vendor documentation; see
4
+ // .research/local-marketplaces/evidence.md. Do not add a command without an evidence row.
5
+ import { execFileSync } from 'node:child_process'
6
+ import * as fs from 'node:fs'
7
+ import * as path from 'node:path'
8
+
9
+ const argv = process.argv.slice(2)
10
+ const rootFlag = argv.indexOf('--root')
11
+ const root = path.resolve(rootFlag === -1 ? process.cwd() : (argv[rootFlag + 1] ?? process.cwd()))
12
+ const slugFlag = argv.indexOf('--repo')
13
+
14
+ const readJson = (file) => {
15
+ try {
16
+ return JSON.parse(fs.readFileSync(file, 'utf8'))
17
+ } catch {
18
+ return null
19
+ }
20
+ }
21
+
22
+ /** owner/repo, from the flag, the manifest's repository field, or the git remote. */
23
+ function resolveSlug() {
24
+ if (slugFlag !== -1 && argv[slugFlag + 1]) return argv[slugFlag + 1]
25
+ const manifest = readJson(path.join(root, 'plugin.json'))
26
+ const repo = typeof manifest?.repository === 'string' ? manifest.repository : manifest?.repository?.url
27
+ const fromManifest = repo?.match(/github\.com[/:]([^/]+\/[^/.]+)/)?.[1]
28
+ if (fromManifest) return fromManifest
29
+ try {
30
+ const remote = execFileSync('git', ['-C', root, 'remote', 'get-url', 'origin'], {
31
+ encoding: 'utf8',
32
+ stdio: ['ignore', 'pipe', 'ignore'],
33
+ })
34
+ return remote.match(/github\.com[/:]([^/]+\/[^/.\s]+)/)?.[1] ?? null
35
+ } catch {
36
+ return null
37
+ }
38
+ }
39
+
40
+ const slug = resolveSlug()
41
+ const repoRef = slug ?? '<owner>/<repo>'
42
+
43
+ const claude = readJson(path.join(root, '.claude-plugin/marketplace.json'))
44
+ const copilot = readJson(path.join(root, '.github/plugin/marketplace.json'))
45
+ // Codex reads its own catalog and the Claude one. Either makes a repository installable from Codex;
46
+ // prefer its own when both exist. See references/runtimes.md (E-CODEX-M10).
47
+ const codexOwn = readJson(path.join(root, '.agents/plugins/marketplace.json'))
48
+ const codex = codexOwn ?? claude
49
+ const cursor = readJson(path.join(root, '.cursor-plugin/marketplace.json'))
50
+
51
+ const targets = []
52
+ const sections = []
53
+
54
+ const pluginNames = (catalog) => (catalog?.plugins ?? []).map((p) => p.name).filter(Boolean)
55
+
56
+ if (claude) {
57
+ const names = pluginNames(claude)
58
+ const installs = (names.length > 0 ? names : ['<plugin>']).map((n) => `/plugin install ${n}@${claude.name}`)
59
+ targets.push('claude-code')
60
+ sections.push(['**Claude Code**', '', '```', `/plugin marketplace add ${repoRef}`, ...installs, '```'].join('\n'))
61
+ }
62
+
63
+ if (copilot) {
64
+ const names = pluginNames(copilot)
65
+ const installs = (names.length > 0 ? names : ['<plugin>']).map((n) => `copilot plugin install ${n}@${copilot.name}`)
66
+ targets.push('copilot-cli')
67
+ sections.push(
68
+ ['**GitHub Copilot CLI**', '', '```', `copilot plugin marketplace add ${repoRef}`, ...installs, '```'].join('\n'),
69
+ )
70
+ }
71
+
72
+ if (codex) {
73
+ const names = pluginNames(codex)
74
+ // Codex installs with `plugin add`; Copilot CLI installs with `plugin install`. Not interchangeable.
75
+ const installs = (names.length > 0 ? names : ['<plugin>']).map((n) => `codex plugin add ${n}@${codex.name}`)
76
+ targets.push('codex')
77
+ sections.push(
78
+ [
79
+ '**Codex**',
80
+ '',
81
+ '```',
82
+ `codex plugin marketplace add ${repoRef}`,
83
+ ...installs,
84
+ '```',
85
+ '',
86
+ 'Start a new session before using the plugin.',
87
+ ].join('\n'),
88
+ )
89
+ }
90
+
91
+ if (cursor) {
92
+ targets.push('cursor')
93
+ sections.push(
94
+ [
95
+ '**Cursor**',
96
+ '',
97
+ 'This repository carries a Cursor catalog, but Cursor has no command that adds it from a',
98
+ 'local path: a team admin imports the repository from the Cursor dashboard. To try the plugin',
99
+ 'before then, run `npx universal-plugin plugin install --vendor cursor` in a clone, then',
100
+ 'reload the window.',
101
+ ].join('\n'),
102
+ )
103
+ }
104
+
105
+ const markdown = sections.length === 0 ? '' : ['## Install', '', sections.join('\n\n'), ''].join('\n')
106
+
107
+ process.stdout.write(
108
+ `${JSON.stringify({
109
+ root,
110
+ repo: slug,
111
+ repoResolved: slug !== null,
112
+ targets,
113
+ markdown,
114
+ })}\n`,
115
+ )
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ // Runs `universal-plugin marketplace init` from the CLI that ships beside this skill, so catalog
3
+ // generation 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/marketplace.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, 'marketplace', 'init')
11
+ await import(join(packageRoot, 'bin', 'universal-plugin.mjs'))
@@ -9,6 +9,10 @@ Guides adding an already-packaged plugin to a marketplace repo by opening a pull
9
9
 
10
10
  **Default marketplace repo:** `cyberuni/marketplace` (adjust if the user targets a different one)
11
11
 
12
+ Listing a plugin in a shared repository is not the only way to distribute it. A repository can carry
13
+ its own catalog and let users add it directly as a marketplace. That is the `marketplace` skill; the
14
+ two compose, and neither replaces the other.
15
+
12
16
  ## Overview
13
17
 
14
18
  Publishing has three steps:
@@ -51,12 +55,10 @@ See `references/vendor-requirements.md` for required fields and hook casing rule
51
55
 
52
56
  ### 1c. Hook casing check
53
57
 
54
- If the plugin has hooks, verify each vendor manifest uses the correct event name casing:
55
-
56
- - Claude Code and Codex: **PascalCase** (`SessionStart`, `PreToolCall`)
57
- - Cursor and GitHub Copilot CLI: **camelCase** (`sessionStart`, `preToolCall`)
58
-
59
- Mixed casing causes silent hook failures at runtime.
58
+ `plugin build` owns hook casing: the canonical file is authored in PascalCase, and Cursor's
59
+ `.cursor-plugin/hooks.json` is derived from it. Verify the build ran rather than hand-checking the
60
+ casing, and read its warnings — a handler type the vendor cannot run is dropped, so a hook can be
61
+ absent from a vendor's file by design.
60
62
 
61
63
  ### 1d. Skills check (if present)
62
64
 
@@ -217,7 +219,7 @@ gh pr create \
217
219
  ## Checklist
218
220
 
219
221
  - [ ] All targeted vendor manifests present and valid
220
- - [ ] Hook event casing correct per vendor
222
+ - [ ] `plugin build` ran, and its dropped-handler warnings were read
221
223
  - [ ] Semver version string
222
224
  - [ ] SPDX license identifier
223
225
  - [ ] Entry appended to each detected marketplace file
@@ -233,7 +235,7 @@ Return the PR URL to the user when done.
233
235
 
234
236
  | Problem | Fix |
235
237
  |---|---|
236
- | Hook events silently don't fire | Check casing: Claude Code/Codex need PascalCase, Cursor/Copilot CLI need camelCase |
238
+ | Hook events silently don't fire | Re-run `plugin build`; check its warnings for a handler type that vendor cannot run |
237
239
  | Codex rejects manifest | `version` and `description` are required by Codex |
238
240
  | PR rejected: missing source link | Add `homepage` or `repository` to plugin.json |
239
241
  | Name conflict in marketplace | Check existing entries in each marketplace file first |
@@ -4,14 +4,14 @@
4
4
 
5
5
  **Manifest path:** `.claude-plugin/plugin.json`
6
6
  **Required fields:** `name`
7
- **Hook casing:** PascalCase (`SessionStart`, `PreToolCall`, `PostToolCall`, `Stop`)
7
+ **Hook casing:** PascalCase (`SessionStart`, `PreToolUse`, `PostToolUse`, `Stop`) — canonical form
8
8
  **Schema:** https://json.schemastore.org/claude-code-plugin-manifest.json
9
9
 
10
10
  ## Cursor
11
11
 
12
12
  **Manifest path:** `.cursor-plugin/plugin.json`
13
13
  **Required fields:** `name`
14
- **Hook casing:** camelCase (`sessionStart`, `preToolCall`, `postToolCall`)
14
+ **Hook casing:** camelCase (`sessionStart`, `preToolUse`, `postToolUse`) — derived by `plugin build`
15
15
  **Schema:** https://raw.githubusercontent.com/cursor/plugins/main/schemas/plugin.schema.json
16
16
 
17
17
  ## Codex
@@ -24,15 +24,18 @@
24
24
 
25
25
  **Manifest path:** `plugin.json` (root of plugin directory)
26
26
  **Required fields:** `name`
27
- **Hook casing:** camelCase (follows Cursor convention)
27
+ **Hook casing:** either — PascalCase selects its Claude-compatible payload format, so the canonical file serves it
28
28
  **Notes:** Also searches `.plugin/plugin.json` as a fallback path
29
29
 
30
30
  ## Hook event name reference
31
31
 
32
- | Canonical | Claude Code / Codex | Cursor / Copilot CLI |
33
- |---|---|---|
34
- | session start | `SessionStart` | `sessionStart` |
35
- | session end | `SessionStop` | `sessionStop` |
36
- | before tool | `PreToolCall` | `preToolCall` |
37
- | after tool | `PostToolCall` | `postToolCall` |
38
- | agent stop | `Stop` | `stop` |
32
+ Author the canonical name; `plugin build` derives Cursor's. Claude Code, Codex, and Copilot CLI read
33
+ the canonical name as authored.
34
+
35
+ | Canonical (Claude Code, Codex, Copilot CLI) | Cursor |
36
+ |---|---|
37
+ | `SessionStart` | `sessionStart` |
38
+ | `SessionEnd` | `sessionEnd` |
39
+ | `PreToolUse` | `preToolUse` |
40
+ | `PostToolUse` | `postToolUse` |
41
+ | `Stop` | `stop` |
@@ -111,7 +111,8 @@ Every guard resolves before the first write, so a failed run leaves the tree unt
111
111
  | Create, adopt, or change what the plugin declares | `init` |
112
112
  | Check whether the two authored versions agree | `doctor` |
113
113
  | Add a changeset for the change being released | `add-changeset` |
114
- | List the released plugin in a marketplace | `publish-plugin` |
114
+ | Refresh the repository's own marketplace catalogs after a bump | `marketplace` |
115
+ | List the released plugin in the shared marketplace | `publish-plugin` |
115
116
 
116
117
  ## References
117
118