lumia-plugin 0.5.0 → 0.5.2
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/README.md +13 -0
- package/examples/base_plugin/package.json +1 -1
- package/package.json +2 -2
- package/scripts/cli.js +9 -0
- package/scripts/skills.js +364 -0
package/README.md
CHANGED
|
@@ -8,6 +8,19 @@ The `lumia-plugin` package bundles the command-line tools for creating, building
|
|
|
8
8
|
npx lumia-plugin create <directory>
|
|
9
9
|
npx lumia-plugin build [--dir <path>] [--out <file>]
|
|
10
10
|
npx lumia-plugin validate <plugin-directory>
|
|
11
|
+
npx lumia-plugin skills [tool] [--target <path>] [--codex-home <path>]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Quick examples:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx lumia-plugin skills
|
|
18
|
+
npx lumia-plugin skills claude
|
|
19
|
+
npx lumia-plugin skills copilot
|
|
20
|
+
npx lumia-plugin skills gemini
|
|
21
|
+
npx lumia-plugin skills cursor
|
|
22
|
+
npx lumia-plugin skills codex
|
|
23
|
+
npx lumia-plugin skills codex --codex-home "$CODEX_HOME"
|
|
11
24
|
```
|
|
12
25
|
|
|
13
26
|
Run any command with `--help` to see detailed options.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lumia-plugin",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"description": "Command-line tools for creating, building, and validating Lumia Stream plugins.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"lumia-plugin": "scripts/cli.js"
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"author": "Lumia Stream",
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@lumiastream/plugin": "^0.5.
|
|
27
|
+
"@lumiastream/plugin": "^0.5.2",
|
|
28
28
|
"jszip": "3.10.1"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/scripts/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ const commands = {
|
|
|
9
9
|
create: "create-plugin.js",
|
|
10
10
|
build: "build-plugin.js",
|
|
11
11
|
validate: "validate-plugin.js",
|
|
12
|
+
skills: "skills.js",
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
const scriptArgs = args.slice(1);
|
|
@@ -22,11 +23,19 @@ Commands:
|
|
|
22
23
|
create [directory] Create a new plugin from template
|
|
23
24
|
build [directory] Build a plugin into .lumiaplugin package
|
|
24
25
|
validate [file] Validate a .lumiaplugin package
|
|
26
|
+
skills [tool] Install/update skills
|
|
25
27
|
|
|
26
28
|
Examples:
|
|
27
29
|
lumia-plugin create my_plugin
|
|
28
30
|
lumia-plugin build ./my_plugin
|
|
29
31
|
lumia-plugin validate my_plugin.lumiaplugin
|
|
32
|
+
lumia-plugin skills
|
|
33
|
+
lumia-plugin skills claude
|
|
34
|
+
lumia-plugin skills copilot
|
|
35
|
+
lumia-plugin skills gemini
|
|
36
|
+
lumia-plugin skills cursor
|
|
37
|
+
lumia-plugin skills codex
|
|
38
|
+
lumia-plugin skills codex --codex-home "$CODEX_HOME"
|
|
30
39
|
`);
|
|
31
40
|
process.exit(command ? 1 : 0);
|
|
32
41
|
}
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const https = require("https");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const os = require("os");
|
|
6
|
+
|
|
7
|
+
const RAW_BASE = "https://raw.githubusercontent.com/lumiastream/Plugin-SDK/main";
|
|
8
|
+
|
|
9
|
+
const PROJECT_TOOL_FILES = {
|
|
10
|
+
claude: [
|
|
11
|
+
{
|
|
12
|
+
remote:
|
|
13
|
+
"skills/lumia-plugin-claude-skill/lumia-plugin-claude-skill.md",
|
|
14
|
+
local: "CLAUDE.md",
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
copilot: [
|
|
18
|
+
{
|
|
19
|
+
remote: ".github/copilot-instructions.md",
|
|
20
|
+
local: ".github/copilot-instructions.md",
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
gemini: [
|
|
24
|
+
{
|
|
25
|
+
remote: "GEMINI.md",
|
|
26
|
+
local: "GEMINI.md",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
cursor: [
|
|
30
|
+
{
|
|
31
|
+
remote: ".cursor/rules/lumia-plugin-workflow.mdc",
|
|
32
|
+
local: ".cursor/rules/lumia-plugin-workflow.mdc",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
remote: ".cursor/rules/lumia-plugin-manifest-contracts.mdc",
|
|
36
|
+
local: ".cursor/rules/lumia-plugin-manifest-contracts.mdc",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
remote: "skills/lumia-plugin-codex-skill/scripts/plugin-audit.js",
|
|
40
|
+
local: "scripts/plugin-audit.js",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const CODEX_TOOL_FILES = [
|
|
46
|
+
{
|
|
47
|
+
remote: "skills/lumia-plugin-codex-skill/SKILL.md",
|
|
48
|
+
local: "skills/lumia-plugin-codex-skill/SKILL.md",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
remote: "skills/lumia-plugin-codex-skill/agents/openai.yaml",
|
|
52
|
+
local: "skills/lumia-plugin-codex-skill/agents/openai.yaml",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
remote: "skills/lumia-plugin-codex-skill/scripts/plugin-audit.js",
|
|
56
|
+
local: "skills/lumia-plugin-codex-skill/scripts/plugin-audit.js",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
remote: "skills/lumia-plugin-codex-skill/references/workflow.md",
|
|
60
|
+
local: "skills/lumia-plugin-codex-skill/references/workflow.md",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
remote:
|
|
64
|
+
"skills/lumia-plugin-codex-skill/references/manifest-capability-contracts.md",
|
|
65
|
+
local:
|
|
66
|
+
"skills/lumia-plugin-codex-skill/references/manifest-capability-contracts.md",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
remote: "skills/lumia-plugin-codex-skill/references/sdk-docs/INDEX.md",
|
|
70
|
+
local: "skills/lumia-plugin-codex-skill/references/sdk-docs/INDEX.md",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
remote: "skills/lumia-plugin-codex-skill/references/sdk-docs/README.md",
|
|
74
|
+
local: "skills/lumia-plugin-codex-skill/references/sdk-docs/README.md",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
remote:
|
|
78
|
+
"skills/lumia-plugin-codex-skill/references/sdk-docs/docs__getting-started.md",
|
|
79
|
+
local:
|
|
80
|
+
"skills/lumia-plugin-codex-skill/references/sdk-docs/docs__getting-started.md",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
remote:
|
|
84
|
+
"skills/lumia-plugin-codex-skill/references/sdk-docs/docs__manifest-guide.md",
|
|
85
|
+
local:
|
|
86
|
+
"skills/lumia-plugin-codex-skill/references/sdk-docs/docs__manifest-guide.md",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
remote:
|
|
90
|
+
"skills/lumia-plugin-codex-skill/references/sdk-docs/docs__api-reference.md",
|
|
91
|
+
local:
|
|
92
|
+
"skills/lumia-plugin-codex-skill/references/sdk-docs/docs__api-reference.md",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
remote:
|
|
96
|
+
"skills/lumia-plugin-codex-skill/references/sdk-docs/docs__field-types-reference.md",
|
|
97
|
+
local:
|
|
98
|
+
"skills/lumia-plugin-codex-skill/references/sdk-docs/docs__field-types-reference.md",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
remote:
|
|
102
|
+
"skills/lumia-plugin-codex-skill/references/sdk-docs/docs__custom-overlays-interop.md",
|
|
103
|
+
local:
|
|
104
|
+
"skills/lumia-plugin-codex-skill/references/sdk-docs/docs__custom-overlays-interop.md",
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
function printHelp() {
|
|
109
|
+
console.log(`Install/update Lumia skills from GitHub.
|
|
110
|
+
|
|
111
|
+
Usage:
|
|
112
|
+
npx lumia-plugin skills [options]
|
|
113
|
+
npx lumia-plugin skills install [options]
|
|
114
|
+
npx lumia-plugin skills list
|
|
115
|
+
npx lumia-plugin skills <tool> [options]
|
|
116
|
+
|
|
117
|
+
Options:
|
|
118
|
+
--target <path> Project path to update (default: current directory)
|
|
119
|
+
--tools <list> Comma-separated: claude,copilot,gemini,cursor,codex
|
|
120
|
+
(default: claude,copilot,gemini,cursor,codex)
|
|
121
|
+
--codex-home <path> Codex home path used when tools include codex
|
|
122
|
+
(default: $CODEX_HOME or ~/.codex)
|
|
123
|
+
--dry-run Print planned updates without writing files
|
|
124
|
+
--help, -h Show this help
|
|
125
|
+
|
|
126
|
+
Examples:
|
|
127
|
+
npx lumia-plugin skills
|
|
128
|
+
npx lumia-plugin skills claude
|
|
129
|
+
npx lumia-plugin skills copilot
|
|
130
|
+
npx lumia-plugin skills gemini
|
|
131
|
+
npx lumia-plugin skills cursor
|
|
132
|
+
npx lumia-plugin skills codex
|
|
133
|
+
npx lumia-plugin skills codex --codex-home "$CODEX_HOME"
|
|
134
|
+
npx lumia-plugin skills --target ./my_plugin
|
|
135
|
+
npx lumia-plugin skills --tools claude,copilot,gemini,cursor
|
|
136
|
+
npx lumia-plugin skills --tools codex --codex-home "$CODEX_HOME"
|
|
137
|
+
`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function printList() {
|
|
141
|
+
console.log(`Available skill bundles:
|
|
142
|
+
|
|
143
|
+
- claude
|
|
144
|
+
- CLAUDE.md
|
|
145
|
+
|
|
146
|
+
- copilot
|
|
147
|
+
- .github/copilot-instructions.md
|
|
148
|
+
|
|
149
|
+
- gemini
|
|
150
|
+
- GEMINI.md
|
|
151
|
+
|
|
152
|
+
- cursor
|
|
153
|
+
- .cursor/rules/lumia-plugin-workflow.mdc
|
|
154
|
+
- .cursor/rules/lumia-plugin-manifest-contracts.mdc
|
|
155
|
+
- scripts/plugin-audit.js
|
|
156
|
+
|
|
157
|
+
- codex (installed into $CODEX_HOME or ~/.codex)
|
|
158
|
+
- skills/lumia-plugin-codex-skill/*
|
|
159
|
+
|
|
160
|
+
Tip: run "npx lumia-plugin skills --dry-run" to preview all file changes.
|
|
161
|
+
`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function parseArgs(argv) {
|
|
165
|
+
const args = argv.slice(2);
|
|
166
|
+
const defaultCodexHome = process.env.CODEX_HOME
|
|
167
|
+
? path.resolve(process.env.CODEX_HOME)
|
|
168
|
+
: path.join(os.homedir(), ".codex");
|
|
169
|
+
const parsed = {
|
|
170
|
+
action: "install",
|
|
171
|
+
target: process.cwd(),
|
|
172
|
+
tools: ["claude", "copilot", "gemini", "cursor", "codex"],
|
|
173
|
+
codexHome: defaultCodexHome,
|
|
174
|
+
dryRun: false,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
if (args[0] === "list") {
|
|
178
|
+
parsed.action = "list";
|
|
179
|
+
args.shift();
|
|
180
|
+
} else if (args[0] === "install") {
|
|
181
|
+
args.shift();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const validTools = new Set(["claude", "copilot", "gemini", "cursor", "codex"]);
|
|
185
|
+
const positionalTools = [];
|
|
186
|
+
while (args.length && !args[0].startsWith("-") && validTools.has(args[0])) {
|
|
187
|
+
positionalTools.push(args.shift());
|
|
188
|
+
}
|
|
189
|
+
if (positionalTools.length > 0) {
|
|
190
|
+
parsed.tools = positionalTools;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
194
|
+
const arg = args[i];
|
|
195
|
+
if (arg === "--help" || arg === "-h") {
|
|
196
|
+
parsed.action = "help";
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (arg === "--dry-run") {
|
|
200
|
+
parsed.dryRun = true;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (arg === "--target") {
|
|
204
|
+
if (!args[i + 1]) {
|
|
205
|
+
throw new Error("Expected a path after --target");
|
|
206
|
+
}
|
|
207
|
+
parsed.target = path.resolve(args[i + 1]);
|
|
208
|
+
i += 1;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (arg === "--codex-home") {
|
|
212
|
+
if (!args[i + 1]) {
|
|
213
|
+
throw new Error("Expected a path after --codex-home");
|
|
214
|
+
}
|
|
215
|
+
parsed.codexHome = path.resolve(args[i + 1]);
|
|
216
|
+
i += 1;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (arg === "--tools") {
|
|
220
|
+
if (!args[i + 1]) {
|
|
221
|
+
throw new Error("Expected a list after --tools");
|
|
222
|
+
}
|
|
223
|
+
parsed.tools = args[i + 1]
|
|
224
|
+
.split(",")
|
|
225
|
+
.map((item) => item.trim().toLowerCase())
|
|
226
|
+
.filter(Boolean);
|
|
227
|
+
i += 1;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return parsed;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function fetchText(url, redirectCount = 0) {
|
|
237
|
+
return new Promise((resolve, reject) => {
|
|
238
|
+
const request = https.get(url, (response) => {
|
|
239
|
+
const status = response.statusCode || 0;
|
|
240
|
+
if ([301, 302, 307, 308].includes(status)) {
|
|
241
|
+
if (redirectCount >= 5) {
|
|
242
|
+
reject(new Error(`Too many redirects for ${url}`));
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const location = response.headers.location;
|
|
246
|
+
if (!location) {
|
|
247
|
+
reject(new Error(`Redirect missing location for ${url}`));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
response.resume();
|
|
251
|
+
const nextUrl = new URL(location, url).toString();
|
|
252
|
+
resolve(fetchText(nextUrl, redirectCount + 1));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (status !== 200) {
|
|
257
|
+
let errorBody = "";
|
|
258
|
+
response.setEncoding("utf8");
|
|
259
|
+
response.on("data", (chunk) => {
|
|
260
|
+
errorBody += chunk;
|
|
261
|
+
});
|
|
262
|
+
response.on("end", () => {
|
|
263
|
+
reject(
|
|
264
|
+
new Error(
|
|
265
|
+
`Request failed (${status}) for ${url}${
|
|
266
|
+
errorBody ? `: ${errorBody.slice(0, 200)}` : ""
|
|
267
|
+
}`
|
|
268
|
+
)
|
|
269
|
+
);
|
|
270
|
+
});
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let body = "";
|
|
275
|
+
response.setEncoding("utf8");
|
|
276
|
+
response.on("data", (chunk) => {
|
|
277
|
+
body += chunk;
|
|
278
|
+
});
|
|
279
|
+
response.on("end", () => resolve(body));
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
request.on("error", reject);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function validateTools(tools) {
|
|
287
|
+
const valid = new Set(["claude", "copilot", "gemini", "cursor", "codex"]);
|
|
288
|
+
for (const tool of tools) {
|
|
289
|
+
if (!valid.has(tool)) {
|
|
290
|
+
throw new Error(
|
|
291
|
+
`Invalid tool "${tool}". Valid values: claude,copilot,gemini,cursor,codex`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function buildPlan({ target, tools, codexHome }) {
|
|
298
|
+
const plan = [];
|
|
299
|
+
for (const tool of tools) {
|
|
300
|
+
if (tool === "codex") {
|
|
301
|
+
for (const file of CODEX_TOOL_FILES) {
|
|
302
|
+
plan.push({
|
|
303
|
+
tool,
|
|
304
|
+
url: `${RAW_BASE}/${file.remote}`,
|
|
305
|
+
destination: path.resolve(codexHome, file.local),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
for (const file of PROJECT_TOOL_FILES[tool] || []) {
|
|
312
|
+
plan.push({
|
|
313
|
+
tool,
|
|
314
|
+
url: `${RAW_BASE}/${file.remote}`,
|
|
315
|
+
destination: path.resolve(target, file.local),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return plan;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async function writePlan(plan, dryRun) {
|
|
323
|
+
for (const item of plan) {
|
|
324
|
+
console.log(`[${item.tool}] ${item.url} -> ${item.destination}`);
|
|
325
|
+
if (dryRun) {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
const text = await fetchText(item.url);
|
|
329
|
+
await fs.promises.mkdir(path.dirname(item.destination), { recursive: true });
|
|
330
|
+
await fs.promises.writeFile(item.destination, text, "utf8");
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async function main() {
|
|
335
|
+
const options = parseArgs(process.argv);
|
|
336
|
+
|
|
337
|
+
if (options.action === "help") {
|
|
338
|
+
printHelp();
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (options.action === "list") {
|
|
342
|
+
printList();
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
validateTools(options.tools);
|
|
347
|
+
const plan = buildPlan(options);
|
|
348
|
+
if (!plan.length) {
|
|
349
|
+
throw new Error("No files selected to update.");
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
await writePlan(plan, options.dryRun);
|
|
353
|
+
|
|
354
|
+
if (options.dryRun) {
|
|
355
|
+
console.log(`Dry run complete. ${plan.length} files planned.`);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
console.log(`Updated ${plan.length} skills.`);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
main().catch((error) => {
|
|
362
|
+
console.error(`✖ Skills command failed: ${error.message || error}`);
|
|
363
|
+
process.exit(1);
|
|
364
|
+
});
|