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.
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cli.mjs +181 -22
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/adopt-upx/scripts/rewrite-upx.mjs +1 -1
- package/skills/build-plugin/SKILL.md +2 -1
- package/skills/build-plugin/scripts/build.mjs +1 -1
- package/skills/doctor-universal-plugin/SKILL.md +1 -1
- package/skills/doctor-universal-plugin/scripts/doctor.mjs +1 -1
- package/skills/init-universal-plugin/references/create.md +1 -1
- package/skills/init-universal-plugin/scripts/init.mjs +1 -1
- package/skills/marketplace/scripts/add.mjs +1 -1
- package/skills/marketplace/scripts/install-docs.mjs +1 -1
- package/skills/marketplace/scripts/marketplace.mjs +1 -1
- package/skills/marketplace/scripts/validate.mjs +1 -1
- package/skills/version/SKILL.md +1 -1
- package/skills/version/scripts/version.mjs +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-plugin",
|
|
3
|
-
"version": "0.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
6977
|
-
|
|
6978
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
8059
|
+
dryRun,
|
|
7906
8060
|
verbose: opts.verbose,
|
|
7907
8061
|
clean: opts.clean
|
|
7908
8062
|
});
|
|
7909
|
-
const scripts = await compileScripts(root, { dryRun
|
|
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
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
package/skills/version/SKILL.md
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|