universal-plugin 0.1.0 → 0.2.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/bin/universal-plugin.mjs +0 -0
- package/dist/cli.mjs +61 -4
- package/governances/plugin-design.md +279 -0
- package/package.json +58 -59
package/bin/universal-plugin.mjs
CHANGED
|
File without changes
|
package/dist/cli.mjs
CHANGED
|
@@ -232,7 +232,7 @@ function buildPlugin(root, opts = {}) {
|
|
|
232
232
|
};
|
|
233
233
|
}
|
|
234
234
|
const written = [];
|
|
235
|
-
const { vendorExtensions: _ext, $schema: _schema, ...canonical } = manifest;
|
|
235
|
+
const { vendorExtensions: _ext, $schema: _schema, packagePath: _pkg, ...canonical } = manifest;
|
|
236
236
|
for (const vendor of vendors) {
|
|
237
237
|
const outputPath = path.join(root, VENDOR_OUTPUT[vendor]);
|
|
238
238
|
const outputDir = path.dirname(outputPath);
|
|
@@ -309,6 +309,9 @@ function getUserDir() {
|
|
|
309
309
|
function getProjectDir(root) {
|
|
310
310
|
return path.join(root, "governances");
|
|
311
311
|
}
|
|
312
|
+
function getLocalDir(root) {
|
|
313
|
+
return path.join(root, ".agents", "governances");
|
|
314
|
+
}
|
|
312
315
|
function getPackageDir() {
|
|
313
316
|
const thisFile = fileURLToPath(import.meta.url);
|
|
314
317
|
return path.join(path.dirname(thisFile), "..", "governances");
|
|
@@ -323,6 +326,10 @@ function getScopedPaths(root) {
|
|
|
323
326
|
scope: "project",
|
|
324
327
|
dir: getProjectDir(root)
|
|
325
328
|
},
|
|
329
|
+
{
|
|
330
|
+
scope: "local",
|
|
331
|
+
dir: getLocalDir(root)
|
|
332
|
+
},
|
|
326
333
|
{
|
|
327
334
|
scope: "user",
|
|
328
335
|
dir: getUserDir()
|
|
@@ -395,7 +402,7 @@ function readGlobalState() {
|
|
|
395
402
|
}
|
|
396
403
|
}
|
|
397
404
|
function governanceCommand() {
|
|
398
|
-
const cmd = new Command("governance").description("Manage plugin governances").
|
|
405
|
+
const cmd = new Command("governance").description("Manage plugin governances").helpCommand(false);
|
|
399
406
|
cmd.command("show <name>").description("Show a governance by name").addOption(ROOT_OPTION).addOption(new Option("--json").hideHelp()).action((name, opts) => {
|
|
400
407
|
const result = showGovernance(name, resolveRoot(opts.root), realGovernanceFs, {
|
|
401
408
|
state: readGlobalState(),
|
|
@@ -655,6 +662,55 @@ function prepareCommand() {
|
|
|
655
662
|
});
|
|
656
663
|
}
|
|
657
664
|
//#endregion
|
|
665
|
+
//#region src/publish/fs.ts
|
|
666
|
+
const realSyncVersionFs = {
|
|
667
|
+
exists: (p) => fsNode.existsSync(p),
|
|
668
|
+
read: (p) => fsNode.readFileSync(p, "utf8"),
|
|
669
|
+
write: (p, content) => fsNode.writeFileSync(p, content)
|
|
670
|
+
};
|
|
671
|
+
//#endregion
|
|
672
|
+
//#region src/publish/sync-version.ts
|
|
673
|
+
function syncVersion(root, syncFs) {
|
|
674
|
+
const manifestPath = path.join(root, ".plugin", "plugin.json");
|
|
675
|
+
if (!syncFs.exists(manifestPath)) throw new Error(`No .plugin/plugin.json found at ${root}`);
|
|
676
|
+
const manifest = JSON.parse(syncFs.read(manifestPath));
|
|
677
|
+
const packagePath = manifest["packagePath"];
|
|
678
|
+
if (!packagePath || typeof packagePath !== "string") throw new Error("packagePath is required in .plugin/plugin.json");
|
|
679
|
+
const pkgJsonPath = path.join(root, packagePath, "package.json");
|
|
680
|
+
if (!syncFs.exists(pkgJsonPath)) throw new Error(`No package.json found at ${packagePath}`);
|
|
681
|
+
const version = JSON.parse(syncFs.read(pkgJsonPath))["version"];
|
|
682
|
+
if (!version || typeof version !== "string") throw new Error(`No version found in ${packagePath}/package.json`);
|
|
683
|
+
const updated = {
|
|
684
|
+
...manifest,
|
|
685
|
+
version
|
|
686
|
+
};
|
|
687
|
+
syncFs.write(manifestPath, `${JSON.stringify(updated, null, " ")}\n`);
|
|
688
|
+
return {
|
|
689
|
+
version,
|
|
690
|
+
manifestPath
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
//#endregion
|
|
694
|
+
//#region src/publish/cli.ts
|
|
695
|
+
function publishCommand() {
|
|
696
|
+
const cmd = new Command("publish").description("Prepare plugin for publishing").helpCommand(false);
|
|
697
|
+
cmd.command("sync-version").description("Sync version from packagePath/package.json into .plugin/plugin.json").addOption(ROOT_OPTION).action((opts) => {
|
|
698
|
+
try {
|
|
699
|
+
const result = syncVersion(resolveRoot(opts.root), realSyncVersionFs);
|
|
700
|
+
output(result, () => {
|
|
701
|
+
printFields({
|
|
702
|
+
version: result.version,
|
|
703
|
+
manifest: result.manifestPath
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
} catch (err) {
|
|
707
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
708
|
+
process.exit(1);
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
return cmd;
|
|
712
|
+
}
|
|
713
|
+
//#endregion
|
|
658
714
|
//#region src/self-update/fs.ts
|
|
659
715
|
function globalStatePath$1() {
|
|
660
716
|
return path.join(os.homedir(), ".agents", "universal-plugin.json");
|
|
@@ -776,7 +832,7 @@ function realSyncFs() {
|
|
|
776
832
|
};
|
|
777
833
|
}
|
|
778
834
|
function syncCommand() {
|
|
779
|
-
const cmd = new Command("sync").description("Manage cross-vendor plugin sync").
|
|
835
|
+
const cmd = new Command("sync").description("Manage cross-vendor plugin sync").helpCommand(false);
|
|
780
836
|
cmd.command("apply").description("Apply a pending sync action").argument("<action-id>", "Action ID from ~/.agents/universal-plugin.json").action((actionId) => {
|
|
781
837
|
const result = applySyncAction({
|
|
782
838
|
actionId,
|
|
@@ -799,11 +855,12 @@ function syncCommand() {
|
|
|
799
855
|
//#endregion
|
|
800
856
|
//#region src/cli.ts
|
|
801
857
|
const program = new Command();
|
|
802
|
-
program.name("universal-plugin").description("Universal AI agent plugin build tool").version("0.0.0").
|
|
858
|
+
program.name("universal-plugin").description("Universal AI agent plugin build tool").version("0.0.0").helpCommand(false);
|
|
803
859
|
program.addCommand(buildCommand());
|
|
804
860
|
program.addCommand(cleanCommand());
|
|
805
861
|
program.addCommand(governanceCommand());
|
|
806
862
|
program.addCommand(prepareCommand());
|
|
863
|
+
program.addCommand(publishCommand());
|
|
807
864
|
program.addCommand(syncCommand());
|
|
808
865
|
program.addCommand(selfUpdateCommand());
|
|
809
866
|
program.parseAsync(process.argv).catch((err) => {
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# Plugin Design
|
|
2
|
+
|
|
3
|
+
Authoritative rules for creating, validating, and transforming cross-vendor agent plugins. Apply when creating, auditing, or distributing a plugin; see **skill-design** governance for standalone skill authoring rules.
|
|
4
|
+
|
|
5
|
+
A **plugin** is the distribution unit — it bundles skills, MCP servers, hooks, commands, agents, and other extensions into a single installable package. A **skill** is the capability unit inside a plugin. Install plugins; invoke skills.
|
|
6
|
+
|
|
7
|
+
## Source of Truth: `.plugin/plugin.json`
|
|
8
|
+
|
|
9
|
+
Author `.plugin/plugin.json` as the canonical manifest. All vendor manifests are derived from it via `build`. This file is never read directly by vendors at runtime; it is the single source that the build layer transforms into each vendor's manifest.
|
|
10
|
+
|
|
11
|
+
Schema declaration (first field):
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{ "$schema": "https://schema.cyberuni.dev/universal-agent-plugin/v1.json" }
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Required fields
|
|
18
|
+
|
|
19
|
+
| Field | Type | Constraint |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| `name` | string | 1–64 chars. Pattern: `^[a-z0-9]([a-z0-9\-.]*[a-z0-9])?$`. Lowercase letters, digits, hyphens, periods only. No leading/trailing `-` or `.`. No `--` or `..`. |
|
|
22
|
+
|
|
23
|
+
### Optional metadata fields
|
|
24
|
+
|
|
25
|
+
| Field | Type | Notes |
|
|
26
|
+
| --- | --- | --- |
|
|
27
|
+
| `version` | string | semver. **Required by Codex** — build fails if absent. |
|
|
28
|
+
| `description` | string | ≤ 1024 chars. **Required by Codex** — build fails if absent. |
|
|
29
|
+
| `author` | object | `{ name, email, url }` — all sub-fields optional. |
|
|
30
|
+
| `homepage` | string | URL. |
|
|
31
|
+
| `repository` | string \| object | URL string or `{ type, url }` object. |
|
|
32
|
+
| `license` | string | SPDX identifier (e.g. `"MIT"`). |
|
|
33
|
+
| `keywords` | string[] | Searchable tags for marketplace discovery. |
|
|
34
|
+
|
|
35
|
+
### Component path fields
|
|
36
|
+
|
|
37
|
+
Each accepts `string | string[] | { paths: string[] }`. Every path must start with `./`. No `../` segments — traversal rejected by conformant hosts.
|
|
38
|
+
|
|
39
|
+
| Field | Component | Core? | Notes |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| `skills` | Skill directories containing `SKILL.md` | Yes | Default: `./skills/` |
|
|
42
|
+
| `mcpServers` | `.mcp.json` path or inline MCP config | Yes | Default: `./.mcp.json` |
|
|
43
|
+
| `commands` | Slash command `.md` files | Extended | Default: `./commands/` |
|
|
44
|
+
| `agents` | Agent `.md` files | Extended | Default: `./agents/` |
|
|
45
|
+
| `rules` | Context rule `.mdc` files | Extended | Cursor-only; ignored by other hosts |
|
|
46
|
+
| `hooks` | `hooks.json` path or inline hook config | Extended | Canonical uses PascalCase events; build translates per vendor |
|
|
47
|
+
| `lspServers` | `.lsp.json` path | Extended | Claude Code only |
|
|
48
|
+
| `outputStyles` | Output style resources directory | Extended | Claude Code only |
|
|
49
|
+
|
|
50
|
+
A conformant host must support at least one core component (`skills` or `mcpServers`). Extended types are silently ignored on non-supporting hosts — do not rely on them for core plugin functionality.
|
|
51
|
+
|
|
52
|
+
### `vendorExtensions` field
|
|
53
|
+
|
|
54
|
+
Declares which vendor manifests `build` generates, and provides vendor-specific fields for each. Each key is a recognized vendor ID; its presence drives build output. An empty `{}` opts into that vendor's output with no vendor-specific fields.
|
|
55
|
+
|
|
56
|
+
| Vendor ID | Output path | Required beyond `name` |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| `claude-code` | `.claude-plugin/plugin.json` | none |
|
|
59
|
+
| `cursor` | `.cursor-plugin/plugin.json` | none |
|
|
60
|
+
| `codex` | `.codex-plugin/plugin.json` | `version`, `description` |
|
|
61
|
+
| `copilot-cli` | `plugin.json` (repo root) | none |
|
|
62
|
+
|
|
63
|
+
Vendor-specific extension fields:
|
|
64
|
+
|
|
65
|
+
| Field | `claude-code` | `cursor` | `codex` | `copilot-cli` |
|
|
66
|
+
| --- | --- | --- | --- | --- |
|
|
67
|
+
| `displayName` | ✓ | — | — | — |
|
|
68
|
+
| `defaultEnabled` | ✓ (bool) | — | — | — |
|
|
69
|
+
| `userConfig` | ✓ (prompted at enable) | — | — | — |
|
|
70
|
+
| `channels` | ✓ | — | — | — |
|
|
71
|
+
| `dependencies` | ✓ (inter-plugin) | — | — | — |
|
|
72
|
+
| `themes` | ✓ | — | — | — |
|
|
73
|
+
| `monitors` | ✓ | — | — | — |
|
|
74
|
+
| `logo` | — | ✓ | — | — |
|
|
75
|
+
| `publisher` | — | ✓ | — | — |
|
|
76
|
+
| `category` | — | ✓ | — | ✓ |
|
|
77
|
+
| `tags` | — | ✓ | — | ✓ |
|
|
78
|
+
| `apps` | — | — | ✓ (→ `.app.json`) | — |
|
|
79
|
+
| `interface` | — | — | ✓ (marketplace metadata) | — |
|
|
80
|
+
|
|
81
|
+
## Canonical Directory Layout
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
<plugin-name>/
|
|
85
|
+
├── .plugin/
|
|
86
|
+
│ └── plugin.json ← canonical source of truth
|
|
87
|
+
│
|
|
88
|
+
├── skills/<skill-name>/SKILL.md ← shared: all vendors, identical format
|
|
89
|
+
│
|
|
90
|
+
├── commands/setup.md ← required when rules/ is present
|
|
91
|
+
├── commands/<cmd-name>.md ← Claude Code + Cursor + Copilot CLI
|
|
92
|
+
├── agents/<agent-name>.md ← Claude Code + Cursor + Copilot CLI
|
|
93
|
+
├── rules/<rule-name>.mdc ← Cursor-only always-on
|
|
94
|
+
│
|
|
95
|
+
├── hooks/hooks.json ← canonical hooks (PascalCase, ${PLUGIN_ROOT})
|
|
96
|
+
│
|
|
97
|
+
├── .mcp.json ← source of truth (all vendors)
|
|
98
|
+
├── mcp.json -> .mcp.json ← symlink (Cursor + open-plugin-spec)
|
|
99
|
+
│
|
|
100
|
+
└── README.md
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Generated build artifacts (gitignore or commit — author's choice):
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
├── .claude-plugin/
|
|
107
|
+
│ ├── plugin.json ← generated
|
|
108
|
+
│ └── hooks/hooks.json ← generated (PascalCase, ${CLAUDE_PLUGIN_ROOT})
|
|
109
|
+
├── .cursor-plugin/
|
|
110
|
+
│ ├── plugin.json ← generated
|
|
111
|
+
│ └── hooks/hooks.json ← generated (camelCase, ${PLUGIN_ROOT} pass-through)
|
|
112
|
+
├── .codex-plugin/
|
|
113
|
+
│ ├── plugin.json ← generated
|
|
114
|
+
│ └── hooks/hooks.json ← generated (PascalCase, ${PLUGIN_ROOT} native)
|
|
115
|
+
└── plugin.json ← generated (copilot-cli root manifest)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Vendor Manifest Derivation
|
|
119
|
+
|
|
120
|
+
Build reads `.plugin/plugin.json`, applies the rules below, writes each vendor's output.
|
|
121
|
+
|
|
122
|
+
### Metadata field mapping
|
|
123
|
+
|
|
124
|
+
| Canonical field | Claude Code | Cursor | Codex | Copilot CLI |
|
|
125
|
+
| --- | --- | --- | --- | --- |
|
|
126
|
+
| `name` | ✓ required | ✓ required | ✓ required | ✓ required |
|
|
127
|
+
| `version` | ✓ optional | ✓ optional | ✓ **required** | ✓ optional |
|
|
128
|
+
| `description` | ✓ optional | ✓ optional | ✓ **required** | ✓ optional |
|
|
129
|
+
| `author`, `homepage`, `repository`, `license`, `keywords` | ✓ | ✓ | ✓ | ✓ |
|
|
130
|
+
|
|
131
|
+
### Component path field mapping
|
|
132
|
+
|
|
133
|
+
| Canonical field | Claude Code | Cursor | Codex | Copilot CLI |
|
|
134
|
+
| --- | --- | --- | --- | --- |
|
|
135
|
+
| `skills` | ✓ | ✓ | ✓ | ✓ |
|
|
136
|
+
| `mcpServers` | ✓ → `./.mcp.json` | adapt → `./mcp.json` (symlink) | ✓ → `./.mcp.json` | ✓ |
|
|
137
|
+
| `commands` | ✓ | ✓ | **omit** | ✓ |
|
|
138
|
+
| `agents` | ✓ | ✓ | **omit** | ✓ |
|
|
139
|
+
| `rules` | **omit** | ✓ | **omit** | **omit** |
|
|
140
|
+
| `hooks` | adapt → PascalCase, `${CLAUDE_PLUGIN_ROOT}` | adapt → camelCase, pass-through env | ✓ → PascalCase, `${PLUGIN_ROOT}` native | adapt → camelCase, pass-through env |
|
|
141
|
+
| `lspServers` | ✓ | **omit** | **omit** | **omit** |
|
|
142
|
+
| `outputStyles` | ✓ | **omit** | **omit** | **omit** |
|
|
143
|
+
|
|
144
|
+
## Hook Event Name Mapping
|
|
145
|
+
|
|
146
|
+
Canonical hooks file uses **PascalCase** event names. Build translates per vendor.
|
|
147
|
+
|
|
148
|
+
| Canonical (PascalCase) | Claude Code | Cursor | Codex | Copilot CLI |
|
|
149
|
+
| --- | --- | --- | --- | --- |
|
|
150
|
+
| `PreToolUse` | `PreToolUse` | `preToolUse` | `PreToolUse` | `preToolUse` |
|
|
151
|
+
| `PostToolUse` | `PostToolUse` | `postToolUse` | `PostToolUse` | `postToolUse` |
|
|
152
|
+
| `PostToolUseFailure` | `PostToolUseFailure` | — (drop+warn) | — (drop+warn) | — (drop+warn) |
|
|
153
|
+
| `SessionStart` | `SessionStart` | `sessionStart` | `SessionStart` | `sessionStart` |
|
|
154
|
+
| `SessionEnd` | `SessionEnd` | `sessionEnd` | `SessionEnd` | `sessionEnd` |
|
|
155
|
+
| `Stop` | `Stop` | — (drop+warn) | — (drop+warn) | `agentStop` |
|
|
156
|
+
| `UserPromptSubmit` | `UserPromptSubmit` | `beforeSubmitPrompt` | — (drop+warn) | — (drop+warn) |
|
|
157
|
+
| `Notification` | `Notification` | — (drop+warn) | — (drop+warn) | `notification` |
|
|
158
|
+
| `PermissionRequest` | `PermissionRequest` | — (drop+warn) | — (drop+warn) | `permissionRequest` |
|
|
159
|
+
| `SubagentStart` | — | `subagentStart` | — | — |
|
|
160
|
+
| `SubagentStop` | — | `subagentStop` | — | — |
|
|
161
|
+
| `PreCompact` | `PreCompact` | `preCompact` | — | — |
|
|
162
|
+
|
|
163
|
+
Events not in this table: pass through for `claude-code`; drop + warn for all others.
|
|
164
|
+
|
|
165
|
+
**Canonical hooks file** (`hooks/hooks.json` — PascalCase, `${PLUGIN_ROOT}`):
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"hooks": {
|
|
170
|
+
"PreToolUse": [
|
|
171
|
+
{
|
|
172
|
+
"matcher": "Write|Edit",
|
|
173
|
+
"hooks": [{ "type": "command", "command": "${PLUGIN_ROOT}/hooks/impl.sh", "timeout": 10 }]
|
|
174
|
+
}
|
|
175
|
+
],
|
|
176
|
+
"SessionStart": [
|
|
177
|
+
{
|
|
178
|
+
"hooks": [{ "type": "command", "command": "${PLUGIN_ROOT}/hooks/impl.sh", "timeout": 10 }]
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Build generates vendor-specific versions. Do not hand-author the generated hook files.
|
|
186
|
+
|
|
187
|
+
## MCP: Symlink Rule
|
|
188
|
+
|
|
189
|
+
`.mcp.json` is the source of truth. `mcp.json` is always a symlink — never a regular file.
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
ln -sf .mcp.json mcp.json
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
| Runtime | Reads |
|
|
196
|
+
| --- | --- |
|
|
197
|
+
| Claude Code, Codex | `.mcp.json` |
|
|
198
|
+
| Cursor, open-plugin-spec | `mcp.json` (via symlink) |
|
|
199
|
+
|
|
200
|
+
If the repo needs explicit symlink tracking: `mcp.json symlink` in `.gitattributes`. MCP server startup failures are non-fatal.
|
|
201
|
+
|
|
202
|
+
## Environment Variable Mapping
|
|
203
|
+
|
|
204
|
+
| Canonical | Claude Code | Cursor | Codex | Copilot CLI |
|
|
205
|
+
| --- | --- | --- | --- | --- |
|
|
206
|
+
| `${PLUGIN_ROOT}` | `${CLAUDE_PLUGIN_ROOT}` | pass-through (undocumented) | `${PLUGIN_ROOT}` (native) | pass-through (undocumented) |
|
|
207
|
+
| `${PLUGIN_DATA}` | `${CLAUDE_PLUGIN_DATA}` | pass-through (undocumented) | `${PLUGIN_DATA}` (native) | pass-through (undocumented) |
|
|
208
|
+
|
|
209
|
+
`${PLUGIN_ROOT}` is ephemeral (changes on update). `${PLUGIN_DATA}` survives updates; use it for caches and generated artifacts. `${CLAUDE_PROJECT_DIR}` (Claude Code only) is the project root the agent launched from.
|
|
210
|
+
|
|
211
|
+
## Component Authoring Rules
|
|
212
|
+
|
|
213
|
+
**Skills:** Author `skills/<name>/SKILL.md` following the **skill-design** governance. Within a plugin, reference MCP tools by fully qualified name: `{plugin-name}:{server-name}__{tool-name}`.
|
|
214
|
+
|
|
215
|
+
**Commands:** One `.md` file per command in `commands/`. Filename (minus extension) is the command identifier. Optional frontmatter: `description`, `argument-hint`, `allowed-tools`, `disable-model-invocation`. `$ARGUMENTS` expands to user input.
|
|
216
|
+
|
|
217
|
+
**Agents:** One `.md` file per agent in `agents/`. Required frontmatter: `name` (1–64 lowercase alphanumeric + hyphens), `description` (≤ 1024 chars). Body is the agent system prompt.
|
|
218
|
+
|
|
219
|
+
**Rules (Cursor-only):** `.mdc` files in `rules/`. Required frontmatter: `description`. Optional: `alwaysApply` (bool), `globs` (file-pattern array). Bundle `commands/setup.md` to merge rule content into project's `AGENTS.md` — after that merge, `.mdc` files are redundant.
|
|
220
|
+
|
|
221
|
+
**Decision tree for always-on guidance:**
|
|
222
|
+
- Situation-triggered → **skill** (all agents)
|
|
223
|
+
- Always-on, cross-agent → merge into **AGENTS.md**
|
|
224
|
+
- Always-on, Cursor-only → `rules/` + `commands/setup.md`
|
|
225
|
+
|
|
226
|
+
## Namespacing
|
|
227
|
+
|
|
228
|
+
| Component | Format |
|
|
229
|
+
| --- | --- |
|
|
230
|
+
| Skills | `{plugin-name}:{skill-name}` |
|
|
231
|
+
| MCP tools | `mcp__plugin_{plugin-name}_{server-name}__{tool-name}` |
|
|
232
|
+
| Commands / agents | `{plugin-name}:{component-name}` |
|
|
233
|
+
|
|
234
|
+
## Distribution
|
|
235
|
+
|
|
236
|
+
| Scope | Claude Code | Cursor | Codex |
|
|
237
|
+
| --- | --- | --- | --- |
|
|
238
|
+
| **Personal** | `~/.claude/plugins/local/<name>` symlink | `~/.cursor/plugins/local/<name>` symlink + reload | `~/.agents/plugins/marketplace.json` |
|
|
239
|
+
| **Team** | npm private package | Cursor Teams admin import | `.agents/plugins/marketplace.json` in repo |
|
|
240
|
+
| **Public** | PR to `anthropics/claude-plugins-official` | `cursor.com/marketplace/publish` | `codex plugin marketplace add` |
|
|
241
|
+
|
|
242
|
+
Default scope: **team**.
|
|
243
|
+
|
|
244
|
+
**npm distribution:** All manifest directories (`.plugin/`, `.claude-plugin/`, `.cursor-plugin/`, `.codex-plugin/`) and component directories (`skills/`, `commands/`, `agents/`, `hooks/`) must be in `package.json#files`. `package.json` carries distribution metadata only — no plugin semantics.
|
|
245
|
+
|
|
246
|
+
## Anti-Patterns
|
|
247
|
+
|
|
248
|
+
- Using `../` in any manifest-declared path
|
|
249
|
+
- Hardcoding absolute paths instead of `${PLUGIN_ROOT}` or `${PLUGIN_DATA}`
|
|
250
|
+
- Committing `mcp.json` as a regular file — must always be a symlink to `.mcp.json`
|
|
251
|
+
- Hand-authoring vendor hook files — use canonical `hooks/hooks.json` and let build generate vendor versions
|
|
252
|
+
- Relying on extended component types (commands, rules, agents, hooks) for core functionality — silently ignored on non-supporting hosts
|
|
253
|
+
- Duplicating SKILL.md content in `plugin.json` — the manifest is a path index, never a content mirror
|
|
254
|
+
- Using `rules/` for cross-agent always-on guidance — use AGENTS.md instead
|
|
255
|
+
- Putting install-time metadata in `plugin.json` instead of `skill.json`
|
|
256
|
+
|
|
257
|
+
## Cross-Platform Portability
|
|
258
|
+
|
|
259
|
+
| Runtime | SKILL.md native | Plugin manifest |
|
|
260
|
+
| --- | --- | --- |
|
|
261
|
+
| Claude Code | Yes | `.claude-plugin/plugin.json` |
|
|
262
|
+
| Codex | Yes | `.codex-plugin/plugin.json` (`version` + `description` required) |
|
|
263
|
+
| Cursor | Yes (conversion) | `.cursor-plugin/plugin.json` |
|
|
264
|
+
| Copilot CLI | Yes | `plugin.json` at repo root |
|
|
265
|
+
| Gemini CLI, GitHub Copilot, Amp | Yes | Different manifest paths — require separate authoring |
|
|
266
|
+
| Windsurf | Needs conversion | 6,000 char/file hard limit; 12,000 chars total |
|
|
267
|
+
| Zed, Aider, Continue.dev, Cline | No | Incompatible formats |
|
|
268
|
+
|
|
269
|
+
Portability rules for skill bodies: keep each `SKILL.md` body under 6,000 chars; use forward slashes in path references; declare environment requirements in `compatibility` frontmatter; do not embed vendor-specific syntax in skill bodies.
|
|
270
|
+
|
|
271
|
+
## References
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
npx universal-plugin@<version> governance show skill-design
|
|
275
|
+
npx universal-plugin@<version> governance show skill-repo-structure
|
|
276
|
+
npx universal-plugin@<version> governance show agent-tool-output
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Spec: https://github.com/cyberuni/universal-plugin/blob/main/spec/universal-plugin-system.md
|
package/package.json
CHANGED
|
@@ -1,60 +1,59 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
2
|
+
"name": "universal-plugin",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Universal AI agent plugin build tool",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agent-plugin",
|
|
7
|
+
"claude-code",
|
|
8
|
+
"cursor",
|
|
9
|
+
"codex",
|
|
10
|
+
"copilot-cli"
|
|
11
|
+
],
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/cyberuni/universal-plugin.git",
|
|
15
|
+
"directory": "packages/universal-plugin"
|
|
16
|
+
},
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "unional <homawong@gmail.com>",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"bin": {
|
|
21
|
+
"universal-plugin": "bin/universal-plugin.mjs"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin",
|
|
28
|
+
"dist",
|
|
29
|
+
"governances"
|
|
30
|
+
],
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"commander": "^14.0.3"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^24.10.1",
|
|
36
|
+
"knip": "^6.14.1",
|
|
37
|
+
"tsdown": "^0.22.0",
|
|
38
|
+
"tsx": "^4.22.3",
|
|
39
|
+
"typescript": "^6.0.0",
|
|
40
|
+
"vitest": "^4.1.7"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=22"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public",
|
|
47
|
+
"provenance": true
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsdown",
|
|
51
|
+
"dev": "tsx src/cli.ts",
|
|
52
|
+
"knip": "knip",
|
|
53
|
+
"lint": "biome check .",
|
|
54
|
+
"test": "pnpm build && vitest run src",
|
|
55
|
+
"test:watch": "vitest",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"verify": "pnpm typecheck && pnpm lint && pnpm test"
|
|
58
|
+
}
|
|
59
|
+
}
|