@kurokeita/add-skill 1.9.0 → 1.10.1

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 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 path4 from "node:path";
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 fs2 from "fs-extra";
22
+ import fs4 from "fs-extra";
23
23
  import pc from "picocolors";
24
24
 
25
25
  // src/utils/github.ts
@@ -98,30 +98,47 @@ async function fetchSkillFromGitHub(url) {
98
98
  }
99
99
 
100
100
  // src/utils/paths.ts
101
+ import fs2 from "node:fs";
101
102
  import os2 from "node:os";
102
103
  import path2 from "node:path";
103
104
  import { fileURLToPath } from "node:url";
104
105
  var __filename = fileURLToPath(import.meta.url);
105
106
  var __dirname = path2.dirname(__filename);
106
- var PROJECT_ROOT = path2.resolve(__dirname, "../..");
107
+ function hasBundledAssets(rootDir) {
108
+ return ["skills", "agents", "workflows"].some(
109
+ (dir) => fs2.existsSync(path2.join(rootDir, dir))
110
+ );
111
+ }
112
+ function resolveProjectRoot(moduleDir) {
113
+ const sourceRoot = path2.resolve(moduleDir, "../..");
114
+ if (hasBundledAssets(sourceRoot)) {
115
+ return sourceRoot;
116
+ }
117
+ return path2.resolve(moduleDir, "..");
118
+ }
119
+ var PROJECT_ROOT = resolveProjectRoot(__dirname);
107
120
  var PLATFORM_LABELS = {
108
121
  antigravity: "Antigravity",
122
+ codex: "Codex",
109
123
  gemini: "Gemini CLI",
110
124
  copilot: "GitHub Copilot",
111
125
  windsurf: "Windsurf"
112
126
  };
113
127
  var PLATFORM_PATHS_SKILLS = {
114
128
  antigravity: path2.join(os2.homedir(), ".gemini/antigravity/global_skills"),
129
+ codex: path2.join(os2.homedir(), ".codex/skills"),
115
130
  copilot: path2.join(os2.homedir(), ".copilot/skills"),
116
131
  gemini: path2.join(os2.homedir(), ".gemini/skills"),
117
132
  windsurf: path2.join(os2.homedir(), ".codeium/windsurf/skills")
118
133
  };
119
134
  var PLATFORM_PATHS_AGENTS = {
135
+ codex: path2.join(os2.homedir(), ".codex/skills"),
120
136
  copilot: path2.join(os2.homedir(), ".copilot/agents"),
121
137
  gemini: path2.join(os2.homedir(), ".gemini/agents")
122
138
  };
123
139
  var PLATFORM_PATHS_WORKFLOWS = {
124
140
  antigravity: path2.join(os2.homedir(), ".gemini/antigravity/global_workflows"),
141
+ codex: path2.join(os2.homedir(), ".codex/skills"),
125
142
  copilot: path2.join(os2.homedir(), ".copilot/prompts"),
126
143
  gemini: path2.join(os2.homedir(), ".gemini/commands"),
127
144
  windsurf: path2.join(os2.homedir(), ".codeium/windsurf/global_workflows")
@@ -153,8 +170,118 @@ var AntigravityHandler = class {
153
170
  }
154
171
  };
155
172
 
156
- // src/utils/platforms/copilot.ts
173
+ // src/utils/platforms/codex.ts
174
+ import path3 from "node:path";
175
+ import fs3 from "fs-extra";
157
176
  import yaml from "js-yaml";
177
+ var frontmatterRegex = /^---\n([\s\S]*?)\n---/;
178
+ var CODEX_TYPE_FIELD = "x-ai-agents-type";
179
+ function extractTitle(body) {
180
+ const match = body.match(/^#\s+(.+)$/m);
181
+ return match?.[1]?.trim();
182
+ }
183
+ function buildDescription(type, itemName, title) {
184
+ const subject = title || itemName;
185
+ if (type === "agent") {
186
+ return `This skill should be used when Codex needs to follow the ${subject} agent instructions.`;
187
+ }
188
+ if (type === "workflow") {
189
+ return `This skill should be used when Codex needs to follow the ${subject} workflow instructions.`;
190
+ }
191
+ return `This skill should be used when Codex needs the ${subject} skill.`;
192
+ }
193
+ function toYamlFrontmatter(name, description, extraFields = {}) {
194
+ const normalizedName = name.replace(/\n+/g, " ").trim();
195
+ const normalizedDescription = description.replace(/\n+/g, " ").trim();
196
+ return yaml.dump(
197
+ {
198
+ name: normalizedName,
199
+ description: normalizedDescription,
200
+ ...extraFields
201
+ },
202
+ { lineWidth: -1 }
203
+ ).trimEnd();
204
+ }
205
+ function parseCodexType(content) {
206
+ const match = content.match(frontmatterRegex);
207
+ if (!match) return "skill";
208
+ try {
209
+ const parsed = yaml.load(match[1]);
210
+ if (parsed && typeof parsed === "object") {
211
+ const type = parsed[CODEX_TYPE_FIELD];
212
+ if (type === "agent" || type === "workflow" || type === "skill") {
213
+ return type;
214
+ }
215
+ }
216
+ } catch {
217
+ return "skill";
218
+ }
219
+ return "skill";
220
+ }
221
+ async function codexEntryMatchesType(basePath, entry, requestedType) {
222
+ let targetPath = null;
223
+ if (entry.isDirectory()) {
224
+ targetPath = path3.join(basePath, entry.name, "SKILL.md");
225
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
226
+ targetPath = path3.join(basePath, entry.name);
227
+ }
228
+ if (!targetPath) return false;
229
+ try {
230
+ const content = await fs3.readFile(targetPath, "utf-8");
231
+ return parseCodexType(content) === requestedType;
232
+ } catch {
233
+ return false;
234
+ }
235
+ }
236
+ var CodexHandler = class {
237
+ platform = "codex";
238
+ getTargetFileName(itemName, type) {
239
+ if (type === "agent" || type === "workflow") {
240
+ return path3.join(path3.parse(itemName).name, "SKILL.md");
241
+ }
242
+ return itemName;
243
+ }
244
+ transform(content, type, itemName) {
245
+ if (type === "skill") return content;
246
+ if (type !== "agent" && type !== "workflow") return content;
247
+ let name = itemName;
248
+ let description = "";
249
+ let body = content.trim();
250
+ const match = content.match(frontmatterRegex);
251
+ if (match) {
252
+ body = content.replace(frontmatterRegex, "").trim();
253
+ try {
254
+ const parsed = yaml.load(match[1]);
255
+ if (parsed && typeof parsed === "object") {
256
+ const frontmatter = parsed;
257
+ if (typeof frontmatter.name === "string" && frontmatter.name.trim()) {
258
+ name = frontmatter.name.trim();
259
+ }
260
+ if (typeof frontmatter.description === "string" && frontmatter.description.trim()) {
261
+ description = frontmatter.description.trim();
262
+ }
263
+ }
264
+ } catch {
265
+ name = itemName;
266
+ description = "";
267
+ }
268
+ }
269
+ if (!description) {
270
+ description = buildDescription(type, itemName, extractTitle(body));
271
+ }
272
+ const yamlStr = toYamlFrontmatter(name, description, {
273
+ [CODEX_TYPE_FIELD]: type
274
+ });
275
+ return `---
276
+ ${yamlStr}
277
+ ---
278
+
279
+ ${body}`;
280
+ }
281
+ };
282
+
283
+ // src/utils/platforms/copilot.ts
284
+ import yaml2 from "js-yaml";
158
285
  var CopilotHandler = class {
159
286
  platform = "copilot";
160
287
  getTargetFileName(itemName, type) {
@@ -165,8 +292,8 @@ var CopilotHandler = class {
165
292
  }
166
293
  transform(content, type, itemName) {
167
294
  if (type !== "agent") return content;
168
- const frontmatterRegex = /^---\n([\s\S]*?)\n---/;
169
- const match = content.match(frontmatterRegex);
295
+ const frontmatterRegex2 = /^---\n([\s\S]*?)\n---/;
296
+ const match = content.match(frontmatterRegex2);
170
297
  let frontmatter = {
171
298
  name: itemName,
172
299
  description: "",
@@ -176,7 +303,7 @@ var CopilotHandler = class {
176
303
  let body = content;
177
304
  if (match) {
178
305
  try {
179
- const parsed = yaml.load(match[1]);
306
+ const parsed = yaml2.load(match[1]);
180
307
  frontmatter = { ...frontmatter, ...parsed };
181
308
  frontmatter.target = "github-copilot";
182
309
  frontmatter.tools = [
@@ -186,11 +313,11 @@ var CopilotHandler = class {
186
313
  "grep_search",
187
314
  "githubRepo"
188
315
  ];
189
- body = content.replace(frontmatterRegex, "").trim();
316
+ body = content.replace(frontmatterRegex2, "").trim();
190
317
  } catch (_) {
191
318
  }
192
319
  }
193
- const yamlStr = yaml.dump(frontmatter);
320
+ const yamlStr = yaml2.dump(frontmatter);
194
321
  return `---
195
322
  ${yamlStr}---
196
323
 
@@ -212,7 +339,7 @@ var DefaultHandler = class {
212
339
  };
213
340
 
214
341
  // src/utils/platforms/gemini.ts
215
- import yaml2 from "js-yaml";
342
+ import yaml3 from "js-yaml";
216
343
 
217
344
  // src/utils/toml.ts
218
345
  function convertToGeminiCommandTOML(markdownContent) {
@@ -255,8 +382,8 @@ var GeminiHandler = class {
255
382
  return convertToGeminiCommandTOML(content);
256
383
  }
257
384
  if (type === "agent") {
258
- const frontmatterRegex = /^---\n([\s\S]*?)\n---/;
259
- const match = content.match(frontmatterRegex);
385
+ const frontmatterRegex2 = /^---\n([\s\S]*?)\n---/;
386
+ const match = content.match(frontmatterRegex2);
260
387
  let frontmatter = {
261
388
  name: itemName,
262
389
  description: "",
@@ -278,7 +405,7 @@ var GeminiHandler = class {
278
405
  let body = content;
279
406
  if (match) {
280
407
  try {
281
- const parsed = yaml2.load(match[1]);
408
+ const parsed = yaml3.load(match[1]);
282
409
  frontmatter = { ...frontmatter, ...parsed };
283
410
  frontmatter.tools = [
284
411
  "list_directory",
@@ -295,11 +422,11 @@ var GeminiHandler = class {
295
422
  "activate_skill"
296
423
  ];
297
424
  delete frontmatter.skills;
298
- body = content.replace(frontmatterRegex, "").trim();
425
+ body = content.replace(frontmatterRegex2, "").trim();
299
426
  } catch (_) {
300
427
  }
301
428
  }
302
- const yamlStr = yaml2.dump(frontmatter);
429
+ const yamlStr = yaml3.dump(frontmatter);
303
430
  return `---
304
431
  ${yamlStr}---
305
432
 
@@ -310,12 +437,12 @@ ${body}`;
310
437
  };
311
438
 
312
439
  // src/utils/platforms/windsurf.ts
313
- import path3 from "node:path";
440
+ import path4 from "node:path";
314
441
  var WindsurfHandler = class {
315
442
  platform = "windsurf";
316
443
  getTargetFileName(itemName, type) {
317
444
  if (type === "agent") {
318
- return path3.join(path3.parse(itemName).name, "AGENTS.md");
445
+ return path4.join(path4.parse(itemName).name, "AGENTS.md");
319
446
  }
320
447
  return itemName;
321
448
  }
@@ -330,6 +457,7 @@ function registerHandler(handler) {
330
457
  handlers.set(handler.platform, handler);
331
458
  }
332
459
  registerHandler(new AntigravityHandler());
460
+ registerHandler(new CodexHandler());
333
461
  registerHandler(new CopilotHandler());
334
462
  registerHandler(new GeminiHandler());
335
463
  registerHandler(new WindsurfHandler());
@@ -347,15 +475,15 @@ function getPlatformOptions(type) {
347
475
  }));
348
476
  }
349
477
  async function installItem(itemName, overwrite, sourcePath, targetBaseDir) {
350
- const targetPath = path4.join(targetBaseDir, itemName);
351
- if (!await fs2.pathExists(sourcePath)) {
478
+ const targetPath = path5.join(targetBaseDir, itemName);
479
+ if (!await fs4.pathExists(sourcePath)) {
352
480
  throw new Error(`Item '${itemName}' not found at ${sourcePath}`);
353
481
  }
354
- if (!overwrite && await fs2.pathExists(targetPath)) {
482
+ if (!overwrite && await fs4.pathExists(targetPath)) {
355
483
  return false;
356
484
  }
357
- await fs2.ensureDir(targetBaseDir);
358
- await fs2.copy(sourcePath, targetPath, { overwrite });
485
+ await fs4.ensureDir(targetBaseDir);
486
+ await fs4.copy(sourcePath, targetPath, { overwrite });
359
487
  return true;
360
488
  }
361
489
  async function add(type, url, options) {
@@ -403,12 +531,12 @@ async function add(type, url, options) {
403
531
  }
404
532
  } else {
405
533
  const sourceDir = TYPE_DIRS[normalizedType];
406
- if (!await fs2.pathExists(sourceDir)) {
534
+ if (!await fs4.pathExists(sourceDir)) {
407
535
  cancel(`${normalizedType} directory not found!`);
408
536
  process.exit(1);
409
537
  return;
410
538
  }
411
- const entries = await fs2.readdir(sourceDir, { withFileTypes: true });
539
+ const entries = await fs4.readdir(sourceDir, { withFileTypes: true });
412
540
  const availableItems = entries.filter(
413
541
  (entry) => entry.isDirectory() || (normalizedType === "workflow" || normalizedType === "agent") && entry.isFile() && entry.name.endsWith(".md")
414
542
  ).map((entry) => ({ label: entry.name, value: entry.name })).sort((a, b) => a.label.localeCompare(b.label));
@@ -443,7 +571,7 @@ async function add(type, url, options) {
443
571
  required: true
444
572
  });
445
573
  if (isCancel(platforms)) {
446
- if (tempDir) await fs2.remove(tempDir);
574
+ if (tempDir) await fs4.remove(tempDir);
447
575
  if (isSingleShot) break;
448
576
  currentType = void 0;
449
577
  continue;
@@ -453,9 +581,11 @@ async function add(type, url, options) {
453
581
  for (const platform of selectedPlatforms) {
454
582
  const targetBase = supportedPaths[platform];
455
583
  if (!targetBase) continue;
584
+ const handler = getHandler(platform);
456
585
  for (const item of selectedItems) {
457
- const targetPath = path4.join(targetBase, item);
458
- if (await fs2.pathExists(targetPath)) {
586
+ const targetItemName = handler.getTargetFileName(item, normalizedType);
587
+ const targetPath = path5.join(targetBase, targetItemName);
588
+ if (await fs4.pathExists(targetPath)) {
459
589
  existingItems.push({ item, platform });
460
590
  }
461
591
  }
@@ -472,7 +602,7 @@ async function add(type, url, options) {
472
602
  initialValue: false
473
603
  });
474
604
  if (isCancel(shouldOverwrite)) {
475
- if (tempDir) await fs2.remove(tempDir);
605
+ if (tempDir) await fs4.remove(tempDir);
476
606
  if (isSingleShot) break;
477
607
  currentType = void 0;
478
608
  continue;
@@ -497,15 +627,15 @@ async function add(type, url, options) {
497
627
  try {
498
628
  let currentSourcePath;
499
629
  if (url && tempDir) {
500
- const potentialFile = path4.join(tempDir, item);
501
- if (await fs2.pathExists(potentialFile) && (await fs2.stat(potentialFile)).isFile()) {
630
+ const potentialFile = path5.join(tempDir, item);
631
+ if (await fs4.pathExists(potentialFile) && (await fs4.stat(potentialFile)).isFile()) {
502
632
  currentSourcePath = potentialFile;
503
633
  } else {
504
634
  currentSourcePath = tempDir;
505
635
  }
506
636
  } else {
507
- const src = path4.join(TYPE_DIRS[normalizedType], item);
508
- if ((await fs2.stat(src)).isFile()) {
637
+ const src = path5.join(TYPE_DIRS[normalizedType], item);
638
+ if ((await fs4.stat(src)).isFile()) {
509
639
  currentSourcePath = src;
510
640
  } else {
511
641
  currentSourcePath = src;
@@ -517,18 +647,18 @@ async function add(type, url, options) {
517
647
  normalizedType
518
648
  );
519
649
  if ((normalizedType === "agent" || normalizedType === "workflow") && currentSourcePath.endsWith(".md")) {
520
- const targetPath = path4.join(targetBase, targetItemName);
521
- if (!overwrite && await fs2.pathExists(targetPath)) {
650
+ const targetPath = path5.join(targetBase, targetItemName);
651
+ if (!overwrite && await fs4.pathExists(targetPath)) {
522
652
  skippedCount++;
523
653
  } else {
524
- let content = await fs2.readFile(currentSourcePath, "utf-8");
654
+ let content = await fs4.readFile(currentSourcePath, "utf-8");
525
655
  content = handler.transform(
526
656
  content,
527
657
  normalizedType,
528
- path4.parse(item).name
658
+ path5.parse(item).name
529
659
  );
530
- await fs2.ensureDir(path4.dirname(targetPath));
531
- await fs2.writeFile(targetPath, content);
660
+ await fs4.ensureDir(path5.dirname(targetPath));
661
+ await fs4.writeFile(targetPath, content);
532
662
  installedCount++;
533
663
  }
534
664
  continue;
@@ -551,7 +681,7 @@ async function add(type, url, options) {
551
681
  }
552
682
  }
553
683
  if (tempDir) {
554
- await fs2.remove(tempDir);
684
+ await fs4.remove(tempDir);
555
685
  }
556
686
  if (errors.length > 0) {
557
687
  s.stop(
@@ -571,7 +701,7 @@ async function add(type, url, options) {
571
701
  );
572
702
  }
573
703
  } catch (error) {
574
- if (tempDir) await fs2.remove(tempDir);
704
+ if (tempDir) await fs4.remove(tempDir);
575
705
  cancel(`An error occurred: ${error}`);
576
706
  process.exit(1);
577
707
  return;
@@ -585,7 +715,7 @@ async function add(type, url, options) {
585
715
  }
586
716
 
587
717
  // src/commands/import.ts
588
- import path5 from "node:path";
718
+ import path6 from "node:path";
589
719
  import {
590
720
  cancel as cancel2,
591
721
  confirm as confirm2,
@@ -596,7 +726,7 @@ import {
596
726
  spinner as spinner2,
597
727
  text
598
728
  } from "@clack/prompts";
599
- import fs3 from "fs-extra";
729
+ import fs5 from "fs-extra";
600
730
  import pc2 from "picocolors";
601
731
  async function importItem(type, url, options) {
602
732
  if (!options?.skipIntro) {
@@ -654,12 +784,12 @@ async function importItem(type, url, options) {
654
784
  throw e;
655
785
  }
656
786
  const shouldFlatten = (normalizedType === "agent" || normalizedType === "workflow") && !isFile;
657
- const targetPath = shouldFlatten ? targetBaseDir : path5.join(targetBaseDir, itemName);
787
+ const targetPath = shouldFlatten ? targetBaseDir : path6.join(targetBaseDir, itemName);
658
788
  if (shouldFlatten) {
659
- const srcFiles = await fs3.readdir(tempDir);
789
+ const srcFiles = await fs5.readdir(tempDir);
660
790
  const conflicts = [];
661
791
  for (const file of srcFiles) {
662
- if (await fs3.pathExists(path5.join(targetBaseDir, file))) {
792
+ if (await fs5.pathExists(path6.join(targetBaseDir, file))) {
663
793
  conflicts.push(file);
664
794
  }
665
795
  }
@@ -673,40 +803,40 @@ async function importItem(type, url, options) {
673
803
  initialValue: false
674
804
  });
675
805
  if (isCancel2(shouldOverwrite) || !shouldOverwrite) {
676
- await fs3.remove(tempDir);
806
+ await fs5.remove(tempDir);
677
807
  return;
678
808
  }
679
809
  }
680
- } else if (await fs3.pathExists(targetPath)) {
810
+ } else if (await fs5.pathExists(targetPath)) {
681
811
  const shouldOverwrite = await confirm2({
682
812
  message: `${normalizedType} '${itemName}' already exists in the repo. Overwrite?`,
683
813
  initialValue: false
684
814
  });
685
815
  if (isCancel2(shouldOverwrite) || !shouldOverwrite) {
686
- await fs3.remove(tempDir);
816
+ await fs5.remove(tempDir);
687
817
  return;
688
818
  }
689
819
  }
690
820
  s.start(`Importing ${itemName} to ${targetBaseDir}...`);
691
- await fs3.ensureDir(targetBaseDir);
821
+ await fs5.ensureDir(targetBaseDir);
692
822
  if (isFile) {
693
- await fs3.copy(path5.join(tempDir, itemName), targetPath, {
823
+ await fs5.copy(path6.join(tempDir, itemName), targetPath, {
694
824
  overwrite: true
695
825
  });
696
826
  } else {
697
827
  if (shouldFlatten) {
698
- await fs3.copy(tempDir, targetBaseDir, { overwrite: true });
828
+ await fs5.copy(tempDir, targetBaseDir, { overwrite: true });
699
829
  } else {
700
- await fs3.copy(tempDir, targetPath, { overwrite: true });
830
+ await fs5.copy(tempDir, targetPath, { overwrite: true });
701
831
  }
702
832
  }
703
833
  s.stop(pc2.green(`Successfully imported ${itemName}!`));
704
- await fs3.remove(tempDir);
834
+ await fs5.remove(tempDir);
705
835
  if (!options?.skipIntro) {
706
836
  outro2(`${normalizedType} available at: ${targetPath}`);
707
837
  }
708
838
  } catch (error) {
709
- if (tempDir) await fs3.remove(tempDir);
839
+ if (tempDir) await fs5.remove(tempDir);
710
840
  cancel2(`An error occurred: ${error}`);
711
841
  process.exit(1);
712
842
  return;
@@ -715,7 +845,7 @@ async function importItem(type, url, options) {
715
845
 
716
846
  // src/commands/list.ts
717
847
  import { intro as intro3, isCancel as isCancel3, outro as outro3, select as select3 } from "@clack/prompts";
718
- import fs4 from "fs-extra";
848
+ import fs6 from "fs-extra";
719
849
  import pc3 from "picocolors";
720
850
  async function performListing(type, local) {
721
851
  const normalizedType = type.toLowerCase().endsWith("s") ? type.slice(0, -1) : type.toLowerCase();
@@ -725,13 +855,23 @@ async function performListing(type, local) {
725
855
  let foundAny = false;
726
856
  for (const [platform, pathStr] of Object.entries(targetPaths)) {
727
857
  const fullPath = pathStr;
728
- if (await fs4.pathExists(fullPath)) {
729
- const entries = await fs4.readdir(fullPath, {
858
+ if (await fs6.pathExists(fullPath)) {
859
+ const entries = await fs6.readdir(fullPath, {
730
860
  withFileTypes: true
731
861
  });
732
- const items = entries.filter(
733
- (entry) => entry.isDirectory() || entry.isFile() && entry.name.endsWith(".md")
734
- ).map((entry) => entry.name).sort();
862
+ const items = [];
863
+ for (const entry of entries) {
864
+ if (platform === "codex") {
865
+ if (await codexEntryMatchesType(fullPath, entry, normalizedType)) {
866
+ items.push(entry.name);
867
+ }
868
+ continue;
869
+ }
870
+ if (entry.isDirectory() || entry.isFile() && entry.name.endsWith(".md")) {
871
+ items.push(entry.name);
872
+ }
873
+ }
874
+ items.sort();
735
875
  if (items.length > 0) {
736
876
  foundAny = true;
737
877
  console.log(pc3.cyan(` ${platform}:`));
@@ -755,13 +895,13 @@ async function performListing(type, local) {
755
895
  return;
756
896
  }
757
897
  const dirPath = TYPE_DIRS[normalizedType];
758
- if (!await fs4.pathExists(dirPath)) {
898
+ if (!await fs6.pathExists(dirPath)) {
759
899
  console.log(
760
900
  pc3.yellow(`No ${normalizedType} directory found at: ${dirPath}`)
761
901
  );
762
902
  return;
763
903
  }
764
- const entries = await fs4.readdir(dirPath, { withFileTypes: true });
904
+ const entries = await fs6.readdir(dirPath, { withFileTypes: true });
765
905
  const items = entries.filter(
766
906
  (entry) => entry.isDirectory() || normalizedType !== "skill" && entry.isFile() && entry.name.endsWith(".md")
767
907
  ).map((entry) => entry.name).sort();
@@ -825,7 +965,7 @@ async function list(type, options) {
825
965
  }
826
966
 
827
967
  // src/commands/remove.ts
828
- import path6 from "node:path";
968
+ import path7 from "node:path";
829
969
  import {
830
970
  confirm as confirm3,
831
971
  intro as intro4,
@@ -836,7 +976,7 @@ import {
836
976
  select as select4,
837
977
  spinner as spinner3
838
978
  } from "@clack/prompts";
839
- import fs5 from "fs-extra";
979
+ import fs7 from "fs-extra";
840
980
  import pc4 from "picocolors";
841
981
  async function remove(type, options) {
842
982
  if (!options?.skipIntro) {
@@ -865,12 +1005,22 @@ async function remove(type, options) {
865
1005
  const uniqueItems = /* @__PURE__ */ new Set();
866
1006
  const s = spinner3();
867
1007
  s.start("Scanning for installed items...");
868
- for (const pathStr of Object.values(targetPaths)) {
869
- if (await fs5.pathExists(pathStr)) {
870
- const entries = await fs5.readdir(pathStr, {
1008
+ for (const [platform, pathStr] of Object.entries(targetPaths)) {
1009
+ if (await fs7.pathExists(pathStr)) {
1010
+ const entries = await fs7.readdir(pathStr, {
871
1011
  withFileTypes: true
872
1012
  });
873
1013
  for (const entry of entries) {
1014
+ if (platform === "codex") {
1015
+ if (await codexEntryMatchesType(
1016
+ pathStr,
1017
+ entry,
1018
+ normalizedType
1019
+ )) {
1020
+ uniqueItems.add(entry.name);
1021
+ }
1022
+ continue;
1023
+ }
874
1024
  if (entry.isDirectory() || normalizedType === "workflow" && entry.isFile() && entry.name.endsWith(".md") || normalizedType === "agent" && entry.isFile() && entry.name.endsWith(".md")) {
875
1025
  uniqueItems.add(entry.name);
876
1026
  }
@@ -929,11 +1079,11 @@ async function remove(type, options) {
929
1079
  const targetBase = targetPaths[platform];
930
1080
  if (!targetBase) continue;
931
1081
  for (const item of selectedItems) {
932
- const targetPath = path6.join(targetBase, item);
933
- if (await fs5.pathExists(targetPath)) {
1082
+ const targetPath = path7.join(targetBase, item);
1083
+ if (await fs7.pathExists(targetPath)) {
934
1084
  sDel.message(`Removing ${pc4.bold(item)} from ${pc4.cyan(platform)}...`);
935
1085
  try {
936
- await fs5.remove(targetPath);
1086
+ await fs7.remove(targetPath);
937
1087
  removedCount++;
938
1088
  } catch (err) {
939
1089
  const msg = err instanceof Error ? err.message : String(err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kurokeita/add-skill",
3
- "version": "1.9.0",
3
+ "version": "1.10.1",
4
4
  "description": "CLI to install AI agent skills to various platforms",
5
5
  "type": "module",
6
6
  "bin": {