universal-plugin 0.3.1 → 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 +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/LICENSE +21 -0
- package/dist/cli.mjs +76 -134
- package/package.json +3 -1
- package/plugin.json +1 -1
- 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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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.4.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.4.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/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
|
|
@@ -419,30 +416,22 @@ function buildCommand() {
|
|
|
419
416
|
});
|
|
420
417
|
for (const warning of result.warnings) process.stderr.write(`warn: ${warning}\n`);
|
|
421
418
|
const { built, skipped, failed, canonical } = result.summary;
|
|
422
|
-
|
|
419
|
+
const jsonResult = {
|
|
423
420
|
built: result.rows.filter((r) => r.status === "built"),
|
|
424
421
|
skipped: result.rows.filter((r) => r.status === "skipped"),
|
|
425
422
|
failed: result.rows.filter((r) => r.status === "failed"),
|
|
426
423
|
canonical: result.rows.filter((r) => r.status === "canonical"),
|
|
427
424
|
summary: result.summary,
|
|
428
425
|
warnings: result.warnings
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
},
|
|
439
|
-
{
|
|
440
|
-
label: "status",
|
|
441
|
-
get: (r) => r.status
|
|
442
|
-
}
|
|
443
|
-
]);
|
|
444
|
-
const counts = `built ${built}, skipped ${skipped}, failed ${failed}`;
|
|
445
|
-
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
|
|
446
435
|
});
|
|
447
436
|
process.stderr.write(NEXT_STEP$2);
|
|
448
437
|
if (failed > 0) process.exitCode = 1;
|
|
@@ -730,29 +719,17 @@ function bundleCommand() {
|
|
|
730
719
|
const pinned = result.pins.filter((p) => p.status === "pinned").length;
|
|
731
720
|
const unchanged = result.pins.filter((p) => p.status === "unchanged").length;
|
|
732
721
|
const skipped = result.pins.filter((p) => p.status === "skipped").length;
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
},
|
|
745
|
-
{
|
|
746
|
-
label: "resolved",
|
|
747
|
-
get: (r) => r.resolved
|
|
748
|
-
},
|
|
749
|
-
{
|
|
750
|
-
label: "status",
|
|
751
|
-
get: (r) => r.status
|
|
752
|
-
}
|
|
753
|
-
]);
|
|
754
|
-
if (truncated) console.log(`… +${result.pins.length - TRUNCATE_THRESHOLD} more — rerun with --full`);
|
|
755
|
-
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}`
|
|
756
733
|
});
|
|
757
734
|
if (result.pins.length === 0) process.stderr.write("nothing to bundle\n");
|
|
758
735
|
process.stderr.write(NEXT_STEP$1);
|
|
@@ -853,22 +830,11 @@ function addCommand(fs) {
|
|
|
853
830
|
key: opts.key,
|
|
854
831
|
name: result.name,
|
|
855
832
|
action: result.action
|
|
856
|
-
},
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
},
|
|
862
|
-
{
|
|
863
|
-
label: "name",
|
|
864
|
-
get: (r) => r.name
|
|
865
|
-
},
|
|
866
|
-
{
|
|
867
|
-
label: "action",
|
|
868
|
-
get: (r) => r.action
|
|
869
|
-
}
|
|
870
|
-
]);
|
|
871
|
-
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`
|
|
872
838
|
});
|
|
873
839
|
process.stderr.write(`→ universal-plugin config get --key ${opts.key}\n`);
|
|
874
840
|
} catch (err) {
|
|
@@ -883,12 +849,9 @@ function getCommand(fs) {
|
|
|
883
849
|
assertNotReserved(opts.key);
|
|
884
850
|
const root = resolveRoot(opts.root);
|
|
885
851
|
const entries = getEntries(fs.read(root), opts.key);
|
|
886
|
-
output(entries,
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
get: (e) => String(e.name ?? "")
|
|
890
|
-
}]);
|
|
891
|
-
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`
|
|
892
855
|
});
|
|
893
856
|
process.stderr.write(`→ universal-plugin config add --key ${opts.key} --entry <json>\n`);
|
|
894
857
|
} catch (err) {
|
|
@@ -1026,20 +989,18 @@ function governanceCommand() {
|
|
|
1026
989
|
process.stderr.write(`Governance "${name}" not found\n`);
|
|
1027
990
|
process.exit(1);
|
|
1028
991
|
}
|
|
1029
|
-
|
|
992
|
+
outputText(result, () => {
|
|
1030
993
|
process.stdout.write(result.content);
|
|
1031
994
|
});
|
|
1032
995
|
});
|
|
1033
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) => {
|
|
1034
997
|
const entries = listGovernances(resolveRoot(opts.root), realGovernanceFs);
|
|
1035
|
-
output(entries,
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
},
|
|
1040
|
-
|
|
1041
|
-
get: (e) => e.scope
|
|
1042
|
-
}]);
|
|
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`
|
|
1043
1004
|
});
|
|
1044
1005
|
});
|
|
1045
1006
|
return cmd;
|
|
@@ -1172,15 +1133,12 @@ function initCommand$1(deps = { fs: realInitFs }) {
|
|
|
1172
1133
|
created: plan.rows.filter((r) => r.action === "created").map((r) => r.path),
|
|
1173
1134
|
updated: plan.rows.filter((r) => r.action === "updated").map((r) => r.path),
|
|
1174
1135
|
summary: plan.summary
|
|
1175
|
-
},
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
},
|
|
1180
|
-
|
|
1181
|
-
get: (r) => r.action
|
|
1182
|
-
}]);
|
|
1183
|
-
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}`
|
|
1184
1142
|
});
|
|
1185
1143
|
process.stderr.write(NEXT_STEP);
|
|
1186
1144
|
} catch (err) {
|
|
@@ -1496,24 +1454,15 @@ function initCommand() {
|
|
|
1496
1454
|
dryRun: opts.dryRun,
|
|
1497
1455
|
force: opts.force
|
|
1498
1456
|
});
|
|
1499
|
-
output(results,
|
|
1500
|
-
{
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
{
|
|
1509
|
-
label: "paths",
|
|
1510
|
-
get: (row) => row.paths.join(", ") || "-"
|
|
1511
|
-
},
|
|
1512
|
-
{
|
|
1513
|
-
label: "plugins",
|
|
1514
|
-
get: (row) => row.plugins.join(", ") || "-"
|
|
1515
|
-
}
|
|
1516
|
-
]));
|
|
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
|
+
});
|
|
1517
1466
|
process.stderr.write("Generated repository metadata only; no marketplace publication, registration, installation, authentication, or provisioning occurred.\n");
|
|
1518
1467
|
} catch (err) {
|
|
1519
1468
|
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
@@ -1849,11 +1798,9 @@ function publishCommand() {
|
|
|
1849
1798
|
cmd.command("sync-version").description("Sync version from packagePath/package.json into plugin.json").addOption(ROOT_OPTION).action((opts) => {
|
|
1850
1799
|
try {
|
|
1851
1800
|
const result = syncVersion(resolveRoot(opts.root), realSyncVersionFs);
|
|
1852
|
-
output(result,
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
manifest: result.manifestPath
|
|
1856
|
-
});
|
|
1801
|
+
output(result, {
|
|
1802
|
+
version: result.version,
|
|
1803
|
+
manifest: result.manifestPath
|
|
1857
1804
|
});
|
|
1858
1805
|
} catch (err) {
|
|
1859
1806
|
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
@@ -2129,19 +2076,14 @@ function versionCommand(deps = { fs: realVersionFs }) {
|
|
|
2129
2076
|
derived: rows.filter((r) => r.action === "derived").length
|
|
2130
2077
|
}
|
|
2131
2078
|
};
|
|
2132
|
-
output(jsonResult,
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
}, {
|
|
2141
|
-
label: "action",
|
|
2142
|
-
get: (r) => r.action
|
|
2143
|
-
}]);
|
|
2144
|
-
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}`
|
|
2145
2087
|
});
|
|
2146
2088
|
process.stderr.write(nextStep(dryRun, opts.build !== false));
|
|
2147
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"
|
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)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# doctor skill
|
|
2
|
+
|
|
3
|
+
Diagnose a universal agent plugin: what the canonical `plugin.json` declares, and whether what is on
|
|
4
|
+
disk still matches it for Claude Code, Cursor, Codex, and GitHub Copilot CLI.
|
|
5
|
+
|
|
6
|
+
## What it does
|
|
7
|
+
|
|
8
|
+
`scripts/doctor.mjs` composes the shipped CLI's `plugin build --dry-run --format json` with the
|
|
9
|
+
filesystem facts that build cannot see — whether each derived manifest exists, whether it predates
|
|
10
|
+
the canonical manifest, whether a stale or shadowing manifest is lying around, and whether the two
|
|
11
|
+
authored version numbers still agree. It emits one JSON object: `vendors`, `findings`, `ok`.
|
|
12
|
+
|
|
13
|
+
The skill supplies the judgment around it: which finding matters, and which skill owns its repair.
|
|
14
|
+
|
|
15
|
+
## It never repairs
|
|
16
|
+
|
|
17
|
+
Every finding names the skill that fixes it — `init` for anything that rewrites the manifest,
|
|
18
|
+
`version` for the release number, `remove-plugin` for artifacts. A repair can overwrite a manifest
|
|
19
|
+
the user maintains, and that judgment belongs to the skill that owns the write.
|
|
20
|
+
|
|
21
|
+
The script is read-only and exits `0` whether or not it finds anything, so it is safe to run
|
|
22
|
+
unattended, including from a session-start hook.
|
|
23
|
+
|
|
24
|
+
## Why a script rather than a checklist
|
|
25
|
+
|
|
26
|
+
The checks are deterministic: same tree, same findings. The one check that is not scriptable is the
|
|
27
|
+
definitive staleness test — rebuild on a clean tree and read the diff — because it writes. The skill
|
|
28
|
+
reports that one as a repair for the user to run.
|
|
29
|
+
|
|
30
|
+
Manifest validation is chartered as a CLI capability (`plugin validate`, specified but not yet
|
|
31
|
+
shipped). This script stays a thin composition on purpose, so it folds into that command rather than
|
|
32
|
+
competing with it.
|
|
33
|
+
|
|
34
|
+
## Boundaries
|
|
35
|
+
|
|
36
|
+
Diagnoses the plugin a project ships. A repository's own agent wiring — `.agents/skills/`,
|
|
37
|
+
`AGENTS.md`, per-harness bridges — is `buddy-agent-harness:doctor`.
|
|
38
|
+
|
|
39
|
+
## References
|
|
40
|
+
|
|
41
|
+
- [Spec](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/spec.md)
|
|
42
|
+
- [`plugin build`](https://github.com/cyberuni/universal-plugin/blob/main/packages/universal-plugin/.agents/spec/plugin/build/README.md)
|