cursor-kit-cli 1.2.0-beta → 1.2.0-beta.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.
Files changed (91) hide show
  1. package/dist/cli.cjs +333 -56
  2. package/dist/cli.cjs.map +1 -1
  3. package/dist/cli.js +334 -57
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.cjs +39 -1
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +9 -1
  8. package/dist/index.d.ts +9 -1
  9. package/dist/index.js +33 -2
  10. package/dist/index.js.map +1 -1
  11. package/package.json +1 -1
  12. package/templates/commands/docs.md +5 -3
  13. package/templates/commands/explain.md +5 -3
  14. package/templates/commands/fix.md +5 -3
  15. package/templates/commands/implement.md +5 -3
  16. package/templates/commands/refactor.md +5 -3
  17. package/templates/commands/review.md +5 -3
  18. package/templates/commands/test.md +5 -3
  19. package/templates/manifest.json +11 -8
  20. package/templates/rules/git.mdc +0 -2
  21. package/templates/rules/toc.mdc +17 -9
  22. package/templates/skills/aesthetic/SKILL.md +121 -0
  23. package/templates/skills/aesthetic/assets/design-guideline-template.md +163 -0
  24. package/templates/skills/aesthetic/assets/design-story-template.md +135 -0
  25. package/templates/skills/aesthetic/references/design-principles.md +62 -0
  26. package/templates/skills/aesthetic/references/design-resources.md +75 -0
  27. package/templates/skills/aesthetic/references/micro-interactions.md +53 -0
  28. package/templates/skills/aesthetic/references/storytelling-design.md +50 -0
  29. package/templates/skills/backend-development/SKILL.mdc +95 -0
  30. package/templates/skills/backend-development/references/backend-api-design.md +495 -0
  31. package/templates/skills/backend-development/references/backend-architecture.md +454 -0
  32. package/templates/skills/backend-development/references/backend-authentication.md +338 -0
  33. package/templates/skills/backend-development/references/backend-code-quality.md +659 -0
  34. package/templates/skills/backend-development/references/backend-debugging.md +904 -0
  35. package/templates/skills/backend-development/references/backend-devops.md +494 -0
  36. package/templates/skills/backend-development/references/backend-mindset.md +387 -0
  37. package/templates/skills/backend-development/references/backend-performance.md +397 -0
  38. package/templates/skills/backend-development/references/backend-security.md +290 -0
  39. package/templates/skills/backend-development/references/backend-technologies.md +256 -0
  40. package/templates/skills/backend-development/references/backend-testing.md +429 -0
  41. package/templates/skills/frontend-design/SKILL.mdc +41 -0
  42. package/templates/skills/frontend-design/references/animejs.md +396 -0
  43. package/templates/skills/frontend-development/SKILL.mdc +399 -0
  44. package/templates/skills/frontend-development/resources/common-patterns.md +331 -0
  45. package/templates/skills/frontend-development/resources/complete-examples.md +872 -0
  46. package/templates/skills/frontend-development/resources/component-patterns.md +502 -0
  47. package/templates/skills/frontend-development/resources/data-fetching.md +767 -0
  48. package/templates/skills/frontend-development/resources/file-organization.md +502 -0
  49. package/templates/skills/frontend-development/resources/loading-and-error-states.md +501 -0
  50. package/templates/skills/frontend-development/resources/performance.md +406 -0
  51. package/templates/skills/frontend-development/resources/routing-guide.md +364 -0
  52. package/templates/skills/frontend-development/resources/styling-guide.md +428 -0
  53. package/templates/skills/frontend-development/resources/typescript-standards.md +418 -0
  54. package/templates/skills/problem-solving/SKILL.mdc +96 -0
  55. package/templates/skills/problem-solving/references/attribution.md +69 -0
  56. package/templates/skills/problem-solving/references/collision-zone-thinking.md +79 -0
  57. package/templates/skills/problem-solving/references/inversion-exercise.md +91 -0
  58. package/templates/skills/problem-solving/references/meta-pattern-recognition.md +87 -0
  59. package/templates/skills/problem-solving/references/scale-game.md +95 -0
  60. package/templates/skills/problem-solving/references/simplification-cascades.md +80 -0
  61. package/templates/skills/problem-solving/references/when-stuck.md +72 -0
  62. package/templates/skills/research/SKILL.mdc +168 -0
  63. package/templates/skills/sequential-thinking/.env.example +8 -0
  64. package/templates/skills/sequential-thinking/README.md +183 -0
  65. package/templates/skills/sequential-thinking/SKILL.mdc +94 -0
  66. package/templates/skills/sequential-thinking/package.json +31 -0
  67. package/templates/skills/sequential-thinking/references/advanced-strategies.md +79 -0
  68. package/templates/skills/sequential-thinking/references/advanced-techniques.md +76 -0
  69. package/templates/skills/sequential-thinking/references/core-patterns.md +95 -0
  70. package/templates/skills/sequential-thinking/references/examples-api.md +88 -0
  71. package/templates/skills/sequential-thinking/references/examples-architecture.md +94 -0
  72. package/templates/skills/sequential-thinking/references/examples-debug.md +90 -0
  73. package/templates/skills/sequential-thinking/scripts/format-thought.js +159 -0
  74. package/templates/skills/sequential-thinking/scripts/process-thought.js +236 -0
  75. package/templates/skills/sequential-thinking/tests/format-thought.test.js +133 -0
  76. package/templates/skills/sequential-thinking/tests/process-thought.test.js +215 -0
  77. package/templates/skills/ui-styling/LICENSE.txt +202 -0
  78. package/templates/skills/ui-styling/SKILL.mdc +321 -0
  79. package/templates/skills/ui-styling/references/canvas-design-system.md +320 -0
  80. package/templates/skills/ui-styling/references/shadcn-accessibility.md +471 -0
  81. package/templates/skills/ui-styling/references/shadcn-components.md +424 -0
  82. package/templates/skills/ui-styling/references/shadcn-theming.md +373 -0
  83. package/templates/skills/ui-styling/references/tailwind-customization.md +483 -0
  84. package/templates/skills/ui-styling/references/tailwind-responsive.md +382 -0
  85. package/templates/skills/ui-styling/references/tailwind-utilities.md +455 -0
  86. package/templates/rules/frontend-design.mdc +0 -48
  87. package/templates/rules/performance.mdc +0 -54
  88. package/templates/rules/react.mdc +0 -58
  89. package/templates/rules/security.mdc +0 -50
  90. package/templates/rules/testing.mdc +0 -54
  91. package/templates/rules/typescript.mdc +0 -36
package/dist/cli.js CHANGED
@@ -50,7 +50,7 @@ import pc2 from "picocolors";
50
50
  import { join as join3 } from "path";
51
51
 
52
52
  // src/utils/fs.ts
53
- import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, rmSync, statSync } from "fs";
53
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, rmSync, statSync, cpSync } from "fs";
54
54
  import { dirname, join, resolve } from "path";
55
55
  function ensureDir(path) {
56
56
  if (!existsSync(path)) {
@@ -75,6 +75,9 @@ function removeFile(path) {
75
75
  rmSync(path, { recursive: true });
76
76
  }
77
77
  }
78
+ function copyDir(src, dest) {
79
+ cpSync(src, dest, { recursive: true });
80
+ }
78
81
  function listFiles(dir, extension) {
79
82
  if (!dirExists(dir)) return [];
80
83
  const files = readdirSync(dir);
@@ -83,6 +86,13 @@ function listFiles(dir, extension) {
83
86
  }
84
87
  return files;
85
88
  }
89
+ function listDirs(dir) {
90
+ if (!dirExists(dir)) return [];
91
+ return readdirSync(dir).filter((item) => {
92
+ const itemPath = join(dir, item);
93
+ return statSync(itemPath).isDirectory();
94
+ });
95
+ }
86
96
  function getCursorDir(cwd = process.cwd()) {
87
97
  return join(cwd, ".cursor");
88
98
  }
@@ -92,10 +102,17 @@ function getCommandsDir(cwd = process.cwd()) {
92
102
  function getRulesDir(cwd = process.cwd()) {
93
103
  return join(getCursorDir(cwd), "rules");
94
104
  }
105
+ function getSkillsDir(cwd = process.cwd()) {
106
+ return join(getCursorDir(cwd), "skills");
107
+ }
95
108
  function getConflictingFiles(dir, files) {
96
109
  if (!dirExists(dir)) return [];
97
110
  return files.filter((file) => fileExists(join(dir, file)));
98
111
  }
112
+ function getConflictingDirs(dir, dirs) {
113
+ if (!dirExists(dir)) return [];
114
+ return dirs.filter((d) => dirExists(join(dir, d)));
115
+ }
99
116
 
100
117
  // src/utils/templates.ts
101
118
  import { join as join2, dirname as dirname2 } from "path";
@@ -107,7 +124,8 @@ var REPO_REF = "master";
107
124
  var REPO_RAW_URL = "https://raw.githubusercontent.com/duongductrong/cursor-kit/master";
108
125
  var TEMPLATE_PATHS = {
109
126
  commands: "templates/commands",
110
- rules: "templates/rules"
127
+ rules: "templates/rules",
128
+ skills: "templates/skills"
111
129
  };
112
130
 
113
131
  // src/utils/templates.ts
@@ -127,12 +145,14 @@ function getLocalManifest() {
127
145
  }
128
146
  const commandsDir = join2(templatesDir, "commands");
129
147
  const rulesDir = join2(templatesDir, "rules");
130
- if (!dirExists(commandsDir) && !dirExists(rulesDir)) {
148
+ const skillsDir = join2(templatesDir, "skills");
149
+ if (!dirExists(commandsDir) && !dirExists(rulesDir) && !dirExists(skillsDir)) {
131
150
  return null;
132
151
  }
133
152
  return {
134
153
  commands: dirExists(commandsDir) ? listFiles(commandsDir, ".md") : [],
135
- rules: dirExists(rulesDir) ? listFiles(rulesDir, ".mdc") : []
154
+ rules: dirExists(rulesDir) ? listFiles(rulesDir, ".mdc") : [],
155
+ skills: dirExists(skillsDir) ? listDirs(skillsDir) : []
136
156
  };
137
157
  }
138
158
  function getLocalTemplateContent(type, filename) {
@@ -143,6 +163,22 @@ function getLocalTemplateContent(type, filename) {
143
163
  }
144
164
  return null;
145
165
  }
166
+ function getLocalSkillDir(skillName) {
167
+ const templatesDir = getLocalTemplatesDir();
168
+ const skillPath = join2(templatesDir, "skills", skillName);
169
+ if (dirExists(skillPath)) {
170
+ return skillPath;
171
+ }
172
+ return null;
173
+ }
174
+ function copyLocalSkill(skillName, targetDir) {
175
+ const sourcePath = getLocalSkillDir(skillName);
176
+ if (!sourcePath) return false;
177
+ const destPath = join2(targetDir, skillName);
178
+ ensureDir(destPath);
179
+ copyDir(sourcePath, destPath);
180
+ return true;
181
+ }
146
182
  async function fetchTemplateManifest() {
147
183
  const localManifest = getLocalManifest();
148
184
  if (localManifest) {
@@ -186,9 +222,13 @@ function getTemplateLabel(filename) {
186
222
  const nameWithoutExt = filename.replace(/\.(md|mdc)$/, "");
187
223
  return nameWithoutExt.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
188
224
  }
225
+ function getSkillLabel(skillName) {
226
+ return skillName.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
227
+ }
189
228
 
190
229
  // src/commands/init.ts
191
230
  async function selectTemplates(type, availableTemplates) {
231
+ const labelFn = type === "skills" ? getSkillLabel : getTemplateLabel;
192
232
  const selectionMode = await p.select({
193
233
  message: `How would you like to add ${type}?`,
194
234
  options: [
@@ -212,7 +252,7 @@ async function selectTemplates(type, availableTemplates) {
212
252
  message: `Select ${type} to add:`,
213
253
  options: availableTemplates.map((template) => ({
214
254
  value: template,
215
- label: getTemplateLabel(template),
255
+ label: labelFn(template),
216
256
  hint: template
217
257
  })),
218
258
  required: true
@@ -276,10 +316,36 @@ async function installTemplates(type, targetDir, selectedTemplates, conflictStra
276
316
  }
277
317
  return result;
278
318
  }
319
+ async function installSkills(targetDir, selectedSkills, conflictStrategy) {
320
+ const result = { added: [], skipped: [] };
321
+ const conflictingDirs = getConflictingDirs(targetDir, selectedSkills);
322
+ let skillsToInstall;
323
+ if (conflictStrategy === "merge") {
324
+ skillsToInstall = selectedSkills.filter(
325
+ (s) => !conflictingDirs.includes(s)
326
+ );
327
+ result.skipped = conflictingDirs.filter(
328
+ (d) => selectedSkills.includes(d)
329
+ );
330
+ } else {
331
+ skillsToInstall = selectedSkills;
332
+ }
333
+ if (skillsToInstall.length === 0) {
334
+ return result;
335
+ }
336
+ ensureDir(targetDir);
337
+ for (const skillName of skillsToInstall) {
338
+ const success = copyLocalSkill(skillName, targetDir);
339
+ if (success) {
340
+ result.added.push(skillName);
341
+ }
342
+ }
343
+ return result;
344
+ }
279
345
  var initCommand = defineCommand({
280
346
  meta: {
281
347
  name: "init",
282
- description: "Initialize .cursor/commands and .cursor/rules in your project"
348
+ description: "Initialize .cursor/commands, .cursor/rules, and .cursor/skills in your project"
283
349
  },
284
350
  args: {
285
351
  force: {
@@ -300,6 +366,12 @@ var initCommand = defineCommand({
300
366
  description: "Only initialize rules",
301
367
  default: false
302
368
  },
369
+ skills: {
370
+ type: "boolean",
371
+ alias: "s",
372
+ description: "Only initialize skills",
373
+ default: false
374
+ },
303
375
  all: {
304
376
  type: "boolean",
305
377
  alias: "a",
@@ -312,9 +384,11 @@ var initCommand = defineCommand({
312
384
  const cursorDir = getCursorDir(cwd);
313
385
  const commandsDir = getCommandsDir(cwd);
314
386
  const rulesDir = getRulesDir(cwd);
315
- const initBoth = !args.commands && !args.rules;
316
- const shouldInitCommands = initBoth || args.commands;
317
- const shouldInitRules = initBoth || args.rules;
387
+ const skillsDir = getSkillsDir(cwd);
388
+ const initAll = !args.commands && !args.rules && !args.skills;
389
+ const shouldInitCommands = initAll || args.commands;
390
+ const shouldInitRules = initAll || args.rules;
391
+ const shouldInitSkills = initAll || args.skills;
318
392
  p.intro(pc2.bgCyan(pc2.black(" cursor-kit init ")));
319
393
  const s = p.spinner();
320
394
  let manifest;
@@ -397,6 +471,36 @@ var initCommand = defineCommand({
397
471
  );
398
472
  s.stop("Rules installed");
399
473
  }
474
+ if (shouldInitSkills) {
475
+ let selectedSkills;
476
+ if (args.all) {
477
+ selectedSkills = manifest.skills;
478
+ } else {
479
+ const selection = await selectTemplates("skills", manifest.skills);
480
+ if (p.isCancel(selection)) {
481
+ p.cancel("Operation cancelled");
482
+ process.exit(0);
483
+ }
484
+ selectedSkills = selection;
485
+ }
486
+ const conflictingSkills = getConflictingDirs(skillsDir, selectedSkills);
487
+ let skillStrategy = "overwrite";
488
+ if (conflictingSkills.length > 0 && !args.force) {
489
+ const strategy = await handleConflicts("skills", conflictingSkills);
490
+ if (p.isCancel(strategy) || strategy === "cancel") {
491
+ p.cancel("Operation cancelled");
492
+ process.exit(0);
493
+ }
494
+ skillStrategy = strategy;
495
+ }
496
+ s.start("Installing skills...");
497
+ results.skills = await installSkills(
498
+ skillsDir,
499
+ selectedSkills,
500
+ skillStrategy
501
+ );
502
+ s.stop("Skills installed");
503
+ }
400
504
  printDivider();
401
505
  console.log();
402
506
  if (results.commands) {
@@ -427,8 +531,22 @@ var initCommand = defineCommand({
427
531
  }
428
532
  }
429
533
  }
430
- const totalAdded = (results.commands?.added.length ?? 0) + (results.rules?.added.length ?? 0);
431
- const totalSkipped = (results.commands?.skipped.length ?? 0) + (results.rules?.skipped.length ?? 0);
534
+ if (results.skills) {
535
+ const { added, skipped } = results.skills;
536
+ if (added.length > 0 || skipped.length > 0) {
537
+ printSuccess(
538
+ `Skills: ${highlight(added.length.toString())} added${skipped.length > 0 ? `, ${pc2.yellow(skipped.length.toString())} skipped` : ""}`
539
+ );
540
+ for (const f of added) {
541
+ console.log(pc2.dim(` \u2514\u2500 ${pc2.green("+")} ${f}`));
542
+ }
543
+ for (const f of skipped) {
544
+ console.log(pc2.dim(` \u2514\u2500 ${pc2.yellow("\u25CB")} ${f} (kept existing)`));
545
+ }
546
+ }
547
+ }
548
+ const totalAdded = (results.commands?.added.length ?? 0) + (results.rules?.added.length ?? 0) + (results.skills?.added.length ?? 0);
549
+ const totalSkipped = (results.commands?.skipped.length ?? 0) + (results.rules?.skipped.length ?? 0) + (results.skills?.skipped.length ?? 0);
432
550
  if (totalAdded === 0 && totalSkipped > 0) {
433
551
  console.log();
434
552
  p.outro(pc2.yellow("No new templates added (all selected files already exist)"));
@@ -447,10 +565,10 @@ var initCommand = defineCommand({
447
565
  });
448
566
 
449
567
  // src/commands/add.ts
450
- import { defineCommand as defineCommand2 } from "citty";
451
568
  import * as p2 from "@clack/prompts";
452
- import pc3 from "picocolors";
569
+ import { defineCommand as defineCommand2 } from "citty";
453
570
  import { join as join4 } from "path";
571
+ import pc3 from "picocolors";
454
572
  var COMMAND_TEMPLATE = `You are a helpful assistant. Describe what this command does.
455
573
 
456
574
  ## Instructions
@@ -477,31 +595,69 @@ Describe the rule behavior here.
477
595
  - Guideline 1
478
596
  - Guideline 2
479
597
  `;
598
+ var SKILL_TEMPLATE = `---
599
+ description: Describe when this skill should be activated
600
+ globs:
601
+ alwaysApply: false
602
+ ---
603
+
604
+ # Skill Name
605
+
606
+ Brief description of what this skill enables.
607
+
608
+ ## Core Capabilities
609
+
610
+ - Capability 1
611
+ - Capability 2
612
+
613
+ ## When to Use
614
+
615
+ Use this skill when:
616
+ - Condition 1
617
+ - Condition 2
618
+
619
+ ## References
620
+
621
+ For detailed guidance, see the references folder:
622
+ - [Reference 1](./references/example.md) - Description
623
+ `;
624
+ var SKILL_REFERENCE_TEMPLATE = `# Reference Title
625
+
626
+ Detailed reference content goes here.
627
+
628
+ ## Section 1
629
+
630
+ Content...
631
+
632
+ ## Section 2
633
+
634
+ Content...
635
+ `;
480
636
  function generateSlug(name) {
481
637
  return name.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim();
482
638
  }
483
639
  var addCommand = defineCommand2({
484
640
  meta: {
485
641
  name: "add",
486
- description: "Add a new command or rule"
642
+ description: "Add a new command, rule, or skill"
487
643
  },
488
644
  args: {
489
645
  type: {
490
646
  type: "string",
491
647
  alias: "t",
492
- description: "Type: 'command' or 'rule'"
648
+ description: "Type: 'command', 'rule', or 'skill'"
493
649
  },
494
650
  name: {
495
651
  type: "string",
496
652
  alias: "n",
497
- description: "Name of the command or rule"
653
+ description: "Name of the command, rule, or skill"
498
654
  }
499
655
  },
500
656
  async run({ args }) {
501
657
  p2.intro(pc3.bgCyan(pc3.black(" cursor-kit add ")));
502
658
  let itemType;
503
659
  let itemName;
504
- if (args.type && ["command", "rule"].includes(args.type)) {
660
+ if (args.type && ["command", "rule", "skill"].includes(args.type)) {
505
661
  itemType = args.type;
506
662
  } else {
507
663
  const typeResult = await p2.select({
@@ -516,6 +672,11 @@ var addCommand = defineCommand2({
516
672
  value: "rule",
517
673
  label: "Rule",
518
674
  hint: "Project-specific AI behavior rules"
675
+ },
676
+ {
677
+ value: "skill",
678
+ label: "Skill",
679
+ hint: "Comprehensive guide with references"
519
680
  }
520
681
  ]
521
682
  });
@@ -530,7 +691,7 @@ var addCommand = defineCommand2({
530
691
  } else {
531
692
  const nameResult = await p2.text({
532
693
  message: `Enter ${itemType} name:`,
533
- placeholder: itemType === "command" ? "my-command" : "my-rule",
694
+ placeholder: itemType === "command" ? "my-command" : itemType === "rule" ? "my-rule" : "my-skill",
534
695
  validate: (value) => {
535
696
  if (!value.trim()) return "Name is required";
536
697
  if (value.length < 2) return "Name must be at least 2 characters";
@@ -545,12 +706,22 @@ var addCommand = defineCommand2({
545
706
  }
546
707
  const slug = generateSlug(itemName);
547
708
  const isCommand = itemType === "command";
548
- const targetDir = isCommand ? getCommandsDir() : getRulesDir();
549
- const extension = isCommand ? ".md" : ".mdc";
550
- const filePath = join4(targetDir, `${slug}${extension}`);
551
- if (fileExists(filePath)) {
709
+ const isSkill = itemType === "skill";
710
+ let targetPath;
711
+ let displayPath;
712
+ if (isSkill) {
713
+ const skillsDir = getSkillsDir();
714
+ targetPath = join4(skillsDir, slug);
715
+ displayPath = targetPath;
716
+ } else {
717
+ const targetDir = isCommand ? getCommandsDir() : getRulesDir();
718
+ const extension = isCommand ? ".md" : ".mdc";
719
+ targetPath = join4(targetDir, `${slug}${extension}`);
720
+ displayPath = targetPath;
721
+ }
722
+ if (isSkill ? dirExists(targetPath) : fileExists(targetPath)) {
552
723
  const shouldOverwrite = await p2.confirm({
553
- message: `${highlight(slug + extension)} already exists. Overwrite?`,
724
+ message: `${highlight(isSkill ? slug : slug + (isCommand ? ".md" : ".mdc"))} already exists. Overwrite?`,
554
725
  initialValue: false
555
726
  });
556
727
  if (p2.isCancel(shouldOverwrite) || !shouldOverwrite) {
@@ -561,12 +732,25 @@ var addCommand = defineCommand2({
561
732
  const s = p2.spinner();
562
733
  s.start(`Creating ${itemType}...`);
563
734
  try {
564
- ensureDir(targetDir);
565
- const template = isCommand ? COMMAND_TEMPLATE : RULE_TEMPLATE;
566
- writeFile(filePath, template);
735
+ if (isSkill) {
736
+ ensureDir(targetPath);
737
+ ensureDir(join4(targetPath, "references"));
738
+ writeFile(join4(targetPath, "SKILL.mdc"), SKILL_TEMPLATE);
739
+ writeFile(join4(targetPath, "references", "example.md"), SKILL_REFERENCE_TEMPLATE);
740
+ } else {
741
+ const targetDir = isCommand ? getCommandsDir() : getRulesDir();
742
+ ensureDir(targetDir);
743
+ const template = isCommand ? COMMAND_TEMPLATE : RULE_TEMPLATE;
744
+ writeFile(targetPath, template);
745
+ }
567
746
  s.stop(`${itemType.charAt(0).toUpperCase() + itemType.slice(1)} created`);
568
747
  console.log();
569
- console.log(pc3.dim(" File: ") + highlight(filePath));
748
+ if (isSkill) {
749
+ console.log(pc3.dim(" Directory: ") + highlight(displayPath));
750
+ console.log(pc3.dim(" Main file: ") + highlight(join4(displayPath, "SKILL.mdc")));
751
+ } else {
752
+ console.log(pc3.dim(" File: ") + highlight(displayPath));
753
+ }
570
754
  console.log();
571
755
  p2.outro(
572
756
  pc3.green(`\u2728 ${itemType.charAt(0).toUpperCase() + itemType.slice(1)} created! Edit the file to customize it.`)
@@ -602,6 +786,12 @@ var pullCommand = defineCommand3({
602
786
  description: "Only pull rules",
603
787
  default: false
604
788
  },
789
+ skills: {
790
+ type: "boolean",
791
+ alias: "s",
792
+ description: "Only pull skills",
793
+ default: false
794
+ },
605
795
  force: {
606
796
  type: "boolean",
607
797
  alias: "f",
@@ -610,15 +800,18 @@ var pullCommand = defineCommand3({
610
800
  }
611
801
  },
612
802
  async run({ args }) {
613
- const pullBoth = !args.commands && !args.rules;
614
- const shouldPullCommands = pullBoth || args.commands;
615
- const shouldPullRules = pullBoth || args.rules;
803
+ const pullAll = !args.commands && !args.rules && !args.skills;
804
+ const shouldPullCommands = pullAll || args.commands;
805
+ const shouldPullRules = pullAll || args.rules;
806
+ const shouldPullSkills = pullAll || args.skills;
616
807
  p3.intro(pc4.bgCyan(pc4.black(" cursor-kit pull ")));
617
808
  const commandsDir = getCommandsDir();
618
809
  const rulesDir = getRulesDir();
810
+ const skillsDir = getSkillsDir();
619
811
  const existingCommands = listFiles(commandsDir, ".md");
620
812
  const existingRules = listFiles(rulesDir, ".mdc");
621
- const hasExisting = existingCommands.length > 0 || existingRules.length > 0;
813
+ const existingSkills = listDirs(skillsDir);
814
+ const hasExisting = existingCommands.length > 0 || existingRules.length > 0 || existingSkills.length > 0;
622
815
  if (hasExisting && !args.force) {
623
816
  printInfo("Current status:");
624
817
  if (existingCommands.length > 0) {
@@ -627,6 +820,9 @@ var pullCommand = defineCommand3({
627
820
  if (existingRules.length > 0) {
628
821
  console.log(pc4.dim(` Rules: ${existingRules.length} files`));
629
822
  }
823
+ if (existingSkills.length > 0) {
824
+ console.log(pc4.dim(` Skills: ${existingSkills.length} directories`));
825
+ }
630
826
  console.log();
631
827
  const shouldContinue = await p3.confirm({
632
828
  message: "This will merge with existing files. Continue?",
@@ -656,10 +852,19 @@ var pullCommand = defineCommand3({
656
852
  });
657
853
  s.stop("Rules updated");
658
854
  }
855
+ if (shouldPullSkills) {
856
+ s.start("Pulling skills...");
857
+ await downloadTemplate(`${REPO_URL}/templates/skills#${REPO_REF}`, {
858
+ dir: skillsDir,
859
+ force: true
860
+ });
861
+ s.stop("Skills updated");
862
+ }
659
863
  printDivider();
660
864
  console.log();
661
865
  const newCommands = listFiles(commandsDir, ".md");
662
866
  const newRules = listFiles(rulesDir, ".mdc");
867
+ const newSkills = listDirs(skillsDir);
663
868
  if (shouldPullCommands) {
664
869
  const added = newCommands.length - existingCommands.length;
665
870
  printSuccess(
@@ -672,6 +877,12 @@ var pullCommand = defineCommand3({
672
877
  `Rules: ${highlight(newRules.length.toString())} total` + (added > 0 ? pc4.green(` (+${added} new)`) : "")
673
878
  );
674
879
  }
880
+ if (shouldPullSkills) {
881
+ const added = newSkills.length - existingSkills.length;
882
+ printSuccess(
883
+ `Skills: ${highlight(newSkills.length.toString())} total` + (added > 0 ? pc4.green(` (+${added} new)`) : "")
884
+ );
885
+ }
675
886
  console.log();
676
887
  p3.outro(pc4.green("\u2728 Successfully pulled latest updates!"));
677
888
  } catch (error) {
@@ -683,10 +894,10 @@ var pullCommand = defineCommand3({
683
894
  });
684
895
 
685
896
  // src/commands/list.ts
686
- import { defineCommand as defineCommand4 } from "citty";
687
897
  import * as p4 from "@clack/prompts";
688
- import pc5 from "picocolors";
898
+ import { defineCommand as defineCommand4 } from "citty";
689
899
  import { join as join5 } from "path";
900
+ import pc5 from "picocolors";
690
901
  function extractDescription(content, isCommand) {
691
902
  if (isCommand) {
692
903
  const firstLine = content.trim().split("\n")[0];
@@ -713,10 +924,31 @@ function getItems(dir, extension, isCommand) {
713
924
  };
714
925
  });
715
926
  }
927
+ function getSkills(dir) {
928
+ const skillDirs = listDirs(dir);
929
+ return skillDirs.map((skillName) => {
930
+ const skillPath = join5(dir, skillName);
931
+ const skillFile = join5(skillPath, "SKILL.mdc");
932
+ const altSkillFile = join5(skillPath, "SKILL.md");
933
+ let description;
934
+ if (fileExists(skillFile)) {
935
+ const content = readFile(skillFile);
936
+ description = extractDescription(content, false);
937
+ } else if (fileExists(altSkillFile)) {
938
+ const content = readFile(altSkillFile);
939
+ description = extractDescription(content, false);
940
+ }
941
+ return {
942
+ name: skillName,
943
+ path: skillPath,
944
+ description
945
+ };
946
+ });
947
+ }
716
948
  var listCommand = defineCommand4({
717
949
  meta: {
718
950
  name: "list",
719
- description: "List all commands and rules"
951
+ description: "List all commands, rules, and skills"
720
952
  },
721
953
  args: {
722
954
  commands: {
@@ -731,6 +963,12 @@ var listCommand = defineCommand4({
731
963
  description: "Only list rules",
732
964
  default: false
733
965
  },
966
+ skills: {
967
+ type: "boolean",
968
+ alias: "s",
969
+ description: "Only list skills",
970
+ default: false
971
+ },
734
972
  verbose: {
735
973
  type: "boolean",
736
974
  alias: "v",
@@ -739,18 +977,23 @@ var listCommand = defineCommand4({
739
977
  }
740
978
  },
741
979
  async run({ args }) {
742
- const listBoth = !args.commands && !args.rules;
743
- const shouldListCommands = listBoth || args.commands;
744
- const shouldListRules = listBoth || args.rules;
980
+ const listAll = !args.commands && !args.rules && !args.skills;
981
+ const shouldListCommands = listAll || args.commands;
982
+ const shouldListRules = listAll || args.rules;
983
+ const shouldListSkills = listAll || args.skills;
745
984
  p4.intro(pc5.bgCyan(pc5.black(" cursor-kit list ")));
746
985
  const commandsDir = getCommandsDir();
747
986
  const rulesDir = getRulesDir();
987
+ const skillsDir = getSkillsDir();
748
988
  const commands = shouldListCommands ? getItems(commandsDir, ".md", true) : [];
749
989
  const rules = shouldListRules ? getItems(rulesDir, ".mdc", false) : [];
750
- if (commands.length === 0 && rules.length === 0) {
990
+ const skills = shouldListSkills ? getSkills(skillsDir) : [];
991
+ if (commands.length === 0 && rules.length === 0 && skills.length === 0) {
751
992
  console.log();
752
- console.log(pc5.yellow(" No commands or rules found."));
753
- console.log(pc5.dim(" Run ") + highlight("cursor-kit init") + pc5.dim(" to get started."));
993
+ console.log(pc5.yellow(" No commands, rules, or skills found."));
994
+ console.log(
995
+ pc5.dim(" Run ") + highlight("cursor-kit init") + pc5.dim(" to get started.")
996
+ );
754
997
  console.log();
755
998
  p4.outro(pc5.dim("Nothing to show"));
756
999
  return;
@@ -758,7 +1001,9 @@ var listCommand = defineCommand4({
758
1001
  printDivider();
759
1002
  if (shouldListCommands && commands.length > 0) {
760
1003
  console.log();
761
- console.log(pc5.bold(pc5.cyan(" \u{1F4DC} Commands")) + pc5.dim(` (${commands.length})`));
1004
+ console.log(
1005
+ pc5.bold(pc5.cyan(" \u{1F4DC} Commands")) + pc5.dim(` (${commands.length})`)
1006
+ );
762
1007
  console.log();
763
1008
  commands.forEach((cmd) => {
764
1009
  console.log(` ${pc5.green("\u25CF")} ${highlight(cmd.name)}`);
@@ -772,7 +1017,9 @@ var listCommand = defineCommand4({
772
1017
  }
773
1018
  if (shouldListRules && rules.length > 0) {
774
1019
  console.log();
775
- console.log(pc5.bold(pc5.cyan(" \u{1F4CB} Rules")) + pc5.dim(` (${rules.length})`));
1020
+ console.log(
1021
+ pc5.bold(pc5.cyan(" \u{1F4CB} Rules")) + pc5.dim(` (${rules.length})`)
1022
+ );
776
1023
  console.log();
777
1024
  rules.forEach((rule) => {
778
1025
  console.log(` ${pc5.green("\u25CF")} ${highlight(rule.name)}`);
@@ -784,9 +1031,25 @@ var listCommand = defineCommand4({
784
1031
  }
785
1032
  });
786
1033
  }
1034
+ if (shouldListSkills && skills.length > 0) {
1035
+ console.log();
1036
+ console.log(
1037
+ pc5.bold(pc5.cyan(" \u{1F3AF} Skills")) + pc5.dim(` (${skills.length})`)
1038
+ );
1039
+ console.log();
1040
+ skills.forEach((skill) => {
1041
+ console.log(` ${pc5.green("\u25CF")} ${highlight(skill.name)}`);
1042
+ if (skill.description) {
1043
+ console.log(pc5.dim(` ${skill.description}`));
1044
+ }
1045
+ if (args.verbose) {
1046
+ console.log(pc5.dim(` ${skill.path}`));
1047
+ }
1048
+ });
1049
+ }
787
1050
  console.log();
788
1051
  printDivider();
789
- const total = commands.length + rules.length;
1052
+ const total = commands.length + rules.length + skills.length;
790
1053
  p4.outro(pc5.dim(`Total: ${total} item${total !== 1 ? "s" : ""}`));
791
1054
  }
792
1055
  });
@@ -799,18 +1062,18 @@ import { join as join6 } from "path";
799
1062
  var removeCommand = defineCommand5({
800
1063
  meta: {
801
1064
  name: "remove",
802
- description: "Remove a command or rule"
1065
+ description: "Remove a command, rule, or skill"
803
1066
  },
804
1067
  args: {
805
1068
  type: {
806
1069
  type: "string",
807
1070
  alias: "t",
808
- description: "Type: 'command' or 'rule'"
1071
+ description: "Type: 'command', 'rule', or 'skill'"
809
1072
  },
810
1073
  name: {
811
1074
  type: "string",
812
1075
  alias: "n",
813
- description: "Name of the command or rule to remove"
1076
+ description: "Name of the command, rule, or skill to remove"
814
1077
  },
815
1078
  force: {
816
1079
  type: "boolean",
@@ -823,18 +1086,20 @@ var removeCommand = defineCommand5({
823
1086
  p5.intro(pc6.bgCyan(pc6.black(" cursor-kit remove ")));
824
1087
  const commandsDir = getCommandsDir();
825
1088
  const rulesDir = getRulesDir();
1089
+ const skillsDir = getSkillsDir();
826
1090
  const commands = listFiles(commandsDir, ".md").map((f) => f.replace(".md", ""));
827
1091
  const rules = listFiles(rulesDir, ".mdc").map((f) => f.replace(".mdc", ""));
828
- if (commands.length === 0 && rules.length === 0) {
1092
+ const skills = listDirs(skillsDir);
1093
+ if (commands.length === 0 && rules.length === 0 && skills.length === 0) {
829
1094
  console.log();
830
- console.log(pc6.yellow(" No commands or rules to remove."));
1095
+ console.log(pc6.yellow(" No commands, rules, or skills to remove."));
831
1096
  console.log();
832
1097
  p5.outro(pc6.dim("Nothing to do"));
833
1098
  return;
834
1099
  }
835
1100
  let itemType;
836
1101
  let itemName;
837
- if (args.type && ["command", "rule"].includes(args.type)) {
1102
+ if (args.type && ["command", "rule", "skill"].includes(args.type)) {
838
1103
  itemType = args.type;
839
1104
  } else {
840
1105
  const typeOptions = [];
@@ -852,6 +1117,13 @@ var removeCommand = defineCommand5({
852
1117
  hint: `${rules.length} available`
853
1118
  });
854
1119
  }
1120
+ if (skills.length > 0) {
1121
+ typeOptions.push({
1122
+ value: "skill",
1123
+ label: "Skill",
1124
+ hint: `${skills.length} available`
1125
+ });
1126
+ }
855
1127
  const typeResult = await p5.select({
856
1128
  message: "What do you want to remove?",
857
1129
  options: typeOptions
@@ -863,9 +1135,11 @@ var removeCommand = defineCommand5({
863
1135
  itemType = typeResult;
864
1136
  }
865
1137
  const isCommand = itemType === "command";
866
- const items = isCommand ? commands : rules;
867
- const dir = isCommand ? commandsDir : rulesDir;
868
- const extension = isCommand ? ".md" : ".mdc";
1138
+ const isRule = itemType === "rule";
1139
+ const isSkill = itemType === "skill";
1140
+ const items = isCommand ? commands : isRule ? rules : skills;
1141
+ const dir = isCommand ? commandsDir : isRule ? rulesDir : skillsDir;
1142
+ const extension = isCommand ? ".md" : isRule ? ".mdc" : "";
869
1143
  if (items.length === 0) {
870
1144
  p5.cancel(`No ${itemType}s found`);
871
1145
  process.exit(0);
@@ -887,14 +1161,16 @@ var removeCommand = defineCommand5({
887
1161
  }
888
1162
  itemName = nameResult;
889
1163
  }
890
- const filePath = join6(dir, `${itemName}${extension}`);
891
- if (!fileExists(filePath)) {
1164
+ const targetPath = isSkill ? join6(dir, itemName) : join6(dir, `${itemName}${extension}`);
1165
+ const exists = isSkill ? dirExists(targetPath) : fileExists(targetPath);
1166
+ if (!exists) {
892
1167
  p5.cancel(`${itemType} '${itemName}' not found`);
893
1168
  process.exit(1);
894
1169
  }
895
1170
  if (!args.force) {
1171
+ const displayName = isSkill ? itemName : itemName + extension;
896
1172
  const shouldDelete = await p5.confirm({
897
- message: `Are you sure you want to delete ${highlight(itemName + extension)}?`,
1173
+ message: `Are you sure you want to delete ${highlight(displayName)}?${isSkill ? " (This will remove the entire skill directory)" : ""}`,
898
1174
  initialValue: false
899
1175
  });
900
1176
  if (p5.isCancel(shouldDelete) || !shouldDelete) {
@@ -903,9 +1179,10 @@ var removeCommand = defineCommand5({
903
1179
  }
904
1180
  }
905
1181
  try {
906
- removeFile(filePath);
1182
+ removeFile(targetPath);
1183
+ const displayName = isSkill ? itemName : itemName + extension;
907
1184
  console.log();
908
- printSuccess(`Removed ${highlight(itemName + extension)}`);
1185
+ printSuccess(`Removed ${highlight(displayName)}`);
909
1186
  console.log();
910
1187
  p5.outro(pc6.green("\u2728 Done!"));
911
1188
  } catch (error) {