universal-plugin 0.3.0 → 0.4.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 +8 -5
- package/.codex-plugin/plugin.json +8 -4
- package/.cursor-plugin/plugin.json +8 -4
- package/LICENSE +21 -0
- package/dist/cli.mjs +77 -138
- package/package.json +3 -1
- package/plugin.json +8 -2
- package/readme.md +73 -42
- package/skills/doctor/README.md +42 -0
- package/skills/doctor/SKILL.md +123 -0
- package/skills/doctor/scripts/doctor.mjs +196 -0
- package/skills/init/README.md +57 -0
- package/skills/init/SKILL.md +200 -0
- package/skills/{plugin → init}/references/adopt.md +8 -4
- package/skills/init/references/create.md +111 -0
- package/skills/init/references/detection.md +62 -0
- package/skills/init/references/frontmatter.md +65 -0
- package/skills/init/references/standard.md +92 -0
- package/skills/init/references/update.md +31 -0
- package/skills/init/references/vendors/claude-code.md +44 -0
- package/skills/init/references/vendors/codex.md +48 -0
- package/skills/init/references/vendors/copilot-cli.md +45 -0
- package/skills/init/references/vendors/cursor.md +45 -0
- package/skills/init/scripts/init.mjs +11 -0
- package/skills/remove-plugin/README.md +38 -0
- package/skills/remove-plugin/SKILL.md +87 -0
- package/skills/version/README.md +36 -0
- package/skills/{plugin/references/version.md → version/SKILL.md} +26 -5
- package/skills/version/scripts/version.mjs +11 -0
- package/skills/plugin/README.md +0 -37
- package/skills/plugin/SKILL.md +0 -105
- package/skills/plugin/references/create.md +0 -163
- package/skills/plugin/references/delete.md +0 -23
- package/skills/plugin/references/inspect.md +0 -21
- package/skills/plugin/references/update.md +0 -26
- /package/skills/{plugin → init}/assets/templates/agent.md +0 -0
- /package/skills/{plugin → init}/assets/templates/command.md +0 -0
- /package/skills/{plugin → init}/assets/templates/hooks.json +0 -0
- /package/skills/{plugin → init}/assets/templates/plugin.json +0 -0
- /package/skills/{plugin → init}/assets/templates/setup-command.md +0 -0
- /package/skills/{plugin → init}/assets/templates/skill.md +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-plugin",
|
|
3
|
+
"version": "0.4.0",
|
|
3
4
|
"description": "Research and design toolkit for building universal AI coding agent plugins that work across Claude Code, Cursor, Codex, and GitHub Copilot CLI.",
|
|
4
5
|
"author": {
|
|
5
6
|
"name": "unional"
|
|
@@ -7,9 +8,11 @@
|
|
|
7
8
|
"homepage": "https://github.com/cyberuni/universal-plugin",
|
|
8
9
|
"repository": "https://github.com/cyberuni/universal-plugin",
|
|
9
10
|
"license": "MIT",
|
|
10
|
-
"keywords": [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
"keywords": [
|
|
12
|
+
"universal",
|
|
13
|
+
"plugin",
|
|
14
|
+
"agent",
|
|
15
|
+
"cross-vendor"
|
|
16
|
+
],
|
|
17
|
+
"skills": "./skills/"
|
|
15
18
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-plugin",
|
|
3
|
+
"version": "0.4.0",
|
|
3
4
|
"description": "Research and design toolkit for building universal AI coding agent plugins that work across Claude Code, Cursor, Codex, and GitHub Copilot CLI.",
|
|
4
5
|
"author": {
|
|
5
6
|
"name": "unional"
|
|
@@ -7,8 +8,11 @@
|
|
|
7
8
|
"homepage": "https://github.com/cyberuni/universal-plugin",
|
|
8
9
|
"repository": "https://github.com/cyberuni/universal-plugin",
|
|
9
10
|
"license": "MIT",
|
|
10
|
-
"keywords": [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
"keywords": [
|
|
12
|
+
"universal",
|
|
13
|
+
"plugin",
|
|
14
|
+
"agent",
|
|
15
|
+
"cross-vendor"
|
|
16
|
+
],
|
|
17
|
+
"skills": "./skills/"
|
|
14
18
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-plugin",
|
|
3
|
+
"version": "0.4.0",
|
|
3
4
|
"description": "Research and design toolkit for building universal AI coding agent plugins that work across Claude Code, Cursor, Codex, and GitHub Copilot CLI.",
|
|
4
5
|
"author": {
|
|
5
6
|
"name": "unional"
|
|
@@ -7,8 +8,11 @@
|
|
|
7
8
|
"homepage": "https://github.com/cyberuni/universal-plugin",
|
|
8
9
|
"repository": "https://github.com/cyberuni/universal-plugin",
|
|
9
10
|
"license": "MIT",
|
|
10
|
-
"keywords": [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
"keywords": [
|
|
12
|
+
"universal",
|
|
13
|
+
"plugin",
|
|
14
|
+
"agent",
|
|
15
|
+
"cross-vendor"
|
|
16
|
+
],
|
|
17
|
+
"skills": "./skills/"
|
|
14
18
|
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 unional
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/cli.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { Command, Option } from "commander";
|
|
|
3
3
|
import * as fsNode from "node:fs";
|
|
4
4
|
import * as os from "node:os";
|
|
5
5
|
import * as path from "node:path";
|
|
6
|
+
import { encode } from "@toon-format/toon";
|
|
6
7
|
import { fileURLToPath } from "node:url";
|
|
7
8
|
import * as childProcess from "node:child_process";
|
|
8
9
|
import * as semver from "semver";
|
|
@@ -158,21 +159,6 @@ function cleanCommand() {
|
|
|
158
159
|
function printJson(data) {
|
|
159
160
|
console.log(JSON.stringify(data, null, 2));
|
|
160
161
|
}
|
|
161
|
-
function printFields(fields) {
|
|
162
|
-
const entries = Object.entries(fields).filter(([, v]) => v != null);
|
|
163
|
-
const width = Math.max(...entries.map(([k]) => k.length));
|
|
164
|
-
for (const [key, val] of entries) console.log(`${key.padEnd(width)} ${val}`);
|
|
165
|
-
}
|
|
166
|
-
function printTable(items, cols) {
|
|
167
|
-
if (items.length === 0) {
|
|
168
|
-
console.log("(none)");
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
const widths = cols.map((c) => Math.max(c.label.length, ...items.map((i) => c.get(i).length)));
|
|
172
|
-
console.log(cols.map((c, i) => c.label.toUpperCase().padEnd(widths[i])).join(" "));
|
|
173
|
-
console.log(widths.map((w) => "-".repeat(w)).join(" "));
|
|
174
|
-
for (const item of items) console.log(cols.map((c, i) => c.get(item).padEnd(widths[i])).join(" "));
|
|
175
|
-
}
|
|
176
162
|
function getFormat() {
|
|
177
163
|
const argv = process.argv;
|
|
178
164
|
const fmtIdx = argv.indexOf("--format");
|
|
@@ -182,9 +168,20 @@ function getFormat() {
|
|
|
182
168
|
function isJsonOutput() {
|
|
183
169
|
return getFormat() === "json";
|
|
184
170
|
}
|
|
185
|
-
|
|
171
|
+
/** Print the machine result on stdout: JSON when asked for, TOON otherwise (AXI #1).
|
|
172
|
+
*
|
|
173
|
+
* `view` is the default format's payload, carrying the minimal row schema and the
|
|
174
|
+
* pre-computed aggregates a reader needs (AXI #2, #4). It defaults to `data`, which
|
|
175
|
+
* suits a command whose full result is already minimal. */
|
|
176
|
+
function output(data, view) {
|
|
177
|
+
if (isJsonOutput()) printJson(data);
|
|
178
|
+
else console.log(encode(view ?? data));
|
|
179
|
+
}
|
|
180
|
+
/** Print a result whose default rendering is a document body rather than a record.
|
|
181
|
+
* `--format json` still emits the structured payload. */
|
|
182
|
+
function outputText(data, text) {
|
|
186
183
|
if (isJsonOutput()) printJson(data);
|
|
187
|
-
else
|
|
184
|
+
else text();
|
|
188
185
|
}
|
|
189
186
|
//#endregion
|
|
190
187
|
//#region src/json.ts
|
|
@@ -334,11 +331,8 @@ function writeSkillArtifacts(root, vendor, skills, opts, written, warnings) {
|
|
|
334
331
|
writeClaudeSkill(skill, opts, written);
|
|
335
332
|
continue;
|
|
336
333
|
}
|
|
334
|
+
if (vendor === "cursor") continue;
|
|
337
335
|
if (skill.invocationPolicy === "model") continue;
|
|
338
|
-
if (vendor === "cursor") {
|
|
339
|
-
writeArtifact(path.join(root, ".cursor", "commands", `${skill.name}.md`), skill.body, opts, written);
|
|
340
|
-
continue;
|
|
341
|
-
}
|
|
342
336
|
if (vendor === "codex") try {
|
|
343
337
|
writeArtifact(path.join(os.homedir(), ".codex", "prompts", `${skill.name}.md`), skill.body, opts, written);
|
|
344
338
|
} catch (err) {
|
|
@@ -422,30 +416,22 @@ function buildCommand() {
|
|
|
422
416
|
});
|
|
423
417
|
for (const warning of result.warnings) process.stderr.write(`warn: ${warning}\n`);
|
|
424
418
|
const { built, skipped, failed, canonical } = result.summary;
|
|
425
|
-
|
|
419
|
+
const jsonResult = {
|
|
426
420
|
built: result.rows.filter((r) => r.status === "built"),
|
|
427
421
|
skipped: result.rows.filter((r) => r.status === "skipped"),
|
|
428
422
|
failed: result.rows.filter((r) => r.status === "failed"),
|
|
429
423
|
canonical: result.rows.filter((r) => r.status === "canonical"),
|
|
430
424
|
summary: result.summary,
|
|
431
425
|
warnings: result.warnings
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
},
|
|
442
|
-
{
|
|
443
|
-
label: "status",
|
|
444
|
-
get: (r) => r.status
|
|
445
|
-
}
|
|
446
|
-
]);
|
|
447
|
-
const counts = `built ${built}, skipped ${skipped}, failed ${failed}`;
|
|
448
|
-
console.log(canonical > 0 ? `${counts}, served by plugin.json ${canonical}` : counts);
|
|
426
|
+
};
|
|
427
|
+
const counts = `built ${built}, skipped ${skipped}, failed ${failed}`;
|
|
428
|
+
output(jsonResult, {
|
|
429
|
+
vendors: result.rows.map((r) => ({
|
|
430
|
+
vendor: r.vendor,
|
|
431
|
+
path: r.path,
|
|
432
|
+
status: r.status
|
|
433
|
+
})),
|
|
434
|
+
summary: canonical > 0 ? `${counts}, served by plugin.json ${canonical}` : counts
|
|
449
435
|
});
|
|
450
436
|
process.stderr.write(NEXT_STEP$2);
|
|
451
437
|
if (failed > 0) process.exitCode = 1;
|
|
@@ -733,29 +719,17 @@ function bundleCommand() {
|
|
|
733
719
|
const pinned = result.pins.filter((p) => p.status === "pinned").length;
|
|
734
720
|
const unchanged = result.pins.filter((p) => p.status === "unchanged").length;
|
|
735
721
|
const skipped = result.pins.filter((p) => p.status === "skipped").length;
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
},
|
|
748
|
-
{
|
|
749
|
-
label: "resolved",
|
|
750
|
-
get: (r) => r.resolved
|
|
751
|
-
},
|
|
752
|
-
{
|
|
753
|
-
label: "status",
|
|
754
|
-
get: (r) => r.status
|
|
755
|
-
}
|
|
756
|
-
]);
|
|
757
|
-
if (truncated) console.log(`… +${result.pins.length - TRUNCATE_THRESHOLD} more — rerun with --full`);
|
|
758
|
-
console.log(`pinned ${pinned}, unchanged ${unchanged}, skipped ${skipped}`);
|
|
722
|
+
const truncated = !opts.full && result.pins.length > TRUNCATE_THRESHOLD;
|
|
723
|
+
const shown = truncated ? result.pins.slice(0, TRUNCATE_THRESHOLD) : result.pins;
|
|
724
|
+
output({ pins: result.pins }, {
|
|
725
|
+
pins: shown.map((r) => ({
|
|
726
|
+
package: r.package,
|
|
727
|
+
current: r.current,
|
|
728
|
+
resolved: r.resolved,
|
|
729
|
+
status: r.status
|
|
730
|
+
})),
|
|
731
|
+
...truncated ? { truncated: `… +${result.pins.length - TRUNCATE_THRESHOLD} more — rerun with --full` } : {},
|
|
732
|
+
summary: `pinned ${pinned}, unchanged ${unchanged}, skipped ${skipped}`
|
|
759
733
|
});
|
|
760
734
|
if (result.pins.length === 0) process.stderr.write("nothing to bundle\n");
|
|
761
735
|
process.stderr.write(NEXT_STEP$1);
|
|
@@ -856,22 +830,11 @@ function addCommand(fs) {
|
|
|
856
830
|
key: opts.key,
|
|
857
831
|
name: result.name,
|
|
858
832
|
action: result.action
|
|
859
|
-
},
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
},
|
|
865
|
-
{
|
|
866
|
-
label: "name",
|
|
867
|
-
get: (r) => r.name
|
|
868
|
-
},
|
|
869
|
-
{
|
|
870
|
-
label: "action",
|
|
871
|
-
get: (r) => r.action
|
|
872
|
-
}
|
|
873
|
-
]);
|
|
874
|
-
console.log(`${opts.key}: ${count} entries`);
|
|
833
|
+
}, {
|
|
834
|
+
key: opts.key,
|
|
835
|
+
name: result.name,
|
|
836
|
+
action: result.action,
|
|
837
|
+
summary: `${opts.key}: ${count} entries`
|
|
875
838
|
});
|
|
876
839
|
process.stderr.write(`→ universal-plugin config get --key ${opts.key}\n`);
|
|
877
840
|
} catch (err) {
|
|
@@ -886,12 +849,9 @@ function getCommand(fs) {
|
|
|
886
849
|
assertNotReserved(opts.key);
|
|
887
850
|
const root = resolveRoot(opts.root);
|
|
888
851
|
const entries = getEntries(fs.read(root), opts.key);
|
|
889
|
-
output(entries,
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
get: (e) => String(e.name ?? "")
|
|
893
|
-
}]);
|
|
894
|
-
console.log(`${opts.key}: ${entries.length} entries`);
|
|
852
|
+
output(entries, {
|
|
853
|
+
names: entries.map((e) => String(e.name ?? "")),
|
|
854
|
+
summary: `${opts.key}: ${entries.length} entries`
|
|
895
855
|
});
|
|
896
856
|
process.stderr.write(`→ universal-plugin config add --key ${opts.key} --entry <json>\n`);
|
|
897
857
|
} catch (err) {
|
|
@@ -1029,20 +989,18 @@ function governanceCommand() {
|
|
|
1029
989
|
process.stderr.write(`Governance "${name}" not found\n`);
|
|
1030
990
|
process.exit(1);
|
|
1031
991
|
}
|
|
1032
|
-
|
|
992
|
+
outputText(result, () => {
|
|
1033
993
|
process.stdout.write(result.content);
|
|
1034
994
|
});
|
|
1035
995
|
});
|
|
1036
996
|
cmd.command("list").description("List available governances").option("--format <format>", "Output format: json or text (default: text)").addOption(ROOT_OPTION).addOption(new Option("--json").hideHelp()).action((opts) => {
|
|
1037
997
|
const entries = listGovernances(resolveRoot(opts.root), realGovernanceFs);
|
|
1038
|
-
output(entries,
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
},
|
|
1043
|
-
|
|
1044
|
-
get: (e) => e.scope
|
|
1045
|
-
}]);
|
|
998
|
+
output(entries, {
|
|
999
|
+
governances: entries.map((e) => ({
|
|
1000
|
+
name: e.name,
|
|
1001
|
+
scope: e.scope
|
|
1002
|
+
})),
|
|
1003
|
+
summary: `${entries.length} governances across ${new Set(entries.map((e) => e.scope)).size} scopes`
|
|
1046
1004
|
});
|
|
1047
1005
|
});
|
|
1048
1006
|
return cmd;
|
|
@@ -1175,15 +1133,12 @@ function initCommand$1(deps = { fs: realInitFs }) {
|
|
|
1175
1133
|
created: plan.rows.filter((r) => r.action === "created").map((r) => r.path),
|
|
1176
1134
|
updated: plan.rows.filter((r) => r.action === "updated").map((r) => r.path),
|
|
1177
1135
|
summary: plan.summary
|
|
1178
|
-
},
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
},
|
|
1183
|
-
|
|
1184
|
-
get: (r) => r.action
|
|
1185
|
-
}]);
|
|
1186
|
-
console.log(`created ${plan.summary.created}, updated ${plan.summary.updated}`);
|
|
1136
|
+
}, {
|
|
1137
|
+
files: plan.rows.map((r) => ({
|
|
1138
|
+
path: r.path,
|
|
1139
|
+
action: r.action
|
|
1140
|
+
})),
|
|
1141
|
+
summary: `created ${plan.summary.created}, updated ${plan.summary.updated}`
|
|
1187
1142
|
});
|
|
1188
1143
|
process.stderr.write(NEXT_STEP);
|
|
1189
1144
|
} catch (err) {
|
|
@@ -1499,24 +1454,15 @@ function initCommand() {
|
|
|
1499
1454
|
dryRun: opts.dryRun,
|
|
1500
1455
|
force: opts.force
|
|
1501
1456
|
});
|
|
1502
|
-
output(results,
|
|
1503
|
-
{
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
{
|
|
1512
|
-
label: "paths",
|
|
1513
|
-
get: (row) => row.paths.join(", ") || "-"
|
|
1514
|
-
},
|
|
1515
|
-
{
|
|
1516
|
-
label: "plugins",
|
|
1517
|
-
get: (row) => row.plugins.join(", ") || "-"
|
|
1518
|
-
}
|
|
1519
|
-
]));
|
|
1457
|
+
output(results, {
|
|
1458
|
+
targets: results.map((row) => ({
|
|
1459
|
+
target: row.target,
|
|
1460
|
+
status: row.status,
|
|
1461
|
+
paths: row.paths.join(" ") || "-",
|
|
1462
|
+
plugins: row.plugins.join(" ") || "-"
|
|
1463
|
+
})),
|
|
1464
|
+
summary: `${results.length} targets`
|
|
1465
|
+
});
|
|
1520
1466
|
process.stderr.write("Generated repository metadata only; no marketplace publication, registration, installation, authentication, or provisioning occurred.\n");
|
|
1521
1467
|
} catch (err) {
|
|
1522
1468
|
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
@@ -1852,11 +1798,9 @@ function publishCommand() {
|
|
|
1852
1798
|
cmd.command("sync-version").description("Sync version from packagePath/package.json into plugin.json").addOption(ROOT_OPTION).action((opts) => {
|
|
1853
1799
|
try {
|
|
1854
1800
|
const result = syncVersion(resolveRoot(opts.root), realSyncVersionFs);
|
|
1855
|
-
output(result,
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
manifest: result.manifestPath
|
|
1859
|
-
});
|
|
1801
|
+
output(result, {
|
|
1802
|
+
version: result.version,
|
|
1803
|
+
manifest: result.manifestPath
|
|
1860
1804
|
});
|
|
1861
1805
|
} catch (err) {
|
|
1862
1806
|
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
@@ -2132,19 +2076,14 @@ function versionCommand(deps = { fs: realVersionFs }) {
|
|
|
2132
2076
|
derived: rows.filter((r) => r.action === "derived").length
|
|
2133
2077
|
}
|
|
2134
2078
|
};
|
|
2135
|
-
output(jsonResult,
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
}, {
|
|
2144
|
-
label: "action",
|
|
2145
|
-
get: (r) => r.action
|
|
2146
|
-
}]);
|
|
2147
|
-
console.log(dryRun ? `planned ${plan.summary.updated}, updated 0 (dry run)` : `updated ${plan.summary.updated}, derived ${jsonResult.summary.derived}`);
|
|
2079
|
+
output(jsonResult, {
|
|
2080
|
+
from: plan.from ?? "(none)",
|
|
2081
|
+
to: plan.to,
|
|
2082
|
+
files: rows.map((r) => ({
|
|
2083
|
+
path: r.path,
|
|
2084
|
+
action: r.action
|
|
2085
|
+
})),
|
|
2086
|
+
summary: dryRun ? `planned ${plan.summary.updated}, updated 0 (dry run)` : `updated ${plan.summary.updated}, derived ${jsonResult.summary.derived}`
|
|
2148
2087
|
});
|
|
2149
2088
|
process.stderr.write(nextStep(dryRun, opts.build !== false));
|
|
2150
2089
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Universal AI agent plugin build tool",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-plugin",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"./package.json": "./package.json"
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
|
+
"LICENSE",
|
|
28
29
|
"bin",
|
|
29
30
|
"dist",
|
|
30
31
|
"governances",
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
"agents"
|
|
37
38
|
],
|
|
38
39
|
"dependencies": {
|
|
40
|
+
"@toon-format/toon": "^4.1.1",
|
|
39
41
|
"commander": "^14.0.3",
|
|
40
42
|
"semver": "^7.8.1"
|
|
41
43
|
},
|
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.4.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"
|
|
@@ -12,7 +12,13 @@
|
|
|
12
12
|
"keywords": ["universal", "plugin", "agent", "cross-vendor"],
|
|
13
13
|
"extensions": {
|
|
14
14
|
"org.cyberuni.universal-plugin": {
|
|
15
|
-
"skills": "./skills/"
|
|
15
|
+
"skills": "./skills/",
|
|
16
|
+
"harnesses": {
|
|
17
|
+
"claude-code": {},
|
|
18
|
+
"cursor": {},
|
|
19
|
+
"codex": {},
|
|
20
|
+
"copilot-cli": {}
|
|
21
|
+
}
|
|
16
22
|
}
|
|
17
23
|
}
|
|
18
24
|
}
|
package/readme.md
CHANGED
|
@@ -1,87 +1,95 @@
|
|
|
1
1
|
# universal-plugin
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/universal-plugin)
|
|
4
|
-
[](https://www.npmjs.com/package/universal-plugin)
|
|
5
|
+
[](https://github.com/cyberuni/universal-plugin/blob/main/LICENSE)
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
## Specification
|
|
9
|
-
|
|
10
|
-
This package follows the [Agent Plugins Specification](https://github.com/agentplugins/agent-plugins-spec).
|
|
11
|
-
Consult that repository's versioned specification and releases before changing manifest
|
|
12
|
-
or component compatibility behavior; it is the canonical reference for the current standard.
|
|
7
|
+
Write one canonical plugin manifest (root `plugin.json`). Generate the vendor manifests for Claude
|
|
8
|
+
Code, Cursor, Codex, and GitHub Copilot CLI.
|
|
13
9
|
|
|
14
10
|
## Usage
|
|
15
11
|
|
|
16
|
-
No install required
|
|
12
|
+
No install required:
|
|
17
13
|
|
|
18
14
|
```sh
|
|
19
15
|
npx universal-plugin <command>
|
|
20
16
|
```
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
Pin an exact version for reproducible builds:
|
|
23
19
|
|
|
24
20
|
```sh
|
|
25
|
-
npx universal-plugin@0.
|
|
21
|
+
npx universal-plugin@0.3.1 <command>
|
|
26
22
|
```
|
|
27
23
|
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
`npm i -g universal-plugin` also puts a second bin, `upx`, on PATH. `upx <pkg>@^<major>` finds an
|
|
31
|
-
already-installed version satisfying the range (local `node_modules` first, then global) and runs
|
|
32
|
-
it directly — about 10× faster than `npx`'s ~1s per-call resolve+spawn cost — falling back to
|
|
33
|
-
`npx` when nothing installed matches:
|
|
34
|
-
|
|
35
|
-
```sh
|
|
36
|
-
npm i -g universal-plugin
|
|
37
|
-
upx cyber-skills@^2 audit validate
|
|
38
|
-
```
|
|
24
|
+
## Specification
|
|
39
25
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
26
|
+
This package follows the [Agent Plugins Specification](https://github.com/agentplugins/agent-plugins-spec).
|
|
27
|
+
That repository is the canonical reference. Consult its versioned specification and releases before
|
|
28
|
+
you change manifest or component compatibility behavior.
|
|
43
29
|
|
|
44
30
|
## Commands
|
|
45
31
|
|
|
46
|
-
### plugin
|
|
32
|
+
### plugin
|
|
33
|
+
|
|
34
|
+
Author the canonical manifest and derive everything from it.
|
|
47
35
|
|
|
48
36
|
```sh
|
|
49
|
-
|
|
50
|
-
npx universal-plugin plugin
|
|
37
|
+
npx universal-plugin plugin init # scaffold plugin.json
|
|
38
|
+
npx universal-plugin plugin init --npm # also wire an npm package to ship it
|
|
39
|
+
npx universal-plugin plugin build # generate vendor manifests
|
|
40
|
+
npx universal-plugin plugin version <bump> # move the version across every file carrying one
|
|
41
|
+
npx universal-plugin plugin bundle # pin skill npx references to workspace versions
|
|
51
42
|
```
|
|
52
43
|
|
|
53
|
-
`
|
|
44
|
+
`build` writes `.claude-plugin/plugin.json`, `.cursor-plugin/plugin.json`, and
|
|
45
|
+
`.codex-plugin/plugin.json`. Copilot CLI reads the canonical root `plugin.json` directly, so no
|
|
46
|
+
fourth file is derived.
|
|
47
|
+
|
|
48
|
+
Each command writes JSON with `JSON.stringify`. Your repository decides how JSON looks, so run your
|
|
49
|
+
formatter after any command that writes a manifest.
|
|
54
50
|
|
|
55
|
-
### sync
|
|
51
|
+
### sync
|
|
52
|
+
|
|
53
|
+
Move an installed plugin from the runtime that installed it to the others.
|
|
56
54
|
|
|
57
55
|
```sh
|
|
58
|
-
# Detect cross-vendor sync actions from a vendor's manifest
|
|
59
56
|
npx universal-plugin prepare <vendor-id> # e.g. claude-code
|
|
60
57
|
npx universal-plugin prepare <vendor-id> --scope project --root <path>
|
|
61
|
-
npx universal-plugin prepare <vendor-id> --dry-run # print action count without writing state
|
|
62
|
-
|
|
63
|
-
# Apply a pending sync action
|
|
58
|
+
npx universal-plugin prepare <vendor-id> --dry-run # print the action count without writing state
|
|
64
59
|
npx universal-plugin sync apply <action-id>
|
|
65
60
|
```
|
|
66
61
|
|
|
67
62
|
### publish
|
|
68
63
|
|
|
69
64
|
```sh
|
|
70
|
-
|
|
71
|
-
npx universal-plugin publish sync-version
|
|
65
|
+
npx universal-plugin publish sync-version # copy packagePath/package.json version into plugin.json
|
|
72
66
|
```
|
|
73
67
|
|
|
68
|
+
This writes the canonical `plugin.json` only. Run `plugin build` afterwards, or the vendor manifests
|
|
69
|
+
keep their previous version.
|
|
70
|
+
|
|
74
71
|
### marketplace
|
|
75
72
|
|
|
76
73
|
```sh
|
|
77
|
-
# Generate a local Codex catalog from canonical plugin manifests.
|
|
78
74
|
npx universal-plugin marketplace init --codex --root .
|
|
79
75
|
```
|
|
80
76
|
|
|
81
|
-
Codex caches a local plugin install by its marketplace entry version. After
|
|
82
|
-
files
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
Codex caches a local plugin install by its marketplace entry version. After you change packaged
|
|
78
|
+
plugin files: update the canonical `plugin.json` version, regenerate the catalog (add `--force` to
|
|
79
|
+
replace an existing one), reinstall the plugin, then start a new Codex session. The installed copy
|
|
80
|
+
and its marketplace entry then carry the same version.
|
|
81
|
+
|
|
82
|
+
### config
|
|
83
|
+
|
|
84
|
+
Read and write plugin-registered config in `.agents/universal-plugin.json`.
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
npx universal-plugin config get --key sdd-plugins
|
|
88
|
+
npx universal-plugin config add --key sdd-plugins --entry '{"name":"aces","handles":["agent evaluation"]}'
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`add` appends the entry, or replaces the existing entry with the same `name`. Both commands print
|
|
92
|
+
TOON by default; pass `--format json` for JSON.
|
|
85
93
|
|
|
86
94
|
### governance
|
|
87
95
|
|
|
@@ -99,6 +107,29 @@ npx universal-plugin clean # remove the asset store
|
|
|
99
107
|
npx universal-plugin self-update <version> # update the version pin in hook files
|
|
100
108
|
```
|
|
101
109
|
|
|
110
|
+
## upx, the fast package runner
|
|
111
|
+
|
|
112
|
+
`npm i -g universal-plugin` puts a second bin, `upx`, on PATH.
|
|
113
|
+
|
|
114
|
+
`upx <pkg>@^<major>` looks for an already-installed version satisfying the range, checking local
|
|
115
|
+
`node_modules` first and then global. It spawns that binary directly, skipping the resolve step that
|
|
116
|
+
costs `npx` roughly 1s per call. When nothing installed matches, it falls back to `npx`.
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
npm i -g universal-plugin
|
|
120
|
+
upx cyber-skills@^2 audit validate
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Use a caret range on the major rather than an exact pin, so one global install serves every caller.
|
|
124
|
+
|
|
125
|
+
`upx` only works once `universal-plugin` is installed globally. `npx` ships with npm, so keep `npx`
|
|
126
|
+
as the default anywhere you cannot guarantee that install.
|
|
127
|
+
|
|
128
|
+
## Related
|
|
129
|
+
|
|
130
|
+
This package publishes a plugin. To set up the agent configuration of a repository you work in, use
|
|
131
|
+
[`buddy-agent-harness`](https://github.com/repobuddy/buddy-agent-harness).
|
|
132
|
+
|
|
102
133
|
## License
|
|
103
134
|
|
|
104
|
-
MIT
|
|
135
|
+
[MIT](https://github.com/cyberuni/universal-plugin/blob/main/LICENSE)
|