universal-plugin 0.7.0 → 0.9.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 (44) 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/bin/upx.mjs +12 -5
  5. package/dist/cli.mjs +5731 -243
  6. package/package.json +4 -2
  7. package/plugin.json +1 -1
  8. package/readme.md +12 -5
  9. package/schema/extension.schema.json +2 -6
  10. package/skills/adopt-upx/README.md +4 -4
  11. package/skills/adopt-upx/SKILL.md +4 -4
  12. package/skills/{doctor → doctor-universal-plugin}/README.md +4 -5
  13. package/skills/{doctor → doctor-universal-plugin}/SKILL.md +30 -10
  14. package/skills/{doctor → doctor-universal-plugin}/scripts/doctor.mjs +84 -38
  15. package/skills/{init → init-universal-plugin}/README.md +1 -1
  16. package/skills/{init → init-universal-plugin}/SKILL.md +3 -3
  17. package/skills/{init → init-universal-plugin}/references/standard.md +5 -1
  18. package/skills/{init → init-universal-plugin}/references/vendors/claude-code.md +1 -1
  19. package/skills/marketplace/SKILL.md +1 -1
  20. package/skills/migrate-plugin/SKILL.md +140 -26
  21. package/skills/migrate-plugin/evals/evals.json +6 -0
  22. package/skills/migrate-plugin/evals/trigger-queries.json +18 -0
  23. package/skills/publish-plugin/README.md +35 -0
  24. package/skills/publish-plugin/SKILL.md +118 -19
  25. package/skills/publish-plugin/evals/evals.json +12 -0
  26. package/skills/remove-plugin/README.md +1 -1
  27. package/skills/remove-plugin/SKILL.md +2 -2
  28. package/skills/version/SKILL.md +2 -2
  29. package/dist/run.mjs +0 -271
  30. /package/skills/{init → init-universal-plugin}/assets/templates/agent.md +0 -0
  31. /package/skills/{init → init-universal-plugin}/assets/templates/command.md +0 -0
  32. /package/skills/{init → init-universal-plugin}/assets/templates/hooks.json +0 -0
  33. /package/skills/{init → init-universal-plugin}/assets/templates/plugin.json +0 -0
  34. /package/skills/{init → init-universal-plugin}/assets/templates/setup-command.md +0 -0
  35. /package/skills/{init → init-universal-plugin}/assets/templates/skill.md +0 -0
  36. /package/skills/{init → init-universal-plugin}/references/adopt.md +0 -0
  37. /package/skills/{init → init-universal-plugin}/references/create.md +0 -0
  38. /package/skills/{init → init-universal-plugin}/references/detection.md +0 -0
  39. /package/skills/{init → init-universal-plugin}/references/frontmatter.md +0 -0
  40. /package/skills/{init → init-universal-plugin}/references/update.md +0 -0
  41. /package/skills/{init → init-universal-plugin}/references/vendors/codex.md +0 -0
  42. /package/skills/{init → init-universal-plugin}/references/vendors/copilot-cli.md +0 -0
  43. /package/skills/{init → init-universal-plugin}/references/vendors/cursor.md +0 -0
  44. /package/skills/{init → init-universal-plugin}/scripts/init.mjs +0 -0
package/dist/run.mjs DELETED
@@ -1,271 +0,0 @@
1
- #!/usr/bin/env node
2
- import * as fsNode from "node:fs";
3
- import * as path from "node:path";
4
- import * as semver$1 from "semver";
5
- import { spawnSync } from "node:child_process";
6
- //#region src/run/fs.ts
7
- function readInstall(dir) {
8
- try {
9
- const raw = fsNode.readFileSync(path.join(dir, "package.json"), "utf8");
10
- const pkg = JSON.parse(raw);
11
- if (typeof pkg.version !== "string") return void 0;
12
- return {
13
- dir,
14
- version: pkg.version,
15
- bin: pkg.bin
16
- };
17
- } catch {
18
- return;
19
- }
20
- }
21
- function packageDirIn(nodeModulesDir, pkg) {
22
- return path.join(nodeModulesDir, ...pkg.split("/"));
23
- }
24
- /** Every ancestor `node_modules` directory from `startDir` up to the filesystem root, nearest
25
- * first. */
26
- function ancestorNodeModulesDirs(startDir) {
27
- const dirs = [];
28
- let dir = startDir;
29
- for (;;) {
30
- dirs.push(path.join(dir, "node_modules"));
31
- const parent = path.dirname(dir);
32
- if (parent === dir) break;
33
- dir = parent;
34
- }
35
- return dirs;
36
- }
37
- /** Walks `node_modules` from `cwd` up through its ancestors, nearest first, collecting every
38
- * install of `pkg` found along the way (an install missing a readable `package.json` version is
39
- * skipped, not a match). */
40
- function findLocalInstalls(pkg, cwd) {
41
- const installs = [];
42
- for (const nodeModulesDir of ancestorNodeModulesDirs(cwd)) {
43
- const install = readInstall(packageDirIn(nodeModulesDir, pkg));
44
- if (install) installs.push(install);
45
- }
46
- return installs;
47
- }
48
- function globalRoot() {
49
- try {
50
- const out = spawnSync("npm", ["root", "-g"], { encoding: "utf8" }).stdout?.trim();
51
- return out ? out : void 0;
52
- } catch {
53
- return;
54
- }
55
- }
56
- function findGlobalInstall(pkg) {
57
- const root = globalRoot();
58
- if (!root) return void 0;
59
- return readInstall(packageDirIn(root, pkg));
60
- }
61
- function spawnBin(binPath, args) {
62
- const result = spawnSync(binPath, args, { stdio: "inherit" });
63
- if (result.error) throw result.error;
64
- return result.status ?? 1;
65
- }
66
- function spawnNpx(args) {
67
- const result = spawnSync("npx", args, { stdio: "inherit" });
68
- if (result.error) throw result.error;
69
- return result.status ?? 1;
70
- }
71
- function realRunFs() {
72
- return {
73
- findLocalInstalls: (pkg) => findLocalInstalls(pkg, process.cwd()),
74
- findGlobalInstall,
75
- spawnBin,
76
- spawnNpx
77
- };
78
- }
79
- //#endregion
80
- //#region src/run/run.ts
81
- const NPM_NAME_PATTERN = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
82
- /** A loose approximation of npm's package-name rules: lowercase, alphanumeric/`.`/`_`/`-`, an
83
- * optional `@scope/` prefix, non-empty. Good enough to reject the obviously malformed without
84
- * chasing every edge npm itself enforces. */
85
- function isValidPackageName(name) {
86
- if (!name || name.length > 214) return false;
87
- return NPM_NAME_PATTERN.test(name);
88
- }
89
- /** Splits `<pkg>@<range>` on the **last** `@` at index > 0, so a scoped name's leading `@` survives.
90
- * No `@` after index 0 — or a trailing `@` with an empty range — is a bare package (range `*`). A
91
- * package name that is empty or violates npm's naming rules is a fail-loud error. */
92
- function parseSpec(spec) {
93
- if (!spec) return {
94
- ok: false,
95
- error: "error: no package spec given"
96
- };
97
- const lastAt = spec.lastIndexOf("@");
98
- let pkg;
99
- let rangeRaw;
100
- if (lastAt <= 0) {
101
- pkg = spec;
102
- rangeRaw = "";
103
- } else {
104
- pkg = spec.slice(0, lastAt);
105
- rangeRaw = spec.slice(lastAt + 1);
106
- }
107
- if (!isValidPackageName(pkg)) return {
108
- ok: false,
109
- error: `error: unparseable package spec "${spec}"`
110
- };
111
- const bare = rangeRaw === "";
112
- return {
113
- ok: true,
114
- spec: {
115
- pkg,
116
- range: bare ? "*" : rangeRaw,
117
- bare
118
- }
119
- };
120
- }
121
- /** A valid semver range drives local-first matching; a non-empty non-semver spec (`next`,
122
- * `latest`) is a dist-tag that can't be matched against an installed `package.json` version. */
123
- function isSemverRange(range) {
124
- return semver$1.validRange(range) !== null;
125
- }
126
- /** Nearest-local → global, first install whose version satisfies `range`. `locals` must already be
127
- * nearest-first. */
128
- function selectInstall(range, locals, globalInstall) {
129
- for (const install of locals) if (semver$1.satisfies(install.version, range)) return install;
130
- if (globalInstall && semver$1.satisfies(globalInstall.version, range)) return globalInstall;
131
- }
132
- /** Resolves the executable from a `package.json` `bin` field: a string bin, an object entry keyed
133
- * by the package's unscoped name (even among several bins), or a single-entry object. A
134
- * multi-entry object with no name match, or a missing `bin` field entirely, fails loud — `upx`
135
- * never guesses which bin to run. */
136
- function resolveBinPath(pkgName, bin) {
137
- if (bin === void 0) return {
138
- ok: false,
139
- error: `error: package "${pkgName}" declares no bin field`
140
- };
141
- if (typeof bin === "string") return {
142
- ok: true,
143
- bin
144
- };
145
- const entries = Object.entries(bin);
146
- if (entries.length === 0) return {
147
- ok: false,
148
- error: `error: package "${pkgName}" declares no bin field`
149
- };
150
- if (entries.length === 1) return {
151
- ok: true,
152
- bin: entries[0][1]
153
- };
154
- const match = bin[pkgName.includes("/") ? pkgName.slice(pkgName.lastIndexOf("/") + 1) : pkgName] ?? bin[pkgName];
155
- if (match) return {
156
- ok: true,
157
- bin: match
158
- };
159
- return {
160
- ok: false,
161
- error: `error: package "${pkgName}" declares multiple bins and none matches its name — upx never guesses which to run`
162
- };
163
- }
164
- const KNOWN_LEADING_FLAGS = new Set(["--help", "-h"]);
165
- /** A flag is a token starting with `-`; `upx`'s own flags are recognized only before the first
166
- * non-flag token (the package spec) — everything from the spec onward belongs to the child. An
167
- * unknown flag before the spec fails loud. */
168
- function parseArgv(argv) {
169
- if (argv.length === 0) return {
170
- ok: false,
171
- error: "error: no package spec given"
172
- };
173
- const first = argv[0];
174
- if (first.startsWith("-")) {
175
- if (KNOWN_LEADING_FLAGS.has(first)) return {
176
- ok: true,
177
- args: {
178
- help: true,
179
- spec: void 0,
180
- childArgs: []
181
- }
182
- };
183
- return {
184
- ok: false,
185
- error: `error: unknown flag "${first}"`
186
- };
187
- }
188
- return {
189
- ok: true,
190
- args: {
191
- help: false,
192
- spec: first,
193
- childArgs: argv.slice(1)
194
- }
195
- };
196
- }
197
- function fallbackNotice(pkg, range) {
198
- return `upx: no installed ${pkg} satisfies "${range}", using npx`;
199
- }
200
- /** A dist-tag (`next`, `latest`) is not a version range, so the miss notice must not claim the
201
- * installed versions failed to "satisfy" it — it never could. Keeps the fixed
202
- * `upx: no installed <pkg>` prefix shared with {@link fallbackNotice}. */
203
- function distTagNotice(pkg, tag) {
204
- return `upx: no installed ${pkg}; "${tag}" is a dist-tag, using npx`;
205
- }
206
- const HELP_TEXT = `Usage: upx <pkg>@<range> [args…]
207
-
208
- Runs a package's CLI from a local or global install matching <range>, falling
209
- back to npx when nothing installed satisfies it.
210
-
211
- Example:
212
- $ upx cyberplace@^1.0.0 build
213
- `;
214
- /** Resolves `argv` (everything after `upx`) to a running child, per the Resolution algorithm in
215
- * the spec: parse → classify the range → search local-then-global → resolve the bin → spawn, or
216
- * fall back to npx with the spec exactly as given. */
217
- function runUpx(argv, fs) {
218
- const parsedArgv = parseArgv(argv);
219
- if (!parsedArgv.ok) return {
220
- kind: "error",
221
- message: parsedArgv.error
222
- };
223
- if (parsedArgv.args.help) return {
224
- kind: "help",
225
- text: HELP_TEXT
226
- };
227
- const specResult = parseSpec(parsedArgv.args.spec);
228
- if (!specResult.ok) return {
229
- kind: "error",
230
- message: specResult.error
231
- };
232
- const { pkg, range, bare } = specResult.spec;
233
- const childArgs = parsedArgv.args.childArgs;
234
- const semverRange = isSemverRange(range);
235
- if (semverRange) {
236
- const install = selectInstall(range, fs.findLocalInstalls(pkg), fs.findGlobalInstall(pkg));
237
- if (install) {
238
- const binResult = resolveBinPath(pkg, install.bin);
239
- if (!binResult.ok) return {
240
- kind: "error",
241
- message: binResult.error
242
- };
243
- const binPath = path.join(install.dir, binResult.bin);
244
- return {
245
- kind: "exit",
246
- code: fs.spawnBin(binPath, childArgs)
247
- };
248
- }
249
- }
250
- const npxSpec = bare ? pkg : `${pkg}@${range}`;
251
- return {
252
- kind: "exit",
253
- code: fs.spawnNpx([npxSpec, ...childArgs]),
254
- notice: semverRange ? fallbackNotice(pkg, range) : distTagNotice(pkg, range)
255
- };
256
- }
257
- //#endregion
258
- //#region src/run/cli.ts
259
- const outcome = runUpx(process.argv.slice(2), realRunFs());
260
- if (outcome.kind === "help") {
261
- process.stdout.write(outcome.text);
262
- process.exit(0);
263
- }
264
- if (outcome.kind === "error") {
265
- process.stderr.write(`${outcome.message}\n`);
266
- process.exit(1);
267
- }
268
- if (outcome.notice) process.stderr.write(`${outcome.notice}\n`);
269
- process.exit(outcome.code);
270
- //#endregion
271
- export {};