universal-plugin 0.14.1 → 0.15.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universal-plugin",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "Research and design toolkit for building universal AI coding agent plugins that work across Claude Code, Cursor, Codex, and GitHub Copilot CLI.",
5
5
  "author": {
6
6
  "name": "unional"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universal-plugin",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "Research and design toolkit for building universal AI coding agent plugins that work across Claude Code, Cursor, Codex, and GitHub Copilot CLI.",
5
5
  "author": {
6
6
  "name": "unional"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universal-plugin",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "Research and design toolkit for building universal AI coding agent plugins that work across Claude Code, Cursor, Codex, and GitHub Copilot CLI.",
5
5
  "author": {
6
6
  "name": "unional"
package/dist/cli.mjs CHANGED
@@ -6920,6 +6920,82 @@ function addEntry(config, key, entry) {
6920
6920
  };
6921
6921
  }
6922
6922
  //#endregion
6923
+ //#region src/compile/invisible.ts
6924
+ /** Invisible characters in compiled output (issue #190).
6925
+ *
6926
+ * A bundler prints a dependency's `'\u{FEFF}'` as the raw character, so a committed script holds
6927
+ * code points a reader cannot see — the ones skill checks reject because they can hide
6928
+ * instructions. Inside a string, template or regular-expression literal the character can be
6929
+ * written as an escape with the same meaning; anywhere else it can only be reported. */
6930
+ /** Controls, format characters (zero-width, bidirectional, BOM, soft hyphen, tags), line and paragraph
6931
+ * separators, and default-ignorable code points such as variation selectors. Tab, line feed and
6932
+ * carriage return are ordinary whitespace. */
6933
+ const INVISIBLE = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}\p{Default_Ignorable_Code_Point}]/u;
6934
+ function isInvisible(char) {
6935
+ return char !== " " && char !== "\n" && char !== "\r" && INVISIBLE.test(char);
6936
+ }
6937
+ /** `\uXXXX` per UTF-16 code unit — valid in strings, templates and regular expressions, with or
6938
+ * without the `u` flag (a surrogate pair of escapes reads as one code point under `u`). */
6939
+ function escapeChar(char) {
6940
+ let out = "";
6941
+ for (let i = 0; i < char.length; i++) out += `\\u${char.charCodeAt(i).toString(16).toUpperCase().padStart(4, "0")}`;
6942
+ return out;
6943
+ }
6944
+ /** Rewrites every invisible character inside `spans` as an escape that keeps the literal's value. */
6945
+ function escapeInvisible(code, spans) {
6946
+ const sorted = [...spans].sort((a, b) => a.start - b.start);
6947
+ let out = "";
6948
+ let copied = 0;
6949
+ for (const span of sorted) {
6950
+ if (span.start < copied) continue;
6951
+ out += code.slice(copied, span.start);
6952
+ out += escapeSpan(code.slice(span.start, span.end), span.kind);
6953
+ copied = span.end;
6954
+ }
6955
+ return out + code.slice(copied);
6956
+ }
6957
+ function escapeSpan(text, kind) {
6958
+ let out = "";
6959
+ let i = 0;
6960
+ while (i < text.length) {
6961
+ const char = String.fromCodePoint(text.codePointAt(i));
6962
+ if (char !== "\\") {
6963
+ out += isInvisible(char) ? escapeChar(char) : char;
6964
+ i += char.length;
6965
+ continue;
6966
+ }
6967
+ const next = i + 1 < text.length ? String.fromCodePoint(text.codePointAt(i + 1)) : "";
6968
+ i += 1 + next.length;
6969
+ if (!isInvisible(next)) out += `\\${next}`;
6970
+ else if (kind !== "regex" && (next === "\u2028" || next === "\u2029")) continue;
6971
+ else out += escapeChar(next);
6972
+ }
6973
+ return out;
6974
+ }
6975
+ /** Every invisible character left in `code`, 1-based line and column, for the build to warn about. */
6976
+ function findInvisible(code) {
6977
+ const found = [];
6978
+ let line = 1;
6979
+ let column = 1;
6980
+ for (const char of code) {
6981
+ if (char === "\n") {
6982
+ line++;
6983
+ column = 1;
6984
+ continue;
6985
+ }
6986
+ if (isInvisible(char)) found.push({
6987
+ line,
6988
+ column,
6989
+ codePoint: char.codePointAt(0)
6990
+ });
6991
+ column++;
6992
+ }
6993
+ return found;
6994
+ }
6995
+ function formatCodePoint(codePoint) {
6996
+ return `U+${codePoint.toString(16).toUpperCase().padStart(4, "0")}`;
6997
+ }
6998
+ //#endregion
6923
6999
  //#region src/compile/scripts.ts
6924
7000
  /** The global the header assigns the script data to. Framework-neutral: a script's own code, or the
6925
7001
  * library it inlines, reads it; universal-plugin depends on neither. */
@@ -6970,18 +7046,23 @@ function parseEntry(output, value) {
6970
7046
  source: value
6971
7047
  };
6972
7048
  if (value === null || typeof value !== "object" || Array.isArray(value)) return `scripts "${output}" must map to a source path or { source, command }`;
6973
- const { source, command } = value;
7049
+ const { source, command, alias, keepNames } = value;
6974
7050
  if (typeof source !== "string" || source.length === 0) return `scripts "${output}" has no source`;
6975
7051
  if (command !== void 0 && (typeof command !== "string" || command.trim().length === 0)) return `scripts "${output}" has a command that is not a command path`;
6976
- return command === void 0 ? {
6977
- output: normalized,
6978
- source
6979
- } : {
7052
+ if (alias !== void 0 && !isStringMap(alias)) return `scripts "${output}" has an alias that is not a map from import specifier to replacement`;
7053
+ if (keepNames !== void 0 && typeof keepNames !== "boolean") return `scripts "${output}" has a keepNames that is not true or false`;
7054
+ return {
6980
7055
  output: normalized,
6981
7056
  source,
6982
- command: command.trim().split(/\s+/).join(" ")
7057
+ ...command === void 0 ? {} : { command: command.trim().split(/\s+/).join(" ") },
7058
+ ...alias === void 0 ? {} : { alias },
7059
+ ...keepNames === void 0 ? {} : { keepNames }
6983
7060
  };
6984
7061
  }
7062
+ function isStringMap(value) {
7063
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
7064
+ return Object.entries(value).every(([k, v]) => k.length > 0 && typeof v === "string" && v.length > 0);
7065
+ }
6985
7066
  /** A `/`-separated path relative to the plugin root, without `./`; `null` when it is absolute or
6986
7067
  * climbs out of the root. */
6987
7068
  function normalizeRelative(p) {
@@ -7101,7 +7182,7 @@ async function compileScripts(root, opts, compiler) {
7101
7182
  entry: entry.output,
7102
7183
  commands
7103
7184
  };
7104
- content = composeScript(await compiler.compile(sourcePath, root), entry, data);
7185
+ content = composeScript(await compiler.compile(sourcePath, root, bundleOptions(entry)), entry, data);
7105
7186
  } catch (err) {
7106
7187
  warnings.push(`Failed to compile ${entry.source} → ${entry.output}: ${err instanceof Error ? err.message : String(err)}`);
7107
7188
  rows.push({
@@ -7114,6 +7195,8 @@ async function compileScripts(root, opts, compiler) {
7114
7195
  const bytes = Buffer.byteLength(content);
7115
7196
  const overBudget = sizeWarning(entry.output, bytes, bin);
7116
7197
  if (overBudget !== null) warnings.push(overBudget);
7198
+ const hidden = findInvisible(content);
7199
+ if (hidden.length > 0) warnings.push(invisibleWarning(entry.output, hidden));
7117
7200
  const comparison = compareScript(fs$1.existsSync(outputPath) ? fs$1.readFileSync(outputPath, "utf8") : null, content);
7118
7201
  if (opts.dryRun || comparison === "unchanged") {
7119
7202
  rows.push({
@@ -7141,6 +7224,19 @@ async function compileScripts(root, opts, compiler) {
7141
7224
  summary: summarize(rows)
7142
7225
  };
7143
7226
  }
7227
+ /** The bundle options the entry sets, and none it does not. */
7228
+ function bundleOptions({ alias, keepNames }) {
7229
+ return {
7230
+ ...alias === void 0 ? {} : { alias },
7231
+ ...keepNames === void 0 ? {} : { keepNames }
7232
+ };
7233
+ }
7234
+ /** The compiler escapes invisible characters inside literals; one left elsewhere (a comment, an
7235
+ * identifier, a tagged template) is named so the author can find it before a skill check does. */
7236
+ function invisibleWarning(output, hidden) {
7237
+ const [first] = hidden;
7238
+ return `${`${output}:${first?.line}:${first?.column} ${formatCodePoint(first?.codePoint ?? 0)}`}${hidden.length > 1 ? ` and ${hidden.length - 1} more` : ""}: invisible character in the compiled script that the build could not escape — skill checks reject it; write it as an escape in a string literal in the source`;
7239
+ }
7144
7240
  /** The `package.json` at `packagePath` (`.agents/universal-plugin.json`), which names the package and
7145
7241
  * bin a script falls back to; `null` when the plugin ships to no package. Its version is never read. */
7146
7242
  function readPackageJson(root) {
@@ -7275,34 +7371,40 @@ function onlyEntryMain() {
7275
7371
  *
7276
7372
  * An entry that inlines only its own source keeps readable output — in effect its types are
7277
7373
  * stripped. One that inlines a dependency is minified, because that is the code the size budget is
7278
- * for. rolldown is loaded only when a plugin declares a script. */
7279
- const rolldownCompiler = { async compile(sourcePath, root) {
7374
+ * for. rolldown is loaded only when a plugin declares a script.
7375
+ *
7376
+ * An entry's `alias` becomes `resolve.alias`, with a relative target resolved from the plugin root;
7377
+ * its `keepNames` is passed to both outputs, so minifying keeps the names too. */
7378
+ const rolldownCompiler = { async compile(sourcePath, root, options = {}) {
7280
7379
  const { rolldown } = await import("rolldown");
7281
7380
  const unresolved = [];
7381
+ const alias = resolveAlias(options, root);
7382
+ const output = {
7383
+ format: "esm",
7384
+ codeSplitting: false,
7385
+ keepNames: options.keepNames ?? false
7386
+ };
7282
7387
  const bundle = await rolldown({
7283
7388
  input: sourcePath,
7284
7389
  cwd: root,
7285
7390
  platform: "node",
7286
7391
  plugins: [onlyEntryMain()],
7392
+ ...alias === void 0 ? {} : { resolve: { alias } },
7287
7393
  onLog(_level, log) {
7288
7394
  if (log.code === "UNRESOLVED_IMPORT") unresolved.push(log.message);
7289
7395
  }
7290
7396
  });
7291
7397
  try {
7292
7398
  if (unresolved.length > 0) throw new Error(unresolved.join("\n"));
7293
- const [chunk, ...rest] = (await bundle.generate({
7294
- format: "esm",
7295
- codeSplitting: false
7296
- })).output;
7399
+ const [chunk, ...rest] = (await bundle.generate(output)).output;
7297
7400
  if (chunk?.type !== "chunk" || rest.some((o) => o.type === "chunk")) throw new Error("compiles to more than one file");
7298
- const external = [...chunk.imports, ...chunk.dynamicImports].filter((id) => !isBuiltin(id));
7401
+ const external = [...chunk.imports, ...chunk.dynamicImports].filter((id) => !isBuiltin(id) && id !== chunk.fileName);
7299
7402
  if (external.length > 0) throw new Error(`imports ${external.join(", ")}, which could not be inlined — install it where the plugin root resolves it`);
7300
- if (!chunk.moduleIds.some((id) => id.split(path$1.sep).includes("node_modules"))) return chunk.code;
7301
- return (await bundle.generate({
7302
- format: "esm",
7303
- codeSplitting: false,
7403
+ if (!chunk.moduleIds.some((id) => id.split(path$1.sep).includes("node_modules"))) return await escapeLiterals(chunk.code);
7404
+ return await escapeLiterals((await bundle.generate({
7405
+ ...output,
7304
7406
  minify: true
7305
- })).output[0].code;
7407
+ })).output[0].code);
7306
7408
  } catch (error) {
7307
7409
  const spec = failedExtends(error);
7308
7410
  throw spec ? new Error(explainTsconfigFailure(sourcePath, root, spec), { cause: error }) : error;
@@ -7310,6 +7412,57 @@ const rolldownCompiler = { async compile(sourcePath, root) {
7310
7412
  await bundle.close();
7311
7413
  }
7312
7414
  } };
7415
+ /** The entry's alias with each relative target (`./`, `../`) made absolute from the plugin root, so it
7416
+ * does not depend on where rolldown resolves from; `undefined` when the entry sets none. */
7417
+ function resolveAlias({ alias }, root) {
7418
+ if (alias === void 0 || Object.keys(alias).length === 0) return void 0;
7419
+ return Object.fromEntries(Object.entries(alias).map(([from, to]) => [from, /^\.\.?\//.test(to) ? path$1.resolve(root, to) : to]));
7420
+ }
7421
+ /** rolldown prints a dependency's escaped invisible characters raw (issue #190); this writes them back
7422
+ * as escapes inside every string, untagged template and regular-expression literal. */
7423
+ async function escapeLiterals(code) {
7424
+ if (![...code].some(isInvisible)) return code;
7425
+ const { parseAst } = await import("rolldown/parseAst");
7426
+ const spans = [];
7427
+ collectSpans(parseAst(code), spans);
7428
+ return escapeInvisible(code, spans);
7429
+ }
7430
+ function isNode(value) {
7431
+ return typeof value === "object" && value !== null && typeof value.type === "string";
7432
+ }
7433
+ function collectSpans(node, spans) {
7434
+ if (node.type === "Literal") {
7435
+ if (typeof node["value"] === "string") spans.push({
7436
+ start: node.start,
7437
+ end: node.end,
7438
+ kind: "string"
7439
+ });
7440
+ else if (node["regex"] !== void 0) spans.push({
7441
+ start: node.start,
7442
+ end: node.end,
7443
+ kind: "regex"
7444
+ });
7445
+ return;
7446
+ }
7447
+ if (node.type === "TaggedTemplateExpression") {
7448
+ visit(node["tag"], spans);
7449
+ for (const expression of node["quasi"]["expressions"]) collectSpans(expression, spans);
7450
+ return;
7451
+ }
7452
+ if (node.type === "TemplateElement") {
7453
+ spans.push({
7454
+ start: node.start,
7455
+ end: node.end,
7456
+ kind: "template"
7457
+ });
7458
+ return;
7459
+ }
7460
+ for (const [key, value] of Object.entries(node)) if (key !== "parent") visit(value, spans);
7461
+ }
7462
+ function visit(value, spans) {
7463
+ if (Array.isArray(value)) for (const item of value) visit(item, spans);
7464
+ else if (isNode(value)) collectSpans(value, spans);
7465
+ }
7313
7466
  //#endregion
7314
7467
  //#region ../../node_modules/.pnpm/@toon-format+toon@4.1.1/node_modules/@toon-format/toon/dist/index.mjs
7315
7468
  const NULL_LITERAL = "null";
@@ -7897,16 +8050,17 @@ function nextStep$1(result, cwd) {
7897
8050
  }
7898
8051
  function buildCommand() {
7899
8052
  const cmd = new Command("build").description("Generate vendor manifests from plugin.json");
7900
- cmd.option("--vendor <id>", "Build only the named vendor").option("--dry-run", "Print what would be written without writing").option("--verbose", "Print field-by-field transformation decisions").option("--clean", "Delete generated manifests before building").option("--strict-dependencies", "Fail when a refreshed catalog cannot resolve a declared dependency").option("--format <format>", "Output format: json or toon (default: toon)").addOption(new Option("--json").hideHelp()).addOption(ROOT_OPTION).addHelpText("after", "\nExample:\n $ universal-plugin plugin build --vendor claude-code\n").action(async (opts) => {
8053
+ cmd.option("--vendor <id>", "Build only the named vendor").option("--dry-run", "Print what would be written without writing").option("--check", "Write nothing; exit 1 when a compiled script is missing, stale, or on another version").option("--verbose", "Print field-by-field transformation decisions").option("--clean", "Delete generated manifests before building").option("--strict-dependencies", "Fail when a refreshed catalog cannot resolve a declared dependency").option("--format <format>", "Output format: json or toon (default: toon)").addOption(new Option("--json").hideHelp()).addOption(ROOT_OPTION).addHelpText("after", "\nExample:\n $ universal-plugin plugin build --vendor claude-code\n").action(async (opts) => {
7901
8054
  try {
7902
8055
  const root = resolveRoot(opts.root);
8056
+ const dryRun = opts.dryRun || opts.check;
7903
8057
  const result = buildPlugin(root, {
7904
8058
  vendor: opts.vendor,
7905
- dryRun: opts.dryRun,
8059
+ dryRun,
7906
8060
  verbose: opts.verbose,
7907
8061
  clean: opts.clean
7908
8062
  });
7909
- const scripts = await compileScripts(root, { dryRun: opts.dryRun }, rolldownCompiler);
8063
+ const scripts = await compileScripts(root, { dryRun }, rolldownCompiler);
7910
8064
  for (const warning of [...result.warnings, ...scripts.warnings]) process.stderr.write(`warn: ${warning}\n`);
7911
8065
  const { built, skipped, failed, canonical } = result.summary;
7912
8066
  const jsonResult = {
@@ -7941,6 +8095,11 @@ function buildCommand() {
7941
8095
  });
7942
8096
  process.stderr.write(nextStep$1(result, process.cwd()));
7943
8097
  if (failed > 0 || scripts.summary.failed > 0) process.exitCode = 1;
8098
+ if (opts.check && scripts.summary.behind > 0) {
8099
+ const behind = scripts.rows.filter((r) => r.status === "missing" || r.status === "stale" || r.status === "version").map((r) => `${r.path} (${r.status})`);
8100
+ process.stderr.write(`error: ${behind.length} compiled ${behind.length === 1 ? "script is" : "scripts are"} behind: ${behind.join(", ")} — run "universal-plugin plugin build" and commit the result (--check)\n`);
8101
+ process.exitCode = 1;
8102
+ }
7944
8103
  if (opts.strictDependencies && result.dependencyIssues.length > 0) {
7945
8104
  process.stderr.write(`error: ${result.dependencyIssues.length} declared ${result.dependencyIssues.length === 1 ? "dependency" : "dependencies"} cannot be resolved through the refreshed catalogs (--strict-dependencies)\n`);
7946
8105
  process.exitCode = 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universal-plugin",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "Universal AI agent plugin build tool",
5
5
  "keywords": [
6
6
  "agent-plugin",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "universal-plugin",
4
- "version": "0.14.1",
4
+ "version": "0.15.0",
5
5
  "description": "Research and design toolkit for building universal AI coding agent plugins that work across Claude Code, Cursor, Codex, and GitHub Copilot CLI.",
6
6
  "author": {
7
7
  "name": "unional"
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // Compiled by `universal-plugin plugin build` from src/skill-scripts/rewrite-upx.ts. Edit the source, not this file.
3
- globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.14.1","entry":"skills/adopt-upx/scripts/rewrite-upx.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
3
+ globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.15.0","entry":"skills/adopt-upx/scripts/rewrite-upx.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
4
4
  import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
5
5
  import { join, resolve } from "node:path";
6
6
  //#region src/skill-scripts/rewrite-upx.ts
@@ -47,7 +47,7 @@ node scripts/build.mjs --root <plugin-root>
47
47
 
48
48
  Resolve `scripts/build.mjs` against this skill's own directory. It runs the CLI the project installed
49
49
  (`pnpm exec universal-plugin`), else the CLI pinned to the version this skill shipped with
50
- (`npx universal-plugin@0.14.1`), so it works in a skill that was linked or copied on its own. `--root` defaults to the working directory and
50
+ (`npx universal-plugin@0.15.0`), so it works in a skill that was linked or copied on its own. `--root` defaults to the working directory and
51
51
  must name the directory that holds the canonical `plugin.json`.
52
52
 
53
53
  The command never prompts, so it is safe to run unattended.
@@ -58,6 +58,7 @@ The command never prompts, so it is safe to run unattended.
58
58
  | `--vendor <id>` | Build one vendor (`claude-code`, `cursor`, `codex`, `copilot-cli`), and refresh only its catalog |
59
59
  | `--verbose` | Print each field-level decision the derivation made |
60
60
  | `--strict-dependencies` | Fail when a refreshed catalog cannot resolve a declared dependency. Use in CI |
61
+ | `--check` | Write nothing; exit 1 when a compiled script is `missing`, `stale`, or `version`. Use in CI |
61
62
  | `--format json` | The same result as JSON, for scripts |
62
63
 
63
64
  `--clean` also exists. It deletes derived manifests before rewriting them, so reach for it only
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // Compiled by `universal-plugin plugin build` from src/skill-scripts/run-cli.ts. Edit the source, not this file.
3
- globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.14.1","entry":"skills/build-plugin/scripts/build.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
3
+ globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.15.0","entry":"skills/build-plugin/scripts/build.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
4
4
  import { spawnSync } from "node:child_process";
5
5
  //#region src/compile/scripts.ts
6
6
  /** The global the header assigns the script data to. Framework-neutral: a script's own code, or the
@@ -172,7 +172,7 @@ it does not, offer to add it — ask first, because both are the user's files:
172
172
  semantic-release bot opens.
173
173
 
174
174
  Use the locked `universal-plugin` from the repository's devDependencies when it has one; otherwise
175
- `npx universal-plugin@0.14.1`, pinned. This check matters most where the release tool picks the
175
+ `npx universal-plugin@0.15.0`, pinned. This check matters most where the release tool picks the
176
176
  number, because nothing else runs between that bump and publish.
177
177
 
178
178
  ## Unreleased content
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // Compiled by `universal-plugin plugin build` from src/skill-scripts/doctor.ts. Edit the source, not this file.
3
- globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.14.1","entry":"skills/doctor-universal-plugin/scripts/doctor.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
3
+ globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.15.0","entry":"skills/doctor-universal-plugin/scripts/doctor.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
4
4
  import { spawnSync } from "node:child_process";
5
5
  import * as fs from "node:fs";
6
6
  import * as path from "node:path";
@@ -43,7 +43,7 @@ node scripts/init.mjs --name <plugin-name> --vendor claude-code --vendor cursor
43
43
  ```
44
44
 
45
45
  Resolve `scripts/init.mjs` against this skill's directory; it runs the project's installed CLI, else
46
- the pinned `npx universal-plugin@0.14.1`. Add `--npm` when an npm package ships the plugin,
46
+ the pinned `npx universal-plugin@0.15.0`. Add `--npm` when an npm package ships the plugin,
47
47
  which also wires that `package.json`'s `files` to carry the derived manifests, copies its `version`
48
48
  into the manifest, and, in a changesets repository, wires the root `version` script to run
49
49
  `plugin version --from-package` (SKILL.md Phase 3 asks before that rewrite).
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // Compiled by `universal-plugin plugin build` from src/skill-scripts/run-cli.ts. Edit the source, not this file.
3
- globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.14.1","entry":"skills/init-universal-plugin/scripts/init.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
3
+ globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.15.0","entry":"skills/init-universal-plugin/scripts/init.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
4
4
  import { spawnSync } from "node:child_process";
5
5
  //#region src/compile/scripts.ts
6
6
  /** The global the header assigns the script data to. Framework-neutral: a script's own code, or the
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // Compiled by `universal-plugin plugin build` from src/skill-scripts/run-cli.ts. Edit the source, not this file.
3
- globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.14.1","entry":"skills/marketplace/scripts/add.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
3
+ globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.15.0","entry":"skills/marketplace/scripts/add.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
4
4
  import { spawnSync } from "node:child_process";
5
5
  //#region src/compile/scripts.ts
6
6
  /** The global the header assigns the script data to. Framework-neutral: a script's own code, or the
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // Compiled by `universal-plugin plugin build` from src/skill-scripts/install-docs.ts. Edit the source, not this file.
3
- globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.14.1","entry":"skills/marketplace/scripts/install-docs.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
3
+ globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.15.0","entry":"skills/marketplace/scripts/install-docs.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
4
4
  import { execFileSync } from "node:child_process";
5
5
  import * as fs from "node:fs";
6
6
  import * as path from "node:path";
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // Compiled by `universal-plugin plugin build` from src/skill-scripts/run-cli.ts. Edit the source, not this file.
3
- globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.14.1","entry":"skills/marketplace/scripts/marketplace.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
3
+ globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.15.0","entry":"skills/marketplace/scripts/marketplace.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
4
4
  import { spawnSync } from "node:child_process";
5
5
  //#region src/compile/scripts.ts
6
6
  /** The global the header assigns the script data to. Framework-neutral: a script's own code, or the
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // Compiled by `universal-plugin plugin build` from src/skill-scripts/run-cli.ts. Edit the source, not this file.
3
- globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.14.1","entry":"skills/marketplace/scripts/validate.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
3
+ globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.15.0","entry":"skills/marketplace/scripts/validate.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
4
4
  import { spawnSync } from "node:child_process";
5
5
  //#region src/compile/scripts.ts
6
6
  /** The global the header assigns the script data to. Framework-neutral: a script's own code, or the
@@ -74,7 +74,7 @@ node scripts/version.mjs --from-package
74
74
  ```
75
75
 
76
76
  Resolve that path against this skill's own directory; it runs the project's installed CLI
77
- (`pnpm exec universal-plugin`), else `npx universal-plugin@0.14.1` pinned to this skill's version.
77
+ (`pnpm exec universal-plugin`), else `npx universal-plugin@0.15.0` pinned to this skill's version.
78
78
  `universal-plugin plugin version` on the `PATH` is the fallback. The command never prompts, so it is safe to run unattended once the
79
79
  source is decided.
80
80
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // Compiled by `universal-plugin plugin build` from src/skill-scripts/run-cli.ts. Edit the source, not this file.
3
- globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.14.1","entry":"skills/version/scripts/version.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
3
+ globalThis.__SKILL_SCRIPT__ = {"package":"universal-plugin","bin":"universal-plugin","version":"0.15.0","entry":"skills/version/scripts/version.mjs","commands":{"marketplace add":"skills/marketplace/scripts/add.mjs","marketplace init":"skills/marketplace/scripts/marketplace.mjs","marketplace validate":"skills/marketplace/scripts/validate.mjs","plugin build":"skills/build-plugin/scripts/build.mjs","plugin init":"skills/init-universal-plugin/scripts/init.mjs","plugin version":"skills/version/scripts/version.mjs"}};
4
4
  import { spawnSync } from "node:child_process";
5
5
  //#region src/compile/scripts.ts
6
6
  /** The global the header assigns the script data to. Framework-neutral: a script's own code, or the