universal-plugin 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/bin/upx.mjs +12 -5
- package/com.github.copilot/agents/agentskills-specialist.agent.md +132 -0
- package/dist/cli.mjs +5806 -261
- package/governances/plugin-design.md +14 -0
- package/package.json +4 -1
- package/plugin.json +1 -1
- package/readme.md +12 -5
- package/schema/README.md +10 -0
- package/schema/claude-code-marketplace.json +1939 -0
- package/schema/extension.schema.json +842 -0
- package/skills/adopt-upx/README.md +4 -4
- package/skills/adopt-upx/SKILL.md +4 -4
- package/skills/doctor/README.md +3 -4
- package/skills/doctor/SKILL.md +44 -14
- package/skills/doctor/scripts/doctor.mjs +145 -37
- package/skills/{init → init-universal-plugin}/README.md +5 -2
- package/skills/{init → init-universal-plugin}/SKILL.md +38 -13
- package/skills/init-universal-plugin/references/adopt.md +206 -0
- package/skills/{init → init-universal-plugin}/references/detection.md +8 -2
- package/skills/{init → init-universal-plugin}/references/standard.md +5 -1
- package/skills/{init → init-universal-plugin}/references/vendors/claude-code.md +1 -1
- package/skills/init-universal-plugin/references/vendors/copilot-cli.md +201 -0
- package/skills/marketplace/SKILL.md +1 -1
- package/skills/migrate-plugin/SKILL.md +140 -26
- package/skills/migrate-plugin/evals/evals.json +6 -0
- package/skills/migrate-plugin/evals/trigger-queries.json +18 -0
- package/skills/publish-plugin/README.md +35 -0
- package/skills/publish-plugin/SKILL.md +118 -19
- package/skills/publish-plugin/evals/evals.json +12 -0
- package/skills/remove-plugin/README.md +1 -1
- package/skills/remove-plugin/SKILL.md +2 -2
- package/skills/version/SKILL.md +2 -2
- package/dist/run.mjs +0 -271
- package/skills/init/references/adopt.md +0 -118
- package/skills/init/references/vendors/copilot-cli.md +0 -53
- /package/skills/{init → init-universal-plugin}/assets/templates/agent.md +0 -0
- /package/skills/{init → init-universal-plugin}/assets/templates/command.md +0 -0
- /package/skills/{init → init-universal-plugin}/assets/templates/hooks.json +0 -0
- /package/skills/{init → init-universal-plugin}/assets/templates/plugin.json +0 -0
- /package/skills/{init → init-universal-plugin}/assets/templates/setup-command.md +0 -0
- /package/skills/{init → init-universal-plugin}/assets/templates/skill.md +0 -0
- /package/skills/{init → init-universal-plugin}/references/create.md +0 -0
- /package/skills/{init → init-universal-plugin}/references/frontmatter.md +0 -0
- /package/skills/{init → init-universal-plugin}/references/update.md +0 -0
- /package/skills/{init → init-universal-plugin}/references/vendors/codex.md +0 -0
- /package/skills/{init → init-universal-plugin}/references/vendors/cursor.md +0 -0
- /package/skills/{init → init-universal-plugin}/scripts/init.mjs +0 -0
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: migrate-plugin
|
|
3
|
-
description: "Move a repository-root universal agent plugin into its npm package so the package distributes its manifest, vendor manifests, skills, and agents. Use this skill when packaging an existing plugin for npm, relocating a top-level plugin into a package,
|
|
3
|
+
description: "Move a repository-root or sibling-workspace universal agent plugin into its npm package so the package distributes its manifest, vendor manifests, skills, and agents, and bundle the package's CLI with tsdown so it runs from an installed plugin directory with no node_modules. Use this skill when packaging an existing plugin for npm, relocating a top-level or plugins/<name> plugin into a package, fixing a package that omits plugin assets, or making a plugin's bundled CLI self-contained, even if the user says only 'ship this plugin through npm', 'move the plugin into the package', or 'the CLI can't find its dependencies when installed as a plugin.'"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Migrate Universal Plugin to npm
|
|
7
7
|
|
|
8
8
|
## Scope
|
|
9
9
|
|
|
10
|
-
Move one existing universal plugin
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
Move one existing universal plugin into its owning npm package. The plugin may
|
|
11
|
+
sit at the repository root or in its own workspace member such as
|
|
12
|
+
`plugins/<name>/`. The package becomes the plugin root and must contain every
|
|
13
|
+
runtime artifact it needs after installation — including a CLI that runs
|
|
14
|
+
without `node_modules`.
|
|
13
15
|
|
|
14
16
|
This skill does not create a plugin from scratch, publish a package, or move
|
|
15
17
|
project-local agent configuration unrelated to the plugin.
|
|
@@ -22,34 +24,44 @@ Before changing manifest fields or component compatibility behavior, consult the
|
|
|
22
24
|
and its versioned specification: it is the canonical reference for the current
|
|
23
25
|
standard. Keep an existing schema version pinned unless the user explicitly
|
|
24
26
|
requests a standards upgrade.
|
|
25
|
-
Inventory these
|
|
27
|
+
Inventory these plugin assets in the source root when they exist:
|
|
26
28
|
|
|
27
29
|
- `plugin.json`
|
|
28
30
|
- vendor manifest directories for Claude Code, Cursor, Codex, and Copilot CLI
|
|
31
|
+
- `.plugin/` (for example a `pins.json` that skills read at runtime)
|
|
29
32
|
- `skills/`, `agents/`, `commands/`, `hooks/`, `rules/`, `output-styles/`
|
|
30
33
|
- `.mcp.json`, `.lsp.json`, and plugin-owned `assets/`
|
|
31
34
|
|
|
35
|
+
Also inventory what exists only to host the plugin in its old place: a
|
|
36
|
+
workspace-member `package.json` (for example a private `@scope/<name>-plugin`),
|
|
37
|
+
and the `pnpm-workspace.yaml` or `workspaces` glob that includes it.
|
|
38
|
+
|
|
32
39
|
Leave project configuration in place unless it is required solely to maintain
|
|
33
40
|
the moved plugin. In particular, do not move `.agents/skills/`, plans, or a
|
|
34
|
-
marketplace catalog such as `.claude/marketplace.json`.
|
|
41
|
+
marketplace catalog such as `.claude-plugin/marketplace.json`.
|
|
35
42
|
|
|
36
43
|
Before modifying files, report the exact source-to-destination mapping and any
|
|
37
|
-
destination collisions.
|
|
38
|
-
|
|
44
|
+
destination collisions. A `readme.md` on both sides is the usual one: merge the
|
|
45
|
+
plugin readme into the package readme as its own section rather than
|
|
46
|
+
overwriting either. Ask for confirmation if a destination contains a different
|
|
47
|
+
file that would be overwritten.
|
|
39
48
|
|
|
40
49
|
## 2. Move the plugin root
|
|
41
50
|
|
|
42
|
-
Move each inventoried asset under the package directory
|
|
43
|
-
|
|
51
|
+
Move each inventoried asset under the package directory with `git mv`,
|
|
52
|
+
preserving its relative path, so history follows the files:
|
|
44
53
|
|
|
45
54
|
```text
|
|
46
55
|
the canonical manifest
|
|
47
56
|
vendor manifests
|
|
57
|
+
.plugin/
|
|
48
58
|
skills
|
|
49
59
|
agents
|
|
50
60
|
```
|
|
51
61
|
|
|
52
62
|
The source root must no longer retain duplicate distributable plugin assets.
|
|
63
|
+
When the source was its own workspace member, delete its `package.json`, drop
|
|
64
|
+
the now-empty workspace glob, and reinstall so the lockfile loses the member.
|
|
53
65
|
Do not move a project-local skill merely because it is under `.agents/skills/`.
|
|
54
66
|
|
|
55
67
|
## 3. Configure npm packaging
|
|
@@ -68,6 +80,7 @@ configuration is:
|
|
|
68
80
|
".claude-plugin",
|
|
69
81
|
".cursor-plugin",
|
|
70
82
|
".codex-plugin",
|
|
83
|
+
".plugin",
|
|
71
84
|
"skills",
|
|
72
85
|
"agents"
|
|
73
86
|
]
|
|
@@ -77,30 +90,131 @@ configuration is:
|
|
|
77
90
|
Retain existing package entries such as `bin`, `dist`, and `governances`.
|
|
78
91
|
Never replace the allowlist wholesale.
|
|
79
92
|
|
|
80
|
-
## 4.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
the
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
## 4. Bundle the CLI with tsdown
|
|
94
|
+
|
|
95
|
+
An installed agent plugin is a copy of a source checkout, not an npm install,
|
|
96
|
+
so its directory has no reliable `node_modules`. A CLI whose build leaves
|
|
97
|
+
dependencies external fails there. When the package ships a CLI, build it so
|
|
98
|
+
every runtime dependency is inlined into the CLI entry, while any library entry
|
|
99
|
+
keeps its dependencies external.
|
|
100
|
+
|
|
101
|
+
Split `tsdown.config.ts` into two configs that share an `outDir`:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { defineConfig } from 'tsdown'
|
|
105
|
+
|
|
106
|
+
// Two configs, because the entries want opposite dependency treatment. They share an
|
|
107
|
+
// `outDir`, which is safe: tsdown hoists `clean` and runs it once across every config
|
|
108
|
+
// before any build writes, so neither wipes the other.
|
|
109
|
+
const shared = {
|
|
110
|
+
outDir: 'dist',
|
|
111
|
+
format: 'esm',
|
|
112
|
+
platform: 'node',
|
|
113
|
+
clean: true,
|
|
114
|
+
} as const
|
|
115
|
+
|
|
116
|
+
export default defineConfig([
|
|
117
|
+
{
|
|
118
|
+
// Library entry. Dependencies stay EXTERNAL: their types surface in the public
|
|
119
|
+
// `.d.ts`, and a consumer that also uses them must share one copy.
|
|
120
|
+
...shared,
|
|
121
|
+
entry: { index: 'src/index.ts' },
|
|
122
|
+
dts: true,
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
// CLI entry. Every runtime dependency is inlined so it runs with no node_modules.
|
|
126
|
+
...shared,
|
|
127
|
+
entry: { cli: 'src/cli.ts' },
|
|
128
|
+
dts: false,
|
|
129
|
+
deps: {
|
|
130
|
+
alwaysBundle: [/^commander(\/|$)/, /^some-dep(\/|$)/],
|
|
131
|
+
onlyBundle: false,
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
])
|
|
89
135
|
```
|
|
90
136
|
|
|
137
|
+
Rules for the CLI config:
|
|
138
|
+
|
|
139
|
+
- **`alwaysBundle` lists exactly the package's own `dependencies`.** Those are
|
|
140
|
+
the only ones tsdown externalizes by default; their transitive dependencies
|
|
141
|
+
are inlined automatically. Use `^name(\/|$)` regexes so subpath imports
|
|
142
|
+
(`some-dep/worktree`) are caught too.
|
|
143
|
+
- **Omit a dependency consumed only through `import type`.** TypeScript strips
|
|
144
|
+
it before bundling, and a package whose entry is raw `.ts` cannot be bundled.
|
|
145
|
+
- **`onlyBundle: false`** silences the "bundled a dependency" warnings that are
|
|
146
|
+
the point of this config.
|
|
147
|
+
- **Keep the existing output extensions.** If `bin` and `exports` already name
|
|
148
|
+
`.mjs`, leave tsdown's default; if they name `.js`, add
|
|
149
|
+
`outExtensions: () => ({ js: '.js' })` to `shared`. Never move published
|
|
150
|
+
paths as a side effect.
|
|
151
|
+
- **Keep the entry's invocation contract.** When `bin` points straight at the
|
|
152
|
+
CLI output, `src/cli.ts` must keep its `#!/usr/bin/env node` shebang (tsdown
|
|
153
|
+
preserves it and sets the execute bit). When `bin` is a wrapper that imports
|
|
154
|
+
the output and calls an exported function, leave the wrapper as is.
|
|
155
|
+
- **Resolve runtime files relative to the output.** A CLI that reads
|
|
156
|
+
`../package.json` through `import.meta.url` still works only if `src/` and
|
|
157
|
+
`dist/` sit at the same depth and `package.json` ships — confirm both.
|
|
158
|
+
|
|
159
|
+
Verify the bundle, not just the build:
|
|
160
|
+
|
|
161
|
+
1. `grep -En "from ['\"](dep-a|dep-b)" dist/cli.*` prints nothing, while the
|
|
162
|
+
library output still imports them.
|
|
163
|
+
2. Pack into a scratch directory, extract, confirm the extracted `package/` has
|
|
164
|
+
no `node_modules`, then run the CLI through its `bin` path: `--version` and
|
|
165
|
+
one read-only command. Do not run it from the workspace, where hoisted
|
|
166
|
+
`node_modules` hides a missing inline.
|
|
167
|
+
|
|
168
|
+
Skills that invoke the CLI through `npx <cli>@<version>` can then prefer the
|
|
169
|
+
shipped CLI. That changes skill behavior and often a frozen spec, so offer it
|
|
170
|
+
as a follow-up instead of doing it inside this migration.
|
|
171
|
+
|
|
172
|
+
## 5. Preserve release synchronization
|
|
173
|
+
|
|
174
|
+
Point every version and build step at the new plugin root:
|
|
175
|
+
|
|
176
|
+
- Set the manifest extension's `packagePath` to `"."`.
|
|
177
|
+
- `universal-plugin publish sync-version --root <pkg>` reads `packagePath` from
|
|
178
|
+
`<pkg>/.agents/universal-plugin.json`, not from the manifest extension. To use
|
|
179
|
+
it, create that file with `{ "packagePath": "." }`. If the repository instead
|
|
180
|
+
runs its own sync script, repoint that script's manifest path and leave it.
|
|
181
|
+
- Change every `universal-plugin plugin build --root <old>` in `package.json`
|
|
182
|
+
scripts to the package directory, then run it and confirm it reports the
|
|
183
|
+
vendor manifests as built and the catalog as unchanged or refolded.
|
|
184
|
+
- Repoint a local-directory `source` in the repository's marketplace catalog to
|
|
185
|
+
the package directory.
|
|
186
|
+
|
|
91
187
|
Update any checked-in skill lockfile that records an absolute source path for a
|
|
92
188
|
moved skill. Do not modify unrelated agent configuration.
|
|
93
189
|
|
|
94
|
-
##
|
|
190
|
+
## 6. Repoint tooling and prose
|
|
191
|
+
|
|
192
|
+
Search the repository for the old plugin path and update live references:
|
|
95
193
|
|
|
96
|
-
|
|
194
|
+
- lint and format excludes for generated vendor manifests (for example
|
|
195
|
+
`biome.json` `files.includes` negations), knip workspaces, turbo filters
|
|
196
|
+
- `AGENTS.md`, `CONTRIBUTING.md`, readmes, docs pages, and spec frontmatter
|
|
197
|
+
such as `project-path`
|
|
198
|
+
- guards scoped to the package directory, such as a vocabulary or lint check —
|
|
199
|
+
confirm moved skills did not enter a scope that rejects their wording
|
|
200
|
+
|
|
201
|
+
Leave historical records as written: changelogs, ADRs, research notes, and
|
|
202
|
+
decision or gate ledgers describe the layout at the time.
|
|
203
|
+
|
|
204
|
+
## 7. Verify the result
|
|
205
|
+
|
|
206
|
+
1. Run the repository's full verification (lint, build, typecheck, test).
|
|
97
207
|
2. Run `npm pack --dry-run` from the destination package. Confirm it lists
|
|
98
|
-
`plugin.json`, every required vendor manifest,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
4. Search the repository for stale references to the old
|
|
208
|
+
`plugin.json`, every required vendor manifest, `.plugin/` when present,
|
|
209
|
+
`skills/`, `agents/`, and the bundled CLI.
|
|
210
|
+
3. Confirm step 4's no-`node_modules` run passed.
|
|
211
|
+
4. Search the repository again for stale references to the old asset paths.
|
|
102
212
|
5. Review the diff to confirm no project-local `.agents` content or marketplace
|
|
103
|
-
catalog was
|
|
213
|
+
catalog was moved into the package.
|
|
214
|
+
6. Add a changeset for the package when the repository uses changesets.
|
|
215
|
+
|
|
216
|
+
Commit the move and the CLI bundling as separate commits: each is revertable on
|
|
217
|
+
its own.
|
|
104
218
|
|
|
105
219
|
Report the package path, files added to the npm tarball, checks run, and any
|
|
106
220
|
intentionally retained root-local files.
|
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
"prompt": "Move the top-level universal plugin into packages/universal-plugin so npm distributes it.",
|
|
7
7
|
"expected_output": "Inventories and relocates only distributable plugin assets, adds them to package.json files, updates version synchronization, verifies npm pack --dry-run, and preserves project-local .agents content.",
|
|
8
8
|
"files": []
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": 2,
|
|
12
|
+
"prompt": "Convert plugins/my-tool into an npm-distributed plugin inside packages/my-tool. The package already has a CLI built with tsdown that depends on commander.",
|
|
13
|
+
"expected_output": "Moves the plugin assets with git mv, merges colliding readmes instead of overwriting, removes the old workspace member and its glob, extends package.json files, sets packagePath to '.', repoints plugin build and the marketplace source, splits tsdown.config.ts so the CLI entry inlines commander via alwaysBundle while the library entry keeps it external, and proves the CLI runs from an extracted tarball with no node_modules.",
|
|
14
|
+
"files": []
|
|
9
15
|
}
|
|
10
16
|
]
|
|
11
17
|
}
|
|
@@ -19,6 +19,24 @@
|
|
|
19
19
|
"should_trigger": true,
|
|
20
20
|
"split": "val"
|
|
21
21
|
},
|
|
22
|
+
{
|
|
23
|
+
"id": 6,
|
|
24
|
+
"query": "Convert plugins/cyberlegion to an npm distributed plugin in packages/cyberlegion.",
|
|
25
|
+
"should_trigger": true,
|
|
26
|
+
"split": "train"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": 7,
|
|
30
|
+
"query": "Our plugin's CLI crashes with 'Cannot find package commander' when Claude Code installs the plugin. Bundle it so it runs without node_modules.",
|
|
31
|
+
"should_trigger": true,
|
|
32
|
+
"split": "val"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": 8,
|
|
36
|
+
"query": "Switch this library's build from tsup to tsdown.",
|
|
37
|
+
"should_trigger": false,
|
|
38
|
+
"split": "train"
|
|
39
|
+
},
|
|
22
40
|
{
|
|
23
41
|
"id": 4,
|
|
24
42
|
"query": "Upgrade all npx universal-plugin pins to version 0.3.0.",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# publish-plugin skill
|
|
2
|
+
|
|
3
|
+
List an already-packaged plugin in a shared marketplace repository by opening a pull request against
|
|
4
|
+
its vendor catalogs.
|
|
5
|
+
|
|
6
|
+
## What it does
|
|
7
|
+
|
|
8
|
+
Four steps: locate the plugin and the marketplace, validate the plugin is ready, prepare the entry
|
|
9
|
+
for each vendor catalog that exists, then open one PR that updates all of them together.
|
|
10
|
+
|
|
11
|
+
## Two repos, either one could be the cwd
|
|
12
|
+
|
|
13
|
+
The plugin being published and the marketplace it is going into are different repos, and the current
|
|
14
|
+
working directory could be either. The skill checks the cwd's remote against the target marketplace
|
|
15
|
+
repo (`cyberuni/marketplace` by default) before doing anything else:
|
|
16
|
+
|
|
17
|
+
- cwd is the plugin → the marketplace repo gets cloned or forked, as it always did.
|
|
18
|
+
- cwd is already the marketplace → work happens in place; the plugin's metadata is read from wherever
|
|
19
|
+
the user points to instead (a local path, or a URL cloned to a scratch directory).
|
|
20
|
+
|
|
21
|
+
Every check and every entry field in Steps 1–2 reads from the plugin location this step finds, never
|
|
22
|
+
from the cwd by assumption. Getting this wrong means the marketplace entry ends up pointing at the
|
|
23
|
+
marketplace repo's own URL instead of the plugin's.
|
|
24
|
+
|
|
25
|
+
## Why this is not the `marketplace` skill
|
|
26
|
+
|
|
27
|
+
`marketplace` generates a repository's own local catalog — no submission, no shared listing, no PR.
|
|
28
|
+
This skill is the opposite case: putting a plugin into someone else's shared catalog, which only a
|
|
29
|
+
pull request can do. They compose rather than overlap: a plugin can have both a local catalog for
|
|
30
|
+
`git clone`-and-go installs and a listing here.
|
|
31
|
+
|
|
32
|
+
## References
|
|
33
|
+
|
|
34
|
+
- [Spec](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/spec.md)
|
|
35
|
+
- `references/vendor-requirements.md` — required fields and hook casing rules per runtime
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: publish-plugin
|
|
3
|
-
description: Use this skill whenever the user wants to publish, release, submit, or share a plugin to the universal plugin marketplace so it works across Claude Code, Cursor, Codex, and GitHub Copilot CLI. Trigger on phrases like "publish my plugin", "submit to marketplace", "release plugin", "list my plugin", "share my plugin", or any mention of getting a plugin into the universal registry. The plugin should already be packaged before using this skill.
|
|
3
|
+
description: Use this skill whenever the user wants to publish, release, submit, or share a plugin to the universal plugin marketplace so it works across Claude Code, Cursor, Codex, and GitHub Copilot CLI. Trigger on phrases like "publish my plugin", "submit to marketplace", "release plugin", "list my plugin", "share my plugin", or any mention of getting a plugin into the universal registry. Works whether the current repo is the plugin being published or the marketplace it is going into. The plugin should already be packaged before using this skill.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Publish Universal Plugin
|
|
@@ -15,8 +15,9 @@ two compose, and neither replaces the other.
|
|
|
15
15
|
|
|
16
16
|
## Overview
|
|
17
17
|
|
|
18
|
-
Publishing has
|
|
18
|
+
Publishing has four steps:
|
|
19
19
|
|
|
20
|
+
0. **Locate** — work out which repo is which, and where the plugin and the marketplace each live
|
|
20
21
|
1. **Pre-flight** — validate the plugin is ready
|
|
21
22
|
2. **Prepare entries** — build the entry for each vendor marketplace file that exists
|
|
22
23
|
3. **Submit PR** — one PR that updates all relevant marketplace files
|
|
@@ -25,9 +26,48 @@ Work through each step in order. Do not skip pre-flight even if the user says th
|
|
|
25
26
|
|
|
26
27
|
---
|
|
27
28
|
|
|
29
|
+
## Step 0: Locate the plugin and the marketplace
|
|
30
|
+
|
|
31
|
+
Two repos are in play — the plugin being published and the marketplace it is going into — and the
|
|
32
|
+
current working directory could be either one. Do not assume it is the plugin repo just because that
|
|
33
|
+
is the common case.
|
|
34
|
+
|
|
35
|
+
### 0a. Identify the marketplace repo
|
|
36
|
+
|
|
37
|
+
Default to `cyberuni/marketplace` unless the user names another one.
|
|
38
|
+
|
|
39
|
+
### 0b. Identify which repo the cwd is
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Compare the result against the marketplace repo from 0a.
|
|
46
|
+
|
|
47
|
+
- **Matches the marketplace repo** → cwd *is* the marketplace. The plugin lives elsewhere: ask the
|
|
48
|
+
user for its location if they have not given one — a local path, or a git URL to clone. Resolve a
|
|
49
|
+
git URL with `gh repo clone <url> /tmp/<plugin-name>` (or a scratch directory) so pre-flight has a
|
|
50
|
+
checkout to read; do not guess metadata from the URL alone.
|
|
51
|
+
- **Does not match, or `gh repo view` fails** (no `origin`, not a GitHub repo, detached checkout) →
|
|
52
|
+
cwd is the plugin repo. This is the default path described in Steps 1–3 below.
|
|
53
|
+
- **User states their role explicitly** ("I'm already in the marketplace repo", "publish the plugin
|
|
54
|
+
at ../foo") — trust that over the remote check; it exists to catch the unstated case, not to
|
|
55
|
+
override what the user already told you.
|
|
56
|
+
|
|
57
|
+
Name two locations going forward and keep them distinct in your own reasoning and in what you tell
|
|
58
|
+
the user:
|
|
59
|
+
|
|
60
|
+
- **plugin location** — the directory holding the plugin's root `plugin.json`, wherever it is
|
|
61
|
+
- **marketplace location** — the directory holding the marketplace's vendor catalog files
|
|
62
|
+
|
|
63
|
+
Every pre-flight check and entry field in Steps 1–2 reads from the plugin location, not the cwd.
|
|
64
|
+
Step 3 branches on whether the marketplace location is already the cwd.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
28
68
|
## Step 1: Pre-flight validation
|
|
29
69
|
|
|
30
|
-
Run these checks against the plugin
|
|
70
|
+
Run these checks against the **plugin location** from Step 0. Stop and fix any failures before continuing.
|
|
31
71
|
|
|
32
72
|
### 1a. Required metadata
|
|
33
73
|
|
|
@@ -74,7 +114,9 @@ List what passed and what failed. Do not proceed to Step 2 until all checks pass
|
|
|
74
114
|
|
|
75
115
|
### 2a. Detect which vendor marketplace files exist
|
|
76
116
|
|
|
77
|
-
|
|
117
|
+
Get the **marketplace location** locally if it is not already the cwd — see Step 3a, which this step
|
|
118
|
+
can run ahead of when you need the files present before drafting entries. Then look for vendor
|
|
119
|
+
marketplace files:
|
|
78
120
|
|
|
79
121
|
| File | Runtime |
|
|
80
122
|
|---|---|
|
|
@@ -83,17 +125,39 @@ Clone or check out the marketplace repo locally, then look for vendor marketplac
|
|
|
83
125
|
|
|
84
126
|
Only prepare entries for files that actually exist — do not create new vendor marketplace files.
|
|
85
127
|
|
|
86
|
-
### 2b.
|
|
128
|
+
### 2b. Determine the source/distribution type
|
|
129
|
+
|
|
130
|
+
Before writing any entry, decide how the plugin is actually distributed — do not default to a
|
|
131
|
+
root-clone `url`. Read the signals from the plugin's own repository, in this order:
|
|
132
|
+
|
|
133
|
+
1. **`npm`** — the CLI reports a `packagePath`, and `<packagePath>/package.json` is not
|
|
134
|
+
`"private": true`. The plugin ships as an npm package. Ask the CLI rather than reading
|
|
135
|
+
`.agents/universal-plugin.json` yourself; the path it prints is relative to the plugin root:
|
|
87
136
|
|
|
88
|
-
|
|
137
|
+
```bash
|
|
138
|
+
npx universal-plugin config get --key packagePath --format json --root <plugin-root>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`null` means no npm package is declared.
|
|
142
|
+
2. **`git-subdir`** — the plugin's `plugin.json` (or its vendor manifests) live below the
|
|
143
|
+
repository root, e.g. `packages/<name>/plugin.json`, and there is no `packagePath`/npm
|
|
144
|
+
distribution. This is the common monorepo case: the repository root holds no plugin manifest at
|
|
145
|
+
all, only a `marketplace.json` catalog or unrelated packages, so a root-clone `url` source
|
|
146
|
+
resolves nothing.
|
|
147
|
+
3. **`url` / `github`** — `plugin.json` sits at the repository root. `url` for a plain clone,
|
|
148
|
+
`github` when a sparse/shallow checkout is preferred.
|
|
149
|
+
|
|
150
|
+
Only ask the user when these signals genuinely conflict or are missing (e.g. no `plugin.json` found
|
|
151
|
+
at any candidate path). Otherwise decide from the signals and state which one you picked and why.
|
|
152
|
+
|
|
153
|
+
### 2c. Claude Code entry (`.claude-plugin/marketplace.json`)
|
|
154
|
+
|
|
155
|
+
The richer format. Use this shape, filling `source` per the type decided in 2b:
|
|
89
156
|
|
|
90
157
|
```json
|
|
91
158
|
{
|
|
92
159
|
"name": "<plugin-name>",
|
|
93
|
-
"source": {
|
|
94
|
-
"source": "url",
|
|
95
|
-
"url": "<git-clone-url>.git"
|
|
96
|
-
},
|
|
160
|
+
"source": { "source": "url", "url": "<git-clone-url>.git" },
|
|
97
161
|
"description": "<one-line description>",
|
|
98
162
|
"author": { "name": "<author>" },
|
|
99
163
|
"homepage": "<URL>",
|
|
@@ -103,7 +167,17 @@ The richer format. Use this shape:
|
|
|
103
167
|
}
|
|
104
168
|
```
|
|
105
169
|
|
|
106
|
-
|
|
170
|
+
**`<git-clone-url>`** — the plugin's own repo URL, read from the **plugin location** (`gh repo view --json url -q .url`, or the `repository` field in its `plugin.json`) — never the marketplace repo's URL.
|
|
171
|
+
|
|
172
|
+
**`source` shapes** — pick the one matching 2b's decision:
|
|
173
|
+
|
|
174
|
+
| Type | Shape | When |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `url` | `{ "source": "url", "url": "<git-clone-url>.git" }` | `plugin.json` at repo root |
|
|
177
|
+
| `github` | `{ "source": "github", "repo": "<owner>/<repo>" }` | repo root, sparse checkout |
|
|
178
|
+
| `git-subdir` | `{ "source": "git-subdir", "url": "<git-clone-url>.git", "path": "<repo-relative-path-to-plugin-dir>" }` | plugin lives in a monorepo subdirectory, e.g. `packages/<name>` |
|
|
179
|
+
| `npm` | `{ "source": "npm", "package": "<npm-package-name>" }` | plugin ships as an npm package (`packagePath` present, not private) |
|
|
180
|
+
| `file` / `directory` | see the marketplace schema | local-only entries |
|
|
107
181
|
|
|
108
182
|
**`category`** — choose closest: `research`, `skills`, `setup`, `productivity`, `plugin-authoring`.
|
|
109
183
|
|
|
@@ -116,29 +190,40 @@ The richer format. Use this shape:
|
|
|
116
190
|
|
|
117
191
|
Omit optional fields rather than leaving them empty.
|
|
118
192
|
|
|
119
|
-
###
|
|
193
|
+
### 2d. Cursor entry (`.cursor-plugin/marketplace.json`)
|
|
120
194
|
|
|
121
195
|
The simpler format — only three required fields:
|
|
122
196
|
|
|
123
197
|
```json
|
|
124
198
|
{
|
|
125
199
|
"name": "<plugin-name>",
|
|
126
|
-
"source": {
|
|
127
|
-
"source": "url",
|
|
128
|
-
"url": "<git-clone-url>.git"
|
|
129
|
-
},
|
|
200
|
+
"source": { "source": "url", "url": "<git-clone-url>.git" },
|
|
130
201
|
"description": "<one-line description>"
|
|
131
202
|
}
|
|
132
203
|
```
|
|
133
204
|
|
|
134
|
-
The `source` field supports the same
|
|
205
|
+
The `source` field supports the same shapes as Claude Code (`url`, `github`, `git-subdir`, `npm`,
|
|
206
|
+
`file`, `directory`) — pick per 2b, same as the Claude Code entry.
|
|
135
207
|
|
|
136
|
-
###
|
|
208
|
+
### 2e. Update scenario
|
|
137
209
|
|
|
138
210
|
If the plugin is already listed (this is an update), find its existing entry, update changed fields, and preserve any curator-added fields not in the standard shape. Do not overwrite `publishedAt` if present — add `"updatedAt": "<today>"` instead.
|
|
139
211
|
|
|
140
212
|
Show all prepared entries to the user and ask them to confirm before proceeding.
|
|
141
213
|
|
|
214
|
+
### 2f. Validate the catalogs
|
|
215
|
+
|
|
216
|
+
Before moving to Step 3, run the validator against the marketplace repo's working copy (the same
|
|
217
|
+
checkout the entries were just written into):
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
npx universal-plugin marketplace validate --root .
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
This is the same check `marketplace init`/`build` rely on — it catches an entry shape no runtime can
|
|
224
|
+
load (a bad `source`, a missing required key) before it reaches a PR. Fix any reported issue and
|
|
225
|
+
re-run before continuing. Do not open the PR while validation fails.
|
|
226
|
+
|
|
142
227
|
---
|
|
143
228
|
|
|
144
229
|
## Step 3: Submit PR
|
|
@@ -147,7 +232,15 @@ Use the `gh` CLI. Confirm commands that affect shared state before running.
|
|
|
147
232
|
|
|
148
233
|
### 3a. Get the marketplace repo locally
|
|
149
234
|
|
|
150
|
-
|
|
235
|
+
Skip this whole step when Step 0 already found the cwd to be the marketplace location — work
|
|
236
|
+
directly in it, but bring it up to date first:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
git fetch origin && git merge origin/main
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Otherwise, the marketplace repo is elsewhere and needs a working copy. If the user has write access
|
|
243
|
+
(org member), work directly:
|
|
151
244
|
|
|
152
245
|
```bash
|
|
153
246
|
gh repo clone cyberuni/marketplace
|
|
@@ -162,6 +255,9 @@ cd marketplace
|
|
|
162
255
|
git fetch upstream && git merge upstream/main
|
|
163
256
|
```
|
|
164
257
|
|
|
258
|
+
From here on, "the marketplace repo" means whichever of these is now the working directory —
|
|
259
|
+
including the original cwd, when Step 0 found it already was the marketplace.
|
|
260
|
+
|
|
165
261
|
### 3b. Create a branch
|
|
166
262
|
|
|
167
263
|
```bash
|
|
@@ -240,6 +336,9 @@ Return the PR URL to the user when done.
|
|
|
240
336
|
| PR rejected: missing source link | Add `homepage` or `repository` to plugin.json |
|
|
241
337
|
| Name conflict in marketplace | Check existing entries in each marketplace file first |
|
|
242
338
|
| Plugin loads but skills missing | Add `skills` array to the Claude Code marketplace entry |
|
|
339
|
+
| Entry installs nothing for a monorepo plugin | `source` was `url`/root-clone instead of `git-subdir` — redo 2b/2c with the plugin's actual repo-relative path |
|
|
340
|
+
| `marketplace validate` fails after 2f | Fix the reported shape before opening the PR — do not open it while validation fails |
|
|
341
|
+
| Entry's `repository`/`source.url` points at the marketplace repo | Step 0 role detection was skipped or overridden wrongly — re-check which repo the cwd is |
|
|
243
342
|
|
|
244
343
|
---
|
|
245
344
|
|
|
@@ -18,6 +18,18 @@
|
|
|
18
18
|
"prompt": "My plugin 'ai-commit' is already published in the universal marketplace at version 1.0.0. I've just released v1.1.0 with some new hooks. How do I update the marketplace listing?",
|
|
19
19
|
"expected_output": "Recognizes this as an update scenario, reads existing registry entry, runs pre-flight on new version, prepares updated entry preserving existing fields (especially curator_note and original publishedAt), adds updatedAt, submits an update PR.",
|
|
20
20
|
"files": []
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": 4,
|
|
24
|
+
"prompt": "My plugin 'foo' lives at packages/foo/plugin.json inside a monorepo (the repo root only has a marketplace.json catalog, no plugin.json). Publish it to cyberuni/marketplace.",
|
|
25
|
+
"expected_output": "Determines the plugin is not at the repo root and has no npm packagePath, so decides source type git-subdir rather than a root-clone url. Writes { \"source\": \"git-subdir\", \"url\": \"<clone-url>.git\", \"path\": \"packages/foo\" } in the marketplace entry. Runs marketplace validate against the marketplace working copy before opening the PR, and does not proceed if it fails.",
|
|
26
|
+
"files": []
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": 5,
|
|
30
|
+
"prompt": "I'm a maintainer of cyberuni/marketplace and I already have it checked out here. I want to add a plugin called 'db-migrate' that lives at github.com/someuser/db-migrate. Add it.",
|
|
31
|
+
"expected_output": "Detects the cwd is the marketplace repo itself, not the plugin, so it reads the plugin's metadata from the given URL (cloning it to a scratch location) instead of the cwd, then works directly in the existing checkout (fetch/merge, branch, edit, commit, push, PR) rather than cloning or forking cyberuni/marketplace again. The prepared entry's source/repository URL points at db-migrate's repo, not at cyberuni/marketplace.",
|
|
32
|
+
"files": []
|
|
21
33
|
}
|
|
22
34
|
]
|
|
23
35
|
}
|
|
@@ -22,7 +22,7 @@ Root `plugin.json` is never deleted as cleanup. It is the canonical source of tr
|
|
|
22
22
|
manifest GitHub Copilot CLI reads, so removing it takes out the source and a live target at once.
|
|
23
23
|
|
|
24
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`.
|
|
25
|
+
the next build writes it straight back. That edit routes to `init-universal-plugin`.
|
|
26
26
|
|
|
27
27
|
Deleting a published plugin's source does not unpublish it. The skill says so rather than implying
|
|
28
28
|
the removal reached consumers.
|
|
@@ -49,7 +49,7 @@ Copilot CLI's search order is `.plugin/plugin.json` → `plugin.json` → `.gith
|
|
|
49
49
|
|
|
50
50
|
Removing a vendor is a manifest edit first and a deletion second — deleting only the file leaves the
|
|
51
51
|
vendor declared, and the next build writes it straight back. That edit belongs to
|
|
52
|
-
`/universal-plugin:init`'s update route; come back here for the file.
|
|
52
|
+
`/universal-plugin:init-universal-plugin`'s update route; come back here for the file.
|
|
53
53
|
|
|
54
54
|
## Remove the whole plugin
|
|
55
55
|
|
|
@@ -82,6 +82,6 @@ this skill does.
|
|
|
82
82
|
|
|
83
83
|
| Task | Skill |
|
|
84
84
|
|------|-------|
|
|
85
|
-
| Remove a vendor from what the plugin declares | `init`, update route |
|
|
85
|
+
| Remove a vendor from what the plugin declares | `init-universal-plugin`, update route |
|
|
86
86
|
| Confirm what is stale, shadowing, or unbuilt before deleting | `doctor` |
|
|
87
87
|
| Take a published plugin out of a marketplace listing | `publish-plugin` |
|
package/skills/version/SKILL.md
CHANGED
|
@@ -90,7 +90,7 @@ Every guard resolves before the first write, so a failed run leaves the tree unt
|
|
|
90
90
|
|
|
91
91
|
| Message names | What it means | Do this |
|
|
92
92
|
|---|---|---|
|
|
93
|
-
| a missing `plugin.json` | not at a plugin root, or the plugin was never scaffolded | `cd` to the plugin root, or run `/universal-plugin:init` |
|
|
93
|
+
| a missing `plugin.json` | not at a plugin root, or the plugin was never scaffolded | `cd` to the plugin root, or run `/universal-plugin:init-universal-plugin` |
|
|
94
94
|
| no version to bump from | the manifest has never carried a `version` | pass an explicit version (`plugin version 0.1.0`) to set the first one |
|
|
95
95
|
| an unknown version or release type | the argument is neither a release type nor valid semver | use one of the values in the table above |
|
|
96
96
|
| a target that does not advance | the requested version is not greater than the current one | pick a higher version, or pass `--force` if the user genuinely wants to move backward |
|
|
@@ -108,7 +108,7 @@ Every guard resolves before the first write, so a failed run leaves the tree unt
|
|
|
108
108
|
|
|
109
109
|
| Task | Skill |
|
|
110
110
|
|------|-------|
|
|
111
|
-
| Create, adopt, or change what the plugin declares | `init` |
|
|
111
|
+
| Create, adopt, or change what the plugin declares | `init-universal-plugin` |
|
|
112
112
|
| Check whether the two authored versions agree | `doctor` |
|
|
113
113
|
| Add a changeset for the change being released | `add-changeset` |
|
|
114
114
|
| Refresh the repository's own marketplace catalogs after a bump | `marketplace` |
|