universal-plugin 0.5.0 → 0.7.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/com.github.copilot/agents/agentskills-specialist.agent.md +132 -0
- package/dist/cli.mjs +727 -43
- package/governances/plugin-design.md +14 -0
- package/package.json +4 -1
- package/plugin.json +1 -1
- package/readme.md +4 -0
- package/schema/README.md +10 -0
- package/schema/claude-code-marketplace.json +1939 -0
- package/schema/extension.schema.json +846 -0
- package/skills/doctor/README.md +3 -1
- package/skills/doctor/SKILL.md +27 -6
- package/skills/doctor/scripts/doctor.mjs +90 -0
- package/skills/init/README.md +4 -1
- package/skills/init/SKILL.md +35 -10
- package/skills/init/references/adopt.md +101 -13
- package/skills/init/references/detection.md +8 -2
- package/skills/init/references/vendors/copilot-cli.md +159 -11
- package/skills/marketplace/README.md +14 -2
- package/skills/marketplace/SKILL.md +45 -6
- package/skills/marketplace/scripts/validate.mjs +11 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# GitHub Copilot CLI
|
|
2
2
|
|
|
3
|
-
Reads the **canonical root `plugin.json` directly
|
|
4
|
-
|
|
3
|
+
Reads the **canonical root `plugin.json` directly**, so the build derives no vendor manifest for it.
|
|
4
|
+
It does derive its **components** — see [`com.github.copilot/` — the namespace directory](#comgithubcopilot--the-namespace-directory).
|
|
5
|
+
A plugin declaring none of the moved kinds is reported with status `canonical`, which is success, not
|
|
6
|
+
a skipped target.
|
|
5
7
|
|
|
6
|
-
## Why
|
|
8
|
+
## Why no manifest is derived
|
|
7
9
|
|
|
8
10
|
Copilot CLI searches four paths and takes the first match:
|
|
9
11
|
|
|
@@ -12,8 +14,9 @@ Copilot CLI searches four paths and takes the first match:
|
|
|
12
14
|
```
|
|
13
15
|
|
|
14
16
|
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,
|
|
16
|
-
|
|
17
|
+
has consumed Open Plugin Spec v1 manifests since **v1.0.74** (2026-07-23, [changelog](https://github.com/github/copilot-cli/blob/main/changelog.md):
|
|
18
|
+
"Add support for Open Plugin Spec v1 plugin manifests and mcp.json configuration"), so it already
|
|
19
|
+
serves the canonical manifest as-is.
|
|
17
20
|
|
|
18
21
|
Earlier builds wrote `.github/plugin/plugin.json`. That path loses to root by construction and was
|
|
19
22
|
never read; a leftover copy is stale and safe to delete.
|
|
@@ -32,22 +35,167 @@ directly — these fields are not delivered
|
|
|
32
35
|
Treat that warning as a decision to make, not noise: either the field belongs to a vendor that has a
|
|
33
36
|
derived manifest, or it does not ship. Do not invent a path for it.
|
|
34
37
|
|
|
38
|
+
## `category` and `tags` belong to the catalog, not the manifest
|
|
39
|
+
|
|
40
|
+
A project on the pre-0.6 layout carries these on its **root** `plugin.json`, because root *was*
|
|
41
|
+
Copilot CLI's derived output. Adoption drops them from there, and that loses nothing — but not for
|
|
42
|
+
the reason the field table suggests.
|
|
43
|
+
|
|
44
|
+
**Copilot CLI has no `plugin.json` handling for either field.** Verified against the shipped
|
|
45
|
+
`@github/copilot-linux-x64` **1.0.83** runtime (the plugin loader is Rust in
|
|
46
|
+
`prebuilds/*/runtime.node`, not the bundled JS), and confirmed by running the binary. The manifest
|
|
47
|
+
validator carries a dedicated message for `keywords` and none for `category` or `tags`; both land on
|
|
48
|
+
the unknown-field path:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
Plugin manifest "…": field "keywords" must be an array of strings (ignored)
|
|
52
|
+
Plugin manifest "…": unknown field "…" (ignored)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Unknown keys are **warn-and-ignore**: not rejected, not stripped from disk, dropped from the parsed
|
|
56
|
+
struct. A manifest carrying `category`, `tags`, and an outright bogus key installs cleanly with no
|
|
57
|
+
output about any of them. GitHub's published field table lists `category`/`tags` under the
|
|
58
|
+
`plugin.json` optional metadata fields anyway — as of 1.0.83 that part of the table does not match
|
|
59
|
+
the shipped loader.
|
|
60
|
+
|
|
61
|
+
**Where they are real is `marketplace.json`.** The catalog validator type-checks both on every
|
|
62
|
+
`plugins[]` entry, and a wrong type is a *fatal* browse failure, not a warning:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Failed to browse marketplace: Invalid marketplace.json:
|
|
66
|
+
plugins.0.category: Expected string, received number,
|
|
67
|
+
plugins.0.tags: Expected array, received string
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
With valid values, nothing renders them — `marketplace browse` prints only `name` and `description`,
|
|
71
|
+
and `plugins list --json` emits neither field, nor `keywords`. No list, search, sort, or filter in
|
|
72
|
+
the CLI touches any of them. GitHub's own marketplace (`github/copilot-plugins`, 17 curated entries)
|
|
73
|
+
sets `category` and `tags` zero times while populating `keywords` on 15 of 17.
|
|
74
|
+
|
|
75
|
+
### So where does a migrating project put them
|
|
76
|
+
|
|
77
|
+
| Was | Goes |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| `category` / `tags` on root `plugin.json` | fold into **`keywords`** — a spec field, in the closed set, and the one Copilot's manifest validator actually knows |
|
|
80
|
+
| the same values for marketplace discovery | the **catalog entry** in `marketplace.json`, where Copilot defines them |
|
|
81
|
+
|
|
82
|
+
Do not build a delivery path for these into the plugin manifest. There is nothing at the other end.
|
|
83
|
+
|
|
84
|
+
### Still open
|
|
85
|
+
|
|
86
|
+
- **Where the `unknown field (ignored)` warning surfaces.** It exists in the binary but reached no
|
|
87
|
+
output on `plugin install`, `plugin list`, or `plugins list`. Likely the interactive dashboard or
|
|
88
|
+
the session config-problems channel.
|
|
89
|
+
- **The full known-field allow-list for `plugin.json`.** The validator's field set is a Rust const
|
|
90
|
+
array in a stripped binary; only fields with dedicated messages are recoverable. The negative is
|
|
91
|
+
solid — neither `category` nor `tags` has any handling — but the positive list is not.
|
|
92
|
+
- **Server-side catalog search.** The runtime carries a remote catalog client with a `canSearch`
|
|
93
|
+
capability. Whether GitHub's hosted catalog indexes `category`/`tags` is outside what the shipped
|
|
94
|
+
code can answer, and it would be indexing `marketplace.json`, not a plugin manifest.
|
|
95
|
+
- **Extension, command, rule, and hook loading from the namespace directory** (below) was not
|
|
96
|
+
observed directly — only agents were proven by experiment. The rest rests on GitHub's GA post.
|
|
97
|
+
|
|
98
|
+
### Sources
|
|
99
|
+
|
|
100
|
+
- Shipped runtime: `@github/copilot-linux-x64` 1.0.83, `prebuilds/linux-x64/runtime.node`; validator
|
|
101
|
+
strings and live `plugin install` / `marketplace browse` / `plugins list --json` runs.
|
|
102
|
+
- Copilot CLI plugin reference (the field table this contradicts):
|
|
103
|
+
https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-plugin-reference
|
|
104
|
+
- GitHub's own catalog: https://github.com/github/copilot-plugins `.github/plugin/marketplace.json`
|
|
105
|
+
- Agent Plugins Specification v1.0.0 §8 (`extensions` is the sanctioned channel for non-spec data)
|
|
106
|
+
and the closed field set: https://agent-plugins.org/schemas/1.0.0/plugin.schema.json
|
|
107
|
+
|
|
108
|
+
## `com.github.copilot/` — the namespace directory
|
|
109
|
+
|
|
110
|
+
Declaring the canonical `$schema` puts a plugin in **spec mode**, and spec mode changes where
|
|
111
|
+
Copilot CLI looks for its *native* components. They move out of the plugin root and into a
|
|
112
|
+
reverse-domain directory that other runtimes ignore by design:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
com.github.copilot/agents/ commands/ rules/ hooks extensions/
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Spec components stay where the spec puts them: `skills/` and `mcp.json` remain at the plugin root.
|
|
119
|
+
Only Copilot-native kinds move. `extensions/` — canvas extensions, added in **1.0.79**
|
|
120
|
+
(2026-08-10) — is one subdirectory of it.
|
|
121
|
+
|
|
122
|
+
The directory pairs with the manifest key of the same name: `com.github.copilot/` carries Copilot's
|
|
123
|
+
*files*, `extensions["com.github.copilot"]` carries Copilot's *data* (§8). Same namespace, two
|
|
124
|
+
surfaces.
|
|
125
|
+
|
|
126
|
+
**This is a real delivery path, and it narrows the rule above.** "A Copilot-only field has nowhere
|
|
127
|
+
to go" still holds for **manifest fields** — the canonical schema is closed and that argument is
|
|
128
|
+
untouched. It does **not** hold for **content**: Copilot-specific agents, commands, rules, and hooks
|
|
129
|
+
have a sanctioned home, and `extensions["com.github.copilot"]` is the right place for Copilot
|
|
130
|
+
metadata.
|
|
131
|
+
|
|
132
|
+
The list above is the shape; these are the exact paths, and `lsp.json` belongs on it too:
|
|
133
|
+
|
|
134
|
+
| Component | Spec-mode path | Root still read |
|
|
135
|
+
| --- | --- | --- |
|
|
136
|
+
| agents | `com.github.copilot/agents/` | no |
|
|
137
|
+
| commands | `com.github.copilot/commands/` | no |
|
|
138
|
+
| rules | `com.github.copilot/rules/` | no |
|
|
139
|
+
| hooks | `com.github.copilot/hooks/hooks.json` | no |
|
|
140
|
+
| LSP servers | `com.github.copilot/lsp.json` | no |
|
|
141
|
+
| extensions (canvases) | `com.github.copilot/extensions/` | never had a root path |
|
|
142
|
+
| skills | `skills/` | **yes — does not move** |
|
|
143
|
+
| MCP servers | `mcp.json` | **yes — does not move** |
|
|
144
|
+
|
|
145
|
+
The namespace **replaces** the root rather than supplementing it, and an explicit component path in
|
|
146
|
+
the manifest does **not** opt back into root loading. The move landed in **1.0.80-0** and the
|
|
147
|
+
runtime's own changelog labels it breaking. Evidence and its confidence, including which kinds were
|
|
148
|
+
proven by experiment:
|
|
149
|
+
[`.research/copilot-spec-mode-namespace/`](../../../../../../.research/copilot-spec-mode-namespace/conclusion.md).
|
|
150
|
+
The published CLI plugin reference still says spec support is "additive on top of standard plugin
|
|
151
|
+
loading" and documents only the root layout — it is stale; do not build against it.
|
|
152
|
+
|
|
153
|
+
### What the build derives
|
|
154
|
+
|
|
155
|
+
`plugin build` derives the tree ([ADR-0015](../../../../.agents/spec/design/decisions/0015-copilot-spec-mode-namespace.md)),
|
|
156
|
+
so **authoring stays at the canonical locations**:
|
|
157
|
+
|
|
158
|
+
- `agents`, `commands` and `rules` are copied, resolved through the same `pathValue` contract
|
|
159
|
+
`skills` uses, defaults included.
|
|
160
|
+
- Agents are **renamed** on the way. Copilot CLI reads `agents/` as `.agent.md` files while the
|
|
161
|
+
canonical `agents/` is the Claude Code-shaped `*.md`, so a copy under the authored name would land
|
|
162
|
+
a file the runtime ignores. Commands and rules keep their authored names — the runtime documents no
|
|
163
|
+
extension for either.
|
|
164
|
+
- Hooks are **translated** into `com.github.copilot/hooks/hooks.json`, so a handler Copilot CLI
|
|
165
|
+
cannot run is dropped from that file with a warning rather than reported as ignored at runtime.
|
|
166
|
+
- A declared `lspServers` **path** is copied to `com.github.copilot/lsp.json`. An **inline** map is
|
|
167
|
+
not delivered: the file's top-level shape is undocumented, so the build warns instead of composing
|
|
168
|
+
one.
|
|
169
|
+
- `com.github.copilot/extensions/` is the inverse — authored there, never derived, and left alone by
|
|
170
|
+
`--clean`.
|
|
171
|
+
|
|
172
|
+
The vendor reports `built` at `com.github.copilot/` when it derives any of this, and keeps reporting
|
|
173
|
+
`canonical` at `plugin.json` when the plugin declares none of the moved kinds.
|
|
174
|
+
|
|
35
175
|
## Do not
|
|
36
176
|
|
|
37
177
|
- **Do not delete root `plugin.json` to "clean up" a Copilot target.** It is the source of truth and
|
|
38
178
|
the Copilot manifest at once.
|
|
39
179
|
- Do not write `.plugin/plugin.json`. It outranks root, so it would silently shadow the canonical
|
|
40
|
-
manifest with a copy nothing regenerates.
|
|
180
|
+
manifest with a copy nothing regenerates. `plugin build` names it as a pre-0.6 signal and exits 1 —
|
|
181
|
+
but only when nothing derived at all. A project whose other harnesses still build keeps the shadow
|
|
182
|
+
and gets no warning, so this stays a rule you follow rather than one the tool enforces.
|
|
183
|
+
- **Do not tell an author to move `agents/` into `com.github.copilot/`.** The root copy is the
|
|
184
|
+
canonical input every other vendor derives from; the namespace is a build output. Moving it
|
|
185
|
+
serves Copilot and strands the other three.
|
|
186
|
+
- **Do not hand-write the namespace directory.** `--clean` replaces everything the build derives
|
|
187
|
+
under it. `extensions/` is the one subtree it leaves alone, and the only one to author there.
|
|
41
188
|
|
|
42
189
|
## Hooks
|
|
43
190
|
|
|
44
191
|
Copilot CLI accepts **either casing**, and the casing selects the payload format: PascalCase gets the
|
|
45
|
-
Claude-compatible format, so the canonical file
|
|
46
|
-
|
|
47
|
-
|
|
192
|
+
Claude-compatible format, so the canonical file needs no translation. It is written to
|
|
193
|
+
`com.github.copilot/hooks/hooks.json` all the same, because that is where spec mode reads it — so a
|
|
194
|
+
handler Copilot CLI cannot run is **dropped from that derived file** with a warning, like any other
|
|
195
|
+
vendor's. See [`claude-code.md`](./claude-code.md).
|
|
48
196
|
|
|
49
197
|
## Dependencies
|
|
50
198
|
|
|
51
|
-
Copilot CLI reads no plugin dependency. Because it reads the canonical manifest directly, there is
|
|
52
|
-
derived
|
|
199
|
+
Copilot CLI reads no plugin dependency. Because it reads the canonical *manifest* directly, there is
|
|
200
|
+
no derived manifest to leave the declaration out of — it sits under `extensions`, which Copilot CLI ignores,
|
|
53
201
|
and the build reports it as ignored at runtime. See [`claude-code.md`](./claude-code.md).
|
|
@@ -6,8 +6,8 @@ then write the README section that tells users what to type.
|
|
|
6
6
|
## What it does
|
|
7
7
|
|
|
8
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,
|
|
10
|
-
the install documentation that goes with it.
|
|
9
|
+
runtime. This skill picks the targets with the user, runs the generation, validates every catalog
|
|
10
|
+
against the schema its runtime loads, and offers the install documentation that goes with it.
|
|
11
11
|
|
|
12
12
|
Nothing is published. The catalogs sit in the repository until someone adds it as a marketplace.
|
|
13
13
|
|
|
@@ -25,6 +25,16 @@ evidence ID. That constraint exists because the obvious way to write an install
|
|
|
25
25
|
one from another project's README, and two of the four commands in the README that prompted this
|
|
26
26
|
skill are not in any vendor documentation.
|
|
27
27
|
|
|
28
|
+
## The validation half
|
|
29
|
+
|
|
30
|
+
`scripts/validate.mjs` checks each catalog against the shape its runtime actually loads and names the
|
|
31
|
+
key at fault. It exists because a broken catalog fails silently here and loudly at install time, in
|
|
32
|
+
someone else's terminal. Two shapes reach a repository unnoticed, both of them what `package.json`
|
|
33
|
+
carries: `owner` as a `"Name <email>"` string, and `repository` as a `{ type, url }` object. Claude
|
|
34
|
+
Code refuses the catalog for either. Generation now reduces what it can (an npm `repository` becomes
|
|
35
|
+
its URL) and validation catches the rest, including a `./` source pointing at a directory that is not
|
|
36
|
+
there.
|
|
37
|
+
|
|
28
38
|
## The README half
|
|
29
39
|
|
|
30
40
|
`scripts/install-docs.mjs` reads the catalogs on disk and emits the section as JSON, so the
|
|
@@ -36,3 +46,5 @@ document.
|
|
|
36
46
|
|
|
37
47
|
- [Research: local marketplaces](https://github.com/cyberuni/universal-plugin/blob/main/.research/local-marketplaces/conclusion.md)
|
|
38
48
|
- [`marketplace init` spec](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/marketplace/init/README.md)
|
|
49
|
+
- [`marketplace validate` spec](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/marketplace/validate/README.md)
|
|
50
|
+
- [Official Claude Code marketplace schema](https://json.schemastore.org/claude-code-marketplace.json)
|
|
@@ -77,7 +77,43 @@ A selected artifact that differs from what would be generated stops the whole ru
|
|
|
77
77
|
command protecting a hand-edited catalog. Read the difference, then re-run with `--force` only once
|
|
78
78
|
you know what it discards.
|
|
79
79
|
|
|
80
|
-
### 4.
|
|
80
|
+
### 4. Validate every catalog
|
|
81
|
+
|
|
82
|
+
A catalog the runtime rejects is worse than no catalog: it is found, read, and refused at install
|
|
83
|
+
time, far from here. Never conclude this skill without running the check.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
node scripts/validate.mjs
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Resolve that path against this skill's own directory; `npx universal-plugin marketplace validate` is
|
|
90
|
+
the fallback. It reads each catalog and checks it against the schema its runtime loads. Exit status is
|
|
91
|
+
1 when any selected catalog is invalid, and every issue names the key and the value to write instead,
|
|
92
|
+
on stderr:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
error: catalog ".claude-plugin/marketplace.json" does not match the marketplace schema:
|
|
96
|
+
owner must be an object with a name, not string — write { "name": "Ari Vance" }
|
|
97
|
+
plugins[0].repository must be a string, not object — write "https://github.com/o/r.git"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
| Status | Means |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| `valid` | loads in that runtime |
|
|
103
|
+
| `invalid` | the runtime would refuse it; the issues say why |
|
|
104
|
+
| `missing` | no catalog at that path, which is only a failure under `--required` |
|
|
105
|
+
|
|
106
|
+
Fix an issue in the source it comes from, then regenerate: an entry's fields are derived from the
|
|
107
|
+
plugin's `plugin.json`, so an npm-style `repository` object belongs fixed there. The top-level `name`
|
|
108
|
+
and `owner` live in the catalog itself, and `owner` must be an object — `{ "name": "…" }`, never the
|
|
109
|
+
`"Name <email>"` string `package.json` uses.
|
|
110
|
+
|
|
111
|
+
The two checks the schema cannot make: `--required` for a target the user asked for, and sources on
|
|
112
|
+
disk. Every `./` source is checked for existence by `validate`; sources resolve against the directory
|
|
113
|
+
containing `.claude-plugin/`, and they do not resolve at all for a user who adds the marketplace by
|
|
114
|
+
direct URL to the JSON file.
|
|
115
|
+
|
|
116
|
+
### 5. Offer the README section
|
|
81
117
|
|
|
82
118
|
Ask before writing. A README is the user's document, and this is an edit to it, not a new file.
|
|
83
119
|
|
|
@@ -95,16 +131,12 @@ rather than leaving a placeholder in their README.
|
|
|
95
131
|
If the README already has an install section, show the difference and let the user choose. Do not
|
|
96
132
|
append a second one.
|
|
97
133
|
|
|
98
|
-
###
|
|
134
|
+
### 6. Verify
|
|
99
135
|
|
|
100
136
|
Re-run the generator and confirm every selected target reports `unchanged`. Confirm each catalog
|
|
101
137
|
path exists. State plainly that nothing was published: these files sit in the repository until a
|
|
102
138
|
user adds it as a marketplace.
|
|
103
139
|
|
|
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
140
|
A local path is the cheapest end-to-end proof:
|
|
109
141
|
|
|
110
142
|
```bash
|
|
@@ -147,6 +179,11 @@ a version the plugin does not have.
|
|
|
147
179
|
- **Do not write a Cursor install command.** Cursor has no command that adds a repository catalog;
|
|
148
180
|
a developer tests through `~/.cursor/plugins/local/<name>` and users get the plugin through a team
|
|
149
181
|
marketplace an admin imports.
|
|
182
|
+
- **Never hand-author or hand-patch a catalog.** Generate it, then validate it. A catalog written by
|
|
183
|
+
copying fields out of `package.json` carries `owner` as a string and `repository` as an object, and
|
|
184
|
+
Claude Code refuses it for either one.
|
|
185
|
+
- **Report the validation result, not just the generation result.** A run that generated four files
|
|
186
|
+
and validated none has not been verified.
|
|
150
187
|
- **Ask before editing the README**, and before `--force` replaces a catalog the user may have
|
|
151
188
|
hand-edited.
|
|
152
189
|
- This command publishes nothing and registers nothing. Say so in the report; a user who believes
|
|
@@ -166,5 +203,7 @@ a version the plugin does not have.
|
|
|
166
203
|
## References
|
|
167
204
|
|
|
168
205
|
- `references/runtimes.md` — per-runtime install commands and their sources
|
|
206
|
+
- The official Claude Code marketplace schema, which `validate` checks against:
|
|
207
|
+
<https://json.schemastore.org/claude-code-marketplace.json>
|
|
169
208
|
- [Research conclusion](https://github.com/cyberuni/universal-plugin/blob/main/.research/local-marketplaces/conclusion.md)
|
|
170
209
|
- [`marketplace init` spec](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/marketplace/init/README.md)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Runs `universal-plugin marketplace validate` from the CLI that ships beside this skill, so the
|
|
3
|
+
// check 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/validate.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', 'validate')
|
|
11
|
+
await import(join(packageRoot, 'bin', 'universal-plugin.mjs'))
|