@kurokeita/add-skill 1.9.0 → 1.10.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/README.md +3 -0
- package/dist/bin/cli.js +206 -69
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -87,11 +87,14 @@ pnpm dev import https://github.com/owner/repo/tree/main/skills/skill-name
|
|
|
87
87
|
| Platform | Agents Path | Skills Path | Workflows Path |
|
|
88
88
|
| :--- | :--- | :--- | :--- |
|
|
89
89
|
| Antigravity | *Not Supported* | `~/.gemini/antigravity/global_skills` | `~/.gemini/antigravity/global_workflows` |
|
|
90
|
+
| Codex | `~/.codex/skills` | `~/.codex/skills` | `~/.codex/skills` |
|
|
90
91
|
| Gemini CLI | `~/.gemini/agents` | `~/.gemini/skills` | `~/.gemini/commands` |
|
|
91
92
|
| GitHub Copilot | `~/.copilot/agents` | `~/.copilot/skills` | `~/.copilot/prompts` |
|
|
92
93
|
| Windsurf | *Not Supported* | `~/.codeium/windsurf/skills` | `~/.codeium/windsurf/global_workflows` |
|
|
93
94
|
<!-- SUPPORTED_AGENTS_END -->
|
|
94
95
|
|
|
96
|
+
For Codex, this package installs skills directly and converts agents and workflows into Codex skill packages under `~/.codex/skills`.
|
|
97
|
+
|
|
95
98
|
## Development
|
|
96
99
|
|
|
97
100
|
1. **Clone the repository:**
|
package/dist/bin/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import pc5 from "picocolors";
|
|
|
7
7
|
|
|
8
8
|
// src/commands/add.ts
|
|
9
9
|
import os3 from "node:os";
|
|
10
|
-
import
|
|
10
|
+
import path5 from "node:path";
|
|
11
11
|
import {
|
|
12
12
|
cancel,
|
|
13
13
|
confirm,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
select,
|
|
20
20
|
spinner
|
|
21
21
|
} from "@clack/prompts";
|
|
22
|
-
import
|
|
22
|
+
import fs3 from "fs-extra";
|
|
23
23
|
import pc from "picocolors";
|
|
24
24
|
|
|
25
25
|
// src/utils/github.ts
|
|
@@ -106,22 +106,26 @@ var __dirname = path2.dirname(__filename);
|
|
|
106
106
|
var PROJECT_ROOT = path2.resolve(__dirname, "../..");
|
|
107
107
|
var PLATFORM_LABELS = {
|
|
108
108
|
antigravity: "Antigravity",
|
|
109
|
+
codex: "Codex",
|
|
109
110
|
gemini: "Gemini CLI",
|
|
110
111
|
copilot: "GitHub Copilot",
|
|
111
112
|
windsurf: "Windsurf"
|
|
112
113
|
};
|
|
113
114
|
var PLATFORM_PATHS_SKILLS = {
|
|
114
115
|
antigravity: path2.join(os2.homedir(), ".gemini/antigravity/global_skills"),
|
|
116
|
+
codex: path2.join(os2.homedir(), ".codex/skills"),
|
|
115
117
|
copilot: path2.join(os2.homedir(), ".copilot/skills"),
|
|
116
118
|
gemini: path2.join(os2.homedir(), ".gemini/skills"),
|
|
117
119
|
windsurf: path2.join(os2.homedir(), ".codeium/windsurf/skills")
|
|
118
120
|
};
|
|
119
121
|
var PLATFORM_PATHS_AGENTS = {
|
|
122
|
+
codex: path2.join(os2.homedir(), ".codex/skills"),
|
|
120
123
|
copilot: path2.join(os2.homedir(), ".copilot/agents"),
|
|
121
124
|
gemini: path2.join(os2.homedir(), ".gemini/agents")
|
|
122
125
|
};
|
|
123
126
|
var PLATFORM_PATHS_WORKFLOWS = {
|
|
124
127
|
antigravity: path2.join(os2.homedir(), ".gemini/antigravity/global_workflows"),
|
|
128
|
+
codex: path2.join(os2.homedir(), ".codex/skills"),
|
|
125
129
|
copilot: path2.join(os2.homedir(), ".copilot/prompts"),
|
|
126
130
|
gemini: path2.join(os2.homedir(), ".gemini/commands"),
|
|
127
131
|
windsurf: path2.join(os2.homedir(), ".codeium/windsurf/global_workflows")
|
|
@@ -153,8 +157,118 @@ var AntigravityHandler = class {
|
|
|
153
157
|
}
|
|
154
158
|
};
|
|
155
159
|
|
|
156
|
-
// src/utils/platforms/
|
|
160
|
+
// src/utils/platforms/codex.ts
|
|
161
|
+
import path3 from "node:path";
|
|
162
|
+
import fs2 from "fs-extra";
|
|
157
163
|
import yaml from "js-yaml";
|
|
164
|
+
var frontmatterRegex = /^---\n([\s\S]*?)\n---/;
|
|
165
|
+
var CODEX_TYPE_FIELD = "x-ai-agents-type";
|
|
166
|
+
function extractTitle(body) {
|
|
167
|
+
const match = body.match(/^#\s+(.+)$/m);
|
|
168
|
+
return match?.[1]?.trim();
|
|
169
|
+
}
|
|
170
|
+
function buildDescription(type, itemName, title) {
|
|
171
|
+
const subject = title || itemName;
|
|
172
|
+
if (type === "agent") {
|
|
173
|
+
return `This skill should be used when Codex needs to follow the ${subject} agent instructions.`;
|
|
174
|
+
}
|
|
175
|
+
if (type === "workflow") {
|
|
176
|
+
return `This skill should be used when Codex needs to follow the ${subject} workflow instructions.`;
|
|
177
|
+
}
|
|
178
|
+
return `This skill should be used when Codex needs the ${subject} skill.`;
|
|
179
|
+
}
|
|
180
|
+
function toYamlFrontmatter(name, description, extraFields = {}) {
|
|
181
|
+
const normalizedName = name.replace(/\n+/g, " ").trim();
|
|
182
|
+
const normalizedDescription = description.replace(/\n+/g, " ").trim();
|
|
183
|
+
return yaml.dump(
|
|
184
|
+
{
|
|
185
|
+
name: normalizedName,
|
|
186
|
+
description: normalizedDescription,
|
|
187
|
+
...extraFields
|
|
188
|
+
},
|
|
189
|
+
{ lineWidth: -1 }
|
|
190
|
+
).trimEnd();
|
|
191
|
+
}
|
|
192
|
+
function parseCodexType(content) {
|
|
193
|
+
const match = content.match(frontmatterRegex);
|
|
194
|
+
if (!match) return "skill";
|
|
195
|
+
try {
|
|
196
|
+
const parsed = yaml.load(match[1]);
|
|
197
|
+
if (parsed && typeof parsed === "object") {
|
|
198
|
+
const type = parsed[CODEX_TYPE_FIELD];
|
|
199
|
+
if (type === "agent" || type === "workflow" || type === "skill") {
|
|
200
|
+
return type;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch {
|
|
204
|
+
return "skill";
|
|
205
|
+
}
|
|
206
|
+
return "skill";
|
|
207
|
+
}
|
|
208
|
+
async function codexEntryMatchesType(basePath, entry, requestedType) {
|
|
209
|
+
let targetPath = null;
|
|
210
|
+
if (entry.isDirectory()) {
|
|
211
|
+
targetPath = path3.join(basePath, entry.name, "SKILL.md");
|
|
212
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
213
|
+
targetPath = path3.join(basePath, entry.name);
|
|
214
|
+
}
|
|
215
|
+
if (!targetPath) return false;
|
|
216
|
+
try {
|
|
217
|
+
const content = await fs2.readFile(targetPath, "utf-8");
|
|
218
|
+
return parseCodexType(content) === requestedType;
|
|
219
|
+
} catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
var CodexHandler = class {
|
|
224
|
+
platform = "codex";
|
|
225
|
+
getTargetFileName(itemName, type) {
|
|
226
|
+
if (type === "agent" || type === "workflow") {
|
|
227
|
+
return path3.join(path3.parse(itemName).name, "SKILL.md");
|
|
228
|
+
}
|
|
229
|
+
return itemName;
|
|
230
|
+
}
|
|
231
|
+
transform(content, type, itemName) {
|
|
232
|
+
if (type === "skill") return content;
|
|
233
|
+
if (type !== "agent" && type !== "workflow") return content;
|
|
234
|
+
let name = itemName;
|
|
235
|
+
let description = "";
|
|
236
|
+
let body = content.trim();
|
|
237
|
+
const match = content.match(frontmatterRegex);
|
|
238
|
+
if (match) {
|
|
239
|
+
body = content.replace(frontmatterRegex, "").trim();
|
|
240
|
+
try {
|
|
241
|
+
const parsed = yaml.load(match[1]);
|
|
242
|
+
if (parsed && typeof parsed === "object") {
|
|
243
|
+
const frontmatter = parsed;
|
|
244
|
+
if (typeof frontmatter.name === "string" && frontmatter.name.trim()) {
|
|
245
|
+
name = frontmatter.name.trim();
|
|
246
|
+
}
|
|
247
|
+
if (typeof frontmatter.description === "string" && frontmatter.description.trim()) {
|
|
248
|
+
description = frontmatter.description.trim();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
252
|
+
name = itemName;
|
|
253
|
+
description = "";
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (!description) {
|
|
257
|
+
description = buildDescription(type, itemName, extractTitle(body));
|
|
258
|
+
}
|
|
259
|
+
const yamlStr = toYamlFrontmatter(name, description, {
|
|
260
|
+
[CODEX_TYPE_FIELD]: type
|
|
261
|
+
});
|
|
262
|
+
return `---
|
|
263
|
+
${yamlStr}
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
${body}`;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// src/utils/platforms/copilot.ts
|
|
271
|
+
import yaml2 from "js-yaml";
|
|
158
272
|
var CopilotHandler = class {
|
|
159
273
|
platform = "copilot";
|
|
160
274
|
getTargetFileName(itemName, type) {
|
|
@@ -165,8 +279,8 @@ var CopilotHandler = class {
|
|
|
165
279
|
}
|
|
166
280
|
transform(content, type, itemName) {
|
|
167
281
|
if (type !== "agent") return content;
|
|
168
|
-
const
|
|
169
|
-
const match = content.match(
|
|
282
|
+
const frontmatterRegex2 = /^---\n([\s\S]*?)\n---/;
|
|
283
|
+
const match = content.match(frontmatterRegex2);
|
|
170
284
|
let frontmatter = {
|
|
171
285
|
name: itemName,
|
|
172
286
|
description: "",
|
|
@@ -176,7 +290,7 @@ var CopilotHandler = class {
|
|
|
176
290
|
let body = content;
|
|
177
291
|
if (match) {
|
|
178
292
|
try {
|
|
179
|
-
const parsed =
|
|
293
|
+
const parsed = yaml2.load(match[1]);
|
|
180
294
|
frontmatter = { ...frontmatter, ...parsed };
|
|
181
295
|
frontmatter.target = "github-copilot";
|
|
182
296
|
frontmatter.tools = [
|
|
@@ -186,11 +300,11 @@ var CopilotHandler = class {
|
|
|
186
300
|
"grep_search",
|
|
187
301
|
"githubRepo"
|
|
188
302
|
];
|
|
189
|
-
body = content.replace(
|
|
303
|
+
body = content.replace(frontmatterRegex2, "").trim();
|
|
190
304
|
} catch (_) {
|
|
191
305
|
}
|
|
192
306
|
}
|
|
193
|
-
const yamlStr =
|
|
307
|
+
const yamlStr = yaml2.dump(frontmatter);
|
|
194
308
|
return `---
|
|
195
309
|
${yamlStr}---
|
|
196
310
|
|
|
@@ -212,7 +326,7 @@ var DefaultHandler = class {
|
|
|
212
326
|
};
|
|
213
327
|
|
|
214
328
|
// src/utils/platforms/gemini.ts
|
|
215
|
-
import
|
|
329
|
+
import yaml3 from "js-yaml";
|
|
216
330
|
|
|
217
331
|
// src/utils/toml.ts
|
|
218
332
|
function convertToGeminiCommandTOML(markdownContent) {
|
|
@@ -255,8 +369,8 @@ var GeminiHandler = class {
|
|
|
255
369
|
return convertToGeminiCommandTOML(content);
|
|
256
370
|
}
|
|
257
371
|
if (type === "agent") {
|
|
258
|
-
const
|
|
259
|
-
const match = content.match(
|
|
372
|
+
const frontmatterRegex2 = /^---\n([\s\S]*?)\n---/;
|
|
373
|
+
const match = content.match(frontmatterRegex2);
|
|
260
374
|
let frontmatter = {
|
|
261
375
|
name: itemName,
|
|
262
376
|
description: "",
|
|
@@ -278,7 +392,7 @@ var GeminiHandler = class {
|
|
|
278
392
|
let body = content;
|
|
279
393
|
if (match) {
|
|
280
394
|
try {
|
|
281
|
-
const parsed =
|
|
395
|
+
const parsed = yaml3.load(match[1]);
|
|
282
396
|
frontmatter = { ...frontmatter, ...parsed };
|
|
283
397
|
frontmatter.tools = [
|
|
284
398
|
"list_directory",
|
|
@@ -295,11 +409,11 @@ var GeminiHandler = class {
|
|
|
295
409
|
"activate_skill"
|
|
296
410
|
];
|
|
297
411
|
delete frontmatter.skills;
|
|
298
|
-
body = content.replace(
|
|
412
|
+
body = content.replace(frontmatterRegex2, "").trim();
|
|
299
413
|
} catch (_) {
|
|
300
414
|
}
|
|
301
415
|
}
|
|
302
|
-
const yamlStr =
|
|
416
|
+
const yamlStr = yaml3.dump(frontmatter);
|
|
303
417
|
return `---
|
|
304
418
|
${yamlStr}---
|
|
305
419
|
|
|
@@ -310,12 +424,12 @@ ${body}`;
|
|
|
310
424
|
};
|
|
311
425
|
|
|
312
426
|
// src/utils/platforms/windsurf.ts
|
|
313
|
-
import
|
|
427
|
+
import path4 from "node:path";
|
|
314
428
|
var WindsurfHandler = class {
|
|
315
429
|
platform = "windsurf";
|
|
316
430
|
getTargetFileName(itemName, type) {
|
|
317
431
|
if (type === "agent") {
|
|
318
|
-
return
|
|
432
|
+
return path4.join(path4.parse(itemName).name, "AGENTS.md");
|
|
319
433
|
}
|
|
320
434
|
return itemName;
|
|
321
435
|
}
|
|
@@ -330,6 +444,7 @@ function registerHandler(handler) {
|
|
|
330
444
|
handlers.set(handler.platform, handler);
|
|
331
445
|
}
|
|
332
446
|
registerHandler(new AntigravityHandler());
|
|
447
|
+
registerHandler(new CodexHandler());
|
|
333
448
|
registerHandler(new CopilotHandler());
|
|
334
449
|
registerHandler(new GeminiHandler());
|
|
335
450
|
registerHandler(new WindsurfHandler());
|
|
@@ -347,15 +462,15 @@ function getPlatformOptions(type) {
|
|
|
347
462
|
}));
|
|
348
463
|
}
|
|
349
464
|
async function installItem(itemName, overwrite, sourcePath, targetBaseDir) {
|
|
350
|
-
const targetPath =
|
|
351
|
-
if (!await
|
|
465
|
+
const targetPath = path5.join(targetBaseDir, itemName);
|
|
466
|
+
if (!await fs3.pathExists(sourcePath)) {
|
|
352
467
|
throw new Error(`Item '${itemName}' not found at ${sourcePath}`);
|
|
353
468
|
}
|
|
354
|
-
if (!overwrite && await
|
|
469
|
+
if (!overwrite && await fs3.pathExists(targetPath)) {
|
|
355
470
|
return false;
|
|
356
471
|
}
|
|
357
|
-
await
|
|
358
|
-
await
|
|
472
|
+
await fs3.ensureDir(targetBaseDir);
|
|
473
|
+
await fs3.copy(sourcePath, targetPath, { overwrite });
|
|
359
474
|
return true;
|
|
360
475
|
}
|
|
361
476
|
async function add(type, url, options) {
|
|
@@ -403,12 +518,12 @@ async function add(type, url, options) {
|
|
|
403
518
|
}
|
|
404
519
|
} else {
|
|
405
520
|
const sourceDir = TYPE_DIRS[normalizedType];
|
|
406
|
-
if (!await
|
|
521
|
+
if (!await fs3.pathExists(sourceDir)) {
|
|
407
522
|
cancel(`${normalizedType} directory not found!`);
|
|
408
523
|
process.exit(1);
|
|
409
524
|
return;
|
|
410
525
|
}
|
|
411
|
-
const entries = await
|
|
526
|
+
const entries = await fs3.readdir(sourceDir, { withFileTypes: true });
|
|
412
527
|
const availableItems = entries.filter(
|
|
413
528
|
(entry) => entry.isDirectory() || (normalizedType === "workflow" || normalizedType === "agent") && entry.isFile() && entry.name.endsWith(".md")
|
|
414
529
|
).map((entry) => ({ label: entry.name, value: entry.name })).sort((a, b) => a.label.localeCompare(b.label));
|
|
@@ -443,7 +558,7 @@ async function add(type, url, options) {
|
|
|
443
558
|
required: true
|
|
444
559
|
});
|
|
445
560
|
if (isCancel(platforms)) {
|
|
446
|
-
if (tempDir) await
|
|
561
|
+
if (tempDir) await fs3.remove(tempDir);
|
|
447
562
|
if (isSingleShot) break;
|
|
448
563
|
currentType = void 0;
|
|
449
564
|
continue;
|
|
@@ -453,9 +568,11 @@ async function add(type, url, options) {
|
|
|
453
568
|
for (const platform of selectedPlatforms) {
|
|
454
569
|
const targetBase = supportedPaths[platform];
|
|
455
570
|
if (!targetBase) continue;
|
|
571
|
+
const handler = getHandler(platform);
|
|
456
572
|
for (const item of selectedItems) {
|
|
457
|
-
const
|
|
458
|
-
|
|
573
|
+
const targetItemName = handler.getTargetFileName(item, normalizedType);
|
|
574
|
+
const targetPath = path5.join(targetBase, targetItemName);
|
|
575
|
+
if (await fs3.pathExists(targetPath)) {
|
|
459
576
|
existingItems.push({ item, platform });
|
|
460
577
|
}
|
|
461
578
|
}
|
|
@@ -472,7 +589,7 @@ async function add(type, url, options) {
|
|
|
472
589
|
initialValue: false
|
|
473
590
|
});
|
|
474
591
|
if (isCancel(shouldOverwrite)) {
|
|
475
|
-
if (tempDir) await
|
|
592
|
+
if (tempDir) await fs3.remove(tempDir);
|
|
476
593
|
if (isSingleShot) break;
|
|
477
594
|
currentType = void 0;
|
|
478
595
|
continue;
|
|
@@ -497,15 +614,15 @@ async function add(type, url, options) {
|
|
|
497
614
|
try {
|
|
498
615
|
let currentSourcePath;
|
|
499
616
|
if (url && tempDir) {
|
|
500
|
-
const potentialFile =
|
|
501
|
-
if (await
|
|
617
|
+
const potentialFile = path5.join(tempDir, item);
|
|
618
|
+
if (await fs3.pathExists(potentialFile) && (await fs3.stat(potentialFile)).isFile()) {
|
|
502
619
|
currentSourcePath = potentialFile;
|
|
503
620
|
} else {
|
|
504
621
|
currentSourcePath = tempDir;
|
|
505
622
|
}
|
|
506
623
|
} else {
|
|
507
|
-
const src =
|
|
508
|
-
if ((await
|
|
624
|
+
const src = path5.join(TYPE_DIRS[normalizedType], item);
|
|
625
|
+
if ((await fs3.stat(src)).isFile()) {
|
|
509
626
|
currentSourcePath = src;
|
|
510
627
|
} else {
|
|
511
628
|
currentSourcePath = src;
|
|
@@ -517,18 +634,18 @@ async function add(type, url, options) {
|
|
|
517
634
|
normalizedType
|
|
518
635
|
);
|
|
519
636
|
if ((normalizedType === "agent" || normalizedType === "workflow") && currentSourcePath.endsWith(".md")) {
|
|
520
|
-
const targetPath =
|
|
521
|
-
if (!overwrite && await
|
|
637
|
+
const targetPath = path5.join(targetBase, targetItemName);
|
|
638
|
+
if (!overwrite && await fs3.pathExists(targetPath)) {
|
|
522
639
|
skippedCount++;
|
|
523
640
|
} else {
|
|
524
|
-
let content = await
|
|
641
|
+
let content = await fs3.readFile(currentSourcePath, "utf-8");
|
|
525
642
|
content = handler.transform(
|
|
526
643
|
content,
|
|
527
644
|
normalizedType,
|
|
528
|
-
|
|
645
|
+
path5.parse(item).name
|
|
529
646
|
);
|
|
530
|
-
await
|
|
531
|
-
await
|
|
647
|
+
await fs3.ensureDir(path5.dirname(targetPath));
|
|
648
|
+
await fs3.writeFile(targetPath, content);
|
|
532
649
|
installedCount++;
|
|
533
650
|
}
|
|
534
651
|
continue;
|
|
@@ -551,7 +668,7 @@ async function add(type, url, options) {
|
|
|
551
668
|
}
|
|
552
669
|
}
|
|
553
670
|
if (tempDir) {
|
|
554
|
-
await
|
|
671
|
+
await fs3.remove(tempDir);
|
|
555
672
|
}
|
|
556
673
|
if (errors.length > 0) {
|
|
557
674
|
s.stop(
|
|
@@ -571,7 +688,7 @@ async function add(type, url, options) {
|
|
|
571
688
|
);
|
|
572
689
|
}
|
|
573
690
|
} catch (error) {
|
|
574
|
-
if (tempDir) await
|
|
691
|
+
if (tempDir) await fs3.remove(tempDir);
|
|
575
692
|
cancel(`An error occurred: ${error}`);
|
|
576
693
|
process.exit(1);
|
|
577
694
|
return;
|
|
@@ -585,7 +702,7 @@ async function add(type, url, options) {
|
|
|
585
702
|
}
|
|
586
703
|
|
|
587
704
|
// src/commands/import.ts
|
|
588
|
-
import
|
|
705
|
+
import path6 from "node:path";
|
|
589
706
|
import {
|
|
590
707
|
cancel as cancel2,
|
|
591
708
|
confirm as confirm2,
|
|
@@ -596,7 +713,7 @@ import {
|
|
|
596
713
|
spinner as spinner2,
|
|
597
714
|
text
|
|
598
715
|
} from "@clack/prompts";
|
|
599
|
-
import
|
|
716
|
+
import fs4 from "fs-extra";
|
|
600
717
|
import pc2 from "picocolors";
|
|
601
718
|
async function importItem(type, url, options) {
|
|
602
719
|
if (!options?.skipIntro) {
|
|
@@ -654,12 +771,12 @@ async function importItem(type, url, options) {
|
|
|
654
771
|
throw e;
|
|
655
772
|
}
|
|
656
773
|
const shouldFlatten = (normalizedType === "agent" || normalizedType === "workflow") && !isFile;
|
|
657
|
-
const targetPath = shouldFlatten ? targetBaseDir :
|
|
774
|
+
const targetPath = shouldFlatten ? targetBaseDir : path6.join(targetBaseDir, itemName);
|
|
658
775
|
if (shouldFlatten) {
|
|
659
|
-
const srcFiles = await
|
|
776
|
+
const srcFiles = await fs4.readdir(tempDir);
|
|
660
777
|
const conflicts = [];
|
|
661
778
|
for (const file of srcFiles) {
|
|
662
|
-
if (await
|
|
779
|
+
if (await fs4.pathExists(path6.join(targetBaseDir, file))) {
|
|
663
780
|
conflicts.push(file);
|
|
664
781
|
}
|
|
665
782
|
}
|
|
@@ -673,40 +790,40 @@ async function importItem(type, url, options) {
|
|
|
673
790
|
initialValue: false
|
|
674
791
|
});
|
|
675
792
|
if (isCancel2(shouldOverwrite) || !shouldOverwrite) {
|
|
676
|
-
await
|
|
793
|
+
await fs4.remove(tempDir);
|
|
677
794
|
return;
|
|
678
795
|
}
|
|
679
796
|
}
|
|
680
|
-
} else if (await
|
|
797
|
+
} else if (await fs4.pathExists(targetPath)) {
|
|
681
798
|
const shouldOverwrite = await confirm2({
|
|
682
799
|
message: `${normalizedType} '${itemName}' already exists in the repo. Overwrite?`,
|
|
683
800
|
initialValue: false
|
|
684
801
|
});
|
|
685
802
|
if (isCancel2(shouldOverwrite) || !shouldOverwrite) {
|
|
686
|
-
await
|
|
803
|
+
await fs4.remove(tempDir);
|
|
687
804
|
return;
|
|
688
805
|
}
|
|
689
806
|
}
|
|
690
807
|
s.start(`Importing ${itemName} to ${targetBaseDir}...`);
|
|
691
|
-
await
|
|
808
|
+
await fs4.ensureDir(targetBaseDir);
|
|
692
809
|
if (isFile) {
|
|
693
|
-
await
|
|
810
|
+
await fs4.copy(path6.join(tempDir, itemName), targetPath, {
|
|
694
811
|
overwrite: true
|
|
695
812
|
});
|
|
696
813
|
} else {
|
|
697
814
|
if (shouldFlatten) {
|
|
698
|
-
await
|
|
815
|
+
await fs4.copy(tempDir, targetBaseDir, { overwrite: true });
|
|
699
816
|
} else {
|
|
700
|
-
await
|
|
817
|
+
await fs4.copy(tempDir, targetPath, { overwrite: true });
|
|
701
818
|
}
|
|
702
819
|
}
|
|
703
820
|
s.stop(pc2.green(`Successfully imported ${itemName}!`));
|
|
704
|
-
await
|
|
821
|
+
await fs4.remove(tempDir);
|
|
705
822
|
if (!options?.skipIntro) {
|
|
706
823
|
outro2(`${normalizedType} available at: ${targetPath}`);
|
|
707
824
|
}
|
|
708
825
|
} catch (error) {
|
|
709
|
-
if (tempDir) await
|
|
826
|
+
if (tempDir) await fs4.remove(tempDir);
|
|
710
827
|
cancel2(`An error occurred: ${error}`);
|
|
711
828
|
process.exit(1);
|
|
712
829
|
return;
|
|
@@ -715,7 +832,7 @@ async function importItem(type, url, options) {
|
|
|
715
832
|
|
|
716
833
|
// src/commands/list.ts
|
|
717
834
|
import { intro as intro3, isCancel as isCancel3, outro as outro3, select as select3 } from "@clack/prompts";
|
|
718
|
-
import
|
|
835
|
+
import fs5 from "fs-extra";
|
|
719
836
|
import pc3 from "picocolors";
|
|
720
837
|
async function performListing(type, local) {
|
|
721
838
|
const normalizedType = type.toLowerCase().endsWith("s") ? type.slice(0, -1) : type.toLowerCase();
|
|
@@ -725,13 +842,23 @@ async function performListing(type, local) {
|
|
|
725
842
|
let foundAny = false;
|
|
726
843
|
for (const [platform, pathStr] of Object.entries(targetPaths)) {
|
|
727
844
|
const fullPath = pathStr;
|
|
728
|
-
if (await
|
|
729
|
-
const entries = await
|
|
845
|
+
if (await fs5.pathExists(fullPath)) {
|
|
846
|
+
const entries = await fs5.readdir(fullPath, {
|
|
730
847
|
withFileTypes: true
|
|
731
848
|
});
|
|
732
|
-
const items =
|
|
733
|
-
|
|
734
|
-
|
|
849
|
+
const items = [];
|
|
850
|
+
for (const entry of entries) {
|
|
851
|
+
if (platform === "codex") {
|
|
852
|
+
if (await codexEntryMatchesType(fullPath, entry, normalizedType)) {
|
|
853
|
+
items.push(entry.name);
|
|
854
|
+
}
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
if (entry.isDirectory() || entry.isFile() && entry.name.endsWith(".md")) {
|
|
858
|
+
items.push(entry.name);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
items.sort();
|
|
735
862
|
if (items.length > 0) {
|
|
736
863
|
foundAny = true;
|
|
737
864
|
console.log(pc3.cyan(` ${platform}:`));
|
|
@@ -755,13 +882,13 @@ async function performListing(type, local) {
|
|
|
755
882
|
return;
|
|
756
883
|
}
|
|
757
884
|
const dirPath = TYPE_DIRS[normalizedType];
|
|
758
|
-
if (!await
|
|
885
|
+
if (!await fs5.pathExists(dirPath)) {
|
|
759
886
|
console.log(
|
|
760
887
|
pc3.yellow(`No ${normalizedType} directory found at: ${dirPath}`)
|
|
761
888
|
);
|
|
762
889
|
return;
|
|
763
890
|
}
|
|
764
|
-
const entries = await
|
|
891
|
+
const entries = await fs5.readdir(dirPath, { withFileTypes: true });
|
|
765
892
|
const items = entries.filter(
|
|
766
893
|
(entry) => entry.isDirectory() || normalizedType !== "skill" && entry.isFile() && entry.name.endsWith(".md")
|
|
767
894
|
).map((entry) => entry.name).sort();
|
|
@@ -825,7 +952,7 @@ async function list(type, options) {
|
|
|
825
952
|
}
|
|
826
953
|
|
|
827
954
|
// src/commands/remove.ts
|
|
828
|
-
import
|
|
955
|
+
import path7 from "node:path";
|
|
829
956
|
import {
|
|
830
957
|
confirm as confirm3,
|
|
831
958
|
intro as intro4,
|
|
@@ -836,7 +963,7 @@ import {
|
|
|
836
963
|
select as select4,
|
|
837
964
|
spinner as spinner3
|
|
838
965
|
} from "@clack/prompts";
|
|
839
|
-
import
|
|
966
|
+
import fs6 from "fs-extra";
|
|
840
967
|
import pc4 from "picocolors";
|
|
841
968
|
async function remove(type, options) {
|
|
842
969
|
if (!options?.skipIntro) {
|
|
@@ -865,12 +992,22 @@ async function remove(type, options) {
|
|
|
865
992
|
const uniqueItems = /* @__PURE__ */ new Set();
|
|
866
993
|
const s = spinner3();
|
|
867
994
|
s.start("Scanning for installed items...");
|
|
868
|
-
for (const pathStr of Object.
|
|
869
|
-
if (await
|
|
870
|
-
const entries = await
|
|
995
|
+
for (const [platform, pathStr] of Object.entries(targetPaths)) {
|
|
996
|
+
if (await fs6.pathExists(pathStr)) {
|
|
997
|
+
const entries = await fs6.readdir(pathStr, {
|
|
871
998
|
withFileTypes: true
|
|
872
999
|
});
|
|
873
1000
|
for (const entry of entries) {
|
|
1001
|
+
if (platform === "codex") {
|
|
1002
|
+
if (await codexEntryMatchesType(
|
|
1003
|
+
pathStr,
|
|
1004
|
+
entry,
|
|
1005
|
+
normalizedType
|
|
1006
|
+
)) {
|
|
1007
|
+
uniqueItems.add(entry.name);
|
|
1008
|
+
}
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
874
1011
|
if (entry.isDirectory() || normalizedType === "workflow" && entry.isFile() && entry.name.endsWith(".md") || normalizedType === "agent" && entry.isFile() && entry.name.endsWith(".md")) {
|
|
875
1012
|
uniqueItems.add(entry.name);
|
|
876
1013
|
}
|
|
@@ -929,11 +1066,11 @@ async function remove(type, options) {
|
|
|
929
1066
|
const targetBase = targetPaths[platform];
|
|
930
1067
|
if (!targetBase) continue;
|
|
931
1068
|
for (const item of selectedItems) {
|
|
932
|
-
const targetPath =
|
|
933
|
-
if (await
|
|
1069
|
+
const targetPath = path7.join(targetBase, item);
|
|
1070
|
+
if (await fs6.pathExists(targetPath)) {
|
|
934
1071
|
sDel.message(`Removing ${pc4.bold(item)} from ${pc4.cyan(platform)}...`);
|
|
935
1072
|
try {
|
|
936
|
-
await
|
|
1073
|
+
await fs6.remove(targetPath);
|
|
937
1074
|
removedCount++;
|
|
938
1075
|
} catch (err) {
|
|
939
1076
|
const msg = err instanceof Error ? err.message : String(err);
|