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.cjs CHANGED
@@ -102,6 +102,9 @@ function removeFile(path) {
102
102
  (0, import_node_fs.rmSync)(path, { recursive: true });
103
103
  }
104
104
  }
105
+ function copyDir(src, dest) {
106
+ (0, import_node_fs.cpSync)(src, dest, { recursive: true });
107
+ }
105
108
  function listFiles(dir, extension) {
106
109
  if (!dirExists(dir)) return [];
107
110
  const files = (0, import_node_fs.readdirSync)(dir);
@@ -110,6 +113,13 @@ function listFiles(dir, extension) {
110
113
  }
111
114
  return files;
112
115
  }
116
+ function listDirs(dir) {
117
+ if (!dirExists(dir)) return [];
118
+ return (0, import_node_fs.readdirSync)(dir).filter((item) => {
119
+ const itemPath = (0, import_node_path.join)(dir, item);
120
+ return (0, import_node_fs.statSync)(itemPath).isDirectory();
121
+ });
122
+ }
113
123
  function getCursorDir(cwd = process.cwd()) {
114
124
  return (0, import_node_path.join)(cwd, ".cursor");
115
125
  }
@@ -119,10 +129,17 @@ function getCommandsDir(cwd = process.cwd()) {
119
129
  function getRulesDir(cwd = process.cwd()) {
120
130
  return (0, import_node_path.join)(getCursorDir(cwd), "rules");
121
131
  }
132
+ function getSkillsDir(cwd = process.cwd()) {
133
+ return (0, import_node_path.join)(getCursorDir(cwd), "skills");
134
+ }
122
135
  function getConflictingFiles(dir, files) {
123
136
  if (!dirExists(dir)) return [];
124
137
  return files.filter((file) => fileExists((0, import_node_path.join)(dir, file)));
125
138
  }
139
+ function getConflictingDirs(dir, dirs) {
140
+ if (!dirExists(dir)) return [];
141
+ return dirs.filter((d) => dirExists((0, import_node_path.join)(dir, d)));
142
+ }
126
143
 
127
144
  // src/utils/templates.ts
128
145
  var import_node_path2 = require("path");
@@ -134,7 +151,8 @@ var REPO_REF = "master";
134
151
  var REPO_RAW_URL = "https://raw.githubusercontent.com/duongductrong/cursor-kit/master";
135
152
  var TEMPLATE_PATHS = {
136
153
  commands: "templates/commands",
137
- rules: "templates/rules"
154
+ rules: "templates/rules",
155
+ skills: "templates/skills"
138
156
  };
139
157
 
140
158
  // src/utils/templates.ts
@@ -154,12 +172,14 @@ function getLocalManifest() {
154
172
  }
155
173
  const commandsDir = (0, import_node_path2.join)(templatesDir, "commands");
156
174
  const rulesDir = (0, import_node_path2.join)(templatesDir, "rules");
157
- if (!dirExists(commandsDir) && !dirExists(rulesDir)) {
175
+ const skillsDir = (0, import_node_path2.join)(templatesDir, "skills");
176
+ if (!dirExists(commandsDir) && !dirExists(rulesDir) && !dirExists(skillsDir)) {
158
177
  return null;
159
178
  }
160
179
  return {
161
180
  commands: dirExists(commandsDir) ? listFiles(commandsDir, ".md") : [],
162
- rules: dirExists(rulesDir) ? listFiles(rulesDir, ".mdc") : []
181
+ rules: dirExists(rulesDir) ? listFiles(rulesDir, ".mdc") : [],
182
+ skills: dirExists(skillsDir) ? listDirs(skillsDir) : []
163
183
  };
164
184
  }
165
185
  function getLocalTemplateContent(type, filename) {
@@ -170,6 +190,22 @@ function getLocalTemplateContent(type, filename) {
170
190
  }
171
191
  return null;
172
192
  }
193
+ function getLocalSkillDir(skillName) {
194
+ const templatesDir = getLocalTemplatesDir();
195
+ const skillPath = (0, import_node_path2.join)(templatesDir, "skills", skillName);
196
+ if (dirExists(skillPath)) {
197
+ return skillPath;
198
+ }
199
+ return null;
200
+ }
201
+ function copyLocalSkill(skillName, targetDir) {
202
+ const sourcePath = getLocalSkillDir(skillName);
203
+ if (!sourcePath) return false;
204
+ const destPath = (0, import_node_path2.join)(targetDir, skillName);
205
+ ensureDir(destPath);
206
+ copyDir(sourcePath, destPath);
207
+ return true;
208
+ }
173
209
  async function fetchTemplateManifest() {
174
210
  const localManifest = getLocalManifest();
175
211
  if (localManifest) {
@@ -213,9 +249,13 @@ function getTemplateLabel(filename) {
213
249
  const nameWithoutExt = filename.replace(/\.(md|mdc)$/, "");
214
250
  return nameWithoutExt.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
215
251
  }
252
+ function getSkillLabel(skillName) {
253
+ return skillName.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
254
+ }
216
255
 
217
256
  // src/commands/init.ts
218
257
  async function selectTemplates(type, availableTemplates) {
258
+ const labelFn = type === "skills" ? getSkillLabel : getTemplateLabel;
219
259
  const selectionMode = await p.select({
220
260
  message: `How would you like to add ${type}?`,
221
261
  options: [
@@ -239,7 +279,7 @@ async function selectTemplates(type, availableTemplates) {
239
279
  message: `Select ${type} to add:`,
240
280
  options: availableTemplates.map((template) => ({
241
281
  value: template,
242
- label: getTemplateLabel(template),
282
+ label: labelFn(template),
243
283
  hint: template
244
284
  })),
245
285
  required: true
@@ -303,10 +343,36 @@ async function installTemplates(type, targetDir, selectedTemplates, conflictStra
303
343
  }
304
344
  return result;
305
345
  }
346
+ async function installSkills(targetDir, selectedSkills, conflictStrategy) {
347
+ const result = { added: [], skipped: [] };
348
+ const conflictingDirs = getConflictingDirs(targetDir, selectedSkills);
349
+ let skillsToInstall;
350
+ if (conflictStrategy === "merge") {
351
+ skillsToInstall = selectedSkills.filter(
352
+ (s) => !conflictingDirs.includes(s)
353
+ );
354
+ result.skipped = conflictingDirs.filter(
355
+ (d) => selectedSkills.includes(d)
356
+ );
357
+ } else {
358
+ skillsToInstall = selectedSkills;
359
+ }
360
+ if (skillsToInstall.length === 0) {
361
+ return result;
362
+ }
363
+ ensureDir(targetDir);
364
+ for (const skillName of skillsToInstall) {
365
+ const success = copyLocalSkill(skillName, targetDir);
366
+ if (success) {
367
+ result.added.push(skillName);
368
+ }
369
+ }
370
+ return result;
371
+ }
306
372
  var initCommand = (0, import_citty.defineCommand)({
307
373
  meta: {
308
374
  name: "init",
309
- description: "Initialize .cursor/commands and .cursor/rules in your project"
375
+ description: "Initialize .cursor/commands, .cursor/rules, and .cursor/skills in your project"
310
376
  },
311
377
  args: {
312
378
  force: {
@@ -327,6 +393,12 @@ var initCommand = (0, import_citty.defineCommand)({
327
393
  description: "Only initialize rules",
328
394
  default: false
329
395
  },
396
+ skills: {
397
+ type: "boolean",
398
+ alias: "s",
399
+ description: "Only initialize skills",
400
+ default: false
401
+ },
330
402
  all: {
331
403
  type: "boolean",
332
404
  alias: "a",
@@ -339,9 +411,11 @@ var initCommand = (0, import_citty.defineCommand)({
339
411
  const cursorDir = getCursorDir(cwd);
340
412
  const commandsDir = getCommandsDir(cwd);
341
413
  const rulesDir = getRulesDir(cwd);
342
- const initBoth = !args.commands && !args.rules;
343
- const shouldInitCommands = initBoth || args.commands;
344
- const shouldInitRules = initBoth || args.rules;
414
+ const skillsDir = getSkillsDir(cwd);
415
+ const initAll = !args.commands && !args.rules && !args.skills;
416
+ const shouldInitCommands = initAll || args.commands;
417
+ const shouldInitRules = initAll || args.rules;
418
+ const shouldInitSkills = initAll || args.skills;
345
419
  p.intro(import_picocolors2.default.bgCyan(import_picocolors2.default.black(" cursor-kit init ")));
346
420
  const s = p.spinner();
347
421
  let manifest;
@@ -424,6 +498,36 @@ var initCommand = (0, import_citty.defineCommand)({
424
498
  );
425
499
  s.stop("Rules installed");
426
500
  }
501
+ if (shouldInitSkills) {
502
+ let selectedSkills;
503
+ if (args.all) {
504
+ selectedSkills = manifest.skills;
505
+ } else {
506
+ const selection = await selectTemplates("skills", manifest.skills);
507
+ if (p.isCancel(selection)) {
508
+ p.cancel("Operation cancelled");
509
+ process.exit(0);
510
+ }
511
+ selectedSkills = selection;
512
+ }
513
+ const conflictingSkills = getConflictingDirs(skillsDir, selectedSkills);
514
+ let skillStrategy = "overwrite";
515
+ if (conflictingSkills.length > 0 && !args.force) {
516
+ const strategy = await handleConflicts("skills", conflictingSkills);
517
+ if (p.isCancel(strategy) || strategy === "cancel") {
518
+ p.cancel("Operation cancelled");
519
+ process.exit(0);
520
+ }
521
+ skillStrategy = strategy;
522
+ }
523
+ s.start("Installing skills...");
524
+ results.skills = await installSkills(
525
+ skillsDir,
526
+ selectedSkills,
527
+ skillStrategy
528
+ );
529
+ s.stop("Skills installed");
530
+ }
427
531
  printDivider();
428
532
  console.log();
429
533
  if (results.commands) {
@@ -454,8 +558,22 @@ var initCommand = (0, import_citty.defineCommand)({
454
558
  }
455
559
  }
456
560
  }
457
- const totalAdded = (results.commands?.added.length ?? 0) + (results.rules?.added.length ?? 0);
458
- const totalSkipped = (results.commands?.skipped.length ?? 0) + (results.rules?.skipped.length ?? 0);
561
+ if (results.skills) {
562
+ const { added, skipped } = results.skills;
563
+ if (added.length > 0 || skipped.length > 0) {
564
+ printSuccess(
565
+ `Skills: ${highlight(added.length.toString())} added${skipped.length > 0 ? `, ${import_picocolors2.default.yellow(skipped.length.toString())} skipped` : ""}`
566
+ );
567
+ for (const f of added) {
568
+ console.log(import_picocolors2.default.dim(` \u2514\u2500 ${import_picocolors2.default.green("+")} ${f}`));
569
+ }
570
+ for (const f of skipped) {
571
+ console.log(import_picocolors2.default.dim(` \u2514\u2500 ${import_picocolors2.default.yellow("\u25CB")} ${f} (kept existing)`));
572
+ }
573
+ }
574
+ }
575
+ const totalAdded = (results.commands?.added.length ?? 0) + (results.rules?.added.length ?? 0) + (results.skills?.added.length ?? 0);
576
+ const totalSkipped = (results.commands?.skipped.length ?? 0) + (results.rules?.skipped.length ?? 0) + (results.skills?.skipped.length ?? 0);
459
577
  if (totalAdded === 0 && totalSkipped > 0) {
460
578
  console.log();
461
579
  p.outro(import_picocolors2.default.yellow("No new templates added (all selected files already exist)"));
@@ -474,10 +592,10 @@ var initCommand = (0, import_citty.defineCommand)({
474
592
  });
475
593
 
476
594
  // src/commands/add.ts
477
- var import_citty2 = require("citty");
478
595
  var p2 = __toESM(require("@clack/prompts"), 1);
479
- var import_picocolors3 = __toESM(require("picocolors"), 1);
596
+ var import_citty2 = require("citty");
480
597
  var import_node_path4 = require("path");
598
+ var import_picocolors3 = __toESM(require("picocolors"), 1);
481
599
  var COMMAND_TEMPLATE = `You are a helpful assistant. Describe what this command does.
482
600
 
483
601
  ## Instructions
@@ -504,31 +622,69 @@ Describe the rule behavior here.
504
622
  - Guideline 1
505
623
  - Guideline 2
506
624
  `;
625
+ var SKILL_TEMPLATE = `---
626
+ description: Describe when this skill should be activated
627
+ globs:
628
+ alwaysApply: false
629
+ ---
630
+
631
+ # Skill Name
632
+
633
+ Brief description of what this skill enables.
634
+
635
+ ## Core Capabilities
636
+
637
+ - Capability 1
638
+ - Capability 2
639
+
640
+ ## When to Use
641
+
642
+ Use this skill when:
643
+ - Condition 1
644
+ - Condition 2
645
+
646
+ ## References
647
+
648
+ For detailed guidance, see the references folder:
649
+ - [Reference 1](./references/example.md) - Description
650
+ `;
651
+ var SKILL_REFERENCE_TEMPLATE = `# Reference Title
652
+
653
+ Detailed reference content goes here.
654
+
655
+ ## Section 1
656
+
657
+ Content...
658
+
659
+ ## Section 2
660
+
661
+ Content...
662
+ `;
507
663
  function generateSlug(name) {
508
664
  return name.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim();
509
665
  }
510
666
  var addCommand = (0, import_citty2.defineCommand)({
511
667
  meta: {
512
668
  name: "add",
513
- description: "Add a new command or rule"
669
+ description: "Add a new command, rule, or skill"
514
670
  },
515
671
  args: {
516
672
  type: {
517
673
  type: "string",
518
674
  alias: "t",
519
- description: "Type: 'command' or 'rule'"
675
+ description: "Type: 'command', 'rule', or 'skill'"
520
676
  },
521
677
  name: {
522
678
  type: "string",
523
679
  alias: "n",
524
- description: "Name of the command or rule"
680
+ description: "Name of the command, rule, or skill"
525
681
  }
526
682
  },
527
683
  async run({ args }) {
528
684
  p2.intro(import_picocolors3.default.bgCyan(import_picocolors3.default.black(" cursor-kit add ")));
529
685
  let itemType;
530
686
  let itemName;
531
- if (args.type && ["command", "rule"].includes(args.type)) {
687
+ if (args.type && ["command", "rule", "skill"].includes(args.type)) {
532
688
  itemType = args.type;
533
689
  } else {
534
690
  const typeResult = await p2.select({
@@ -543,6 +699,11 @@ var addCommand = (0, import_citty2.defineCommand)({
543
699
  value: "rule",
544
700
  label: "Rule",
545
701
  hint: "Project-specific AI behavior rules"
702
+ },
703
+ {
704
+ value: "skill",
705
+ label: "Skill",
706
+ hint: "Comprehensive guide with references"
546
707
  }
547
708
  ]
548
709
  });
@@ -557,7 +718,7 @@ var addCommand = (0, import_citty2.defineCommand)({
557
718
  } else {
558
719
  const nameResult = await p2.text({
559
720
  message: `Enter ${itemType} name:`,
560
- placeholder: itemType === "command" ? "my-command" : "my-rule",
721
+ placeholder: itemType === "command" ? "my-command" : itemType === "rule" ? "my-rule" : "my-skill",
561
722
  validate: (value) => {
562
723
  if (!value.trim()) return "Name is required";
563
724
  if (value.length < 2) return "Name must be at least 2 characters";
@@ -572,12 +733,22 @@ var addCommand = (0, import_citty2.defineCommand)({
572
733
  }
573
734
  const slug = generateSlug(itemName);
574
735
  const isCommand = itemType === "command";
575
- const targetDir = isCommand ? getCommandsDir() : getRulesDir();
576
- const extension = isCommand ? ".md" : ".mdc";
577
- const filePath = (0, import_node_path4.join)(targetDir, `${slug}${extension}`);
578
- if (fileExists(filePath)) {
736
+ const isSkill = itemType === "skill";
737
+ let targetPath;
738
+ let displayPath;
739
+ if (isSkill) {
740
+ const skillsDir = getSkillsDir();
741
+ targetPath = (0, import_node_path4.join)(skillsDir, slug);
742
+ displayPath = targetPath;
743
+ } else {
744
+ const targetDir = isCommand ? getCommandsDir() : getRulesDir();
745
+ const extension = isCommand ? ".md" : ".mdc";
746
+ targetPath = (0, import_node_path4.join)(targetDir, `${slug}${extension}`);
747
+ displayPath = targetPath;
748
+ }
749
+ if (isSkill ? dirExists(targetPath) : fileExists(targetPath)) {
579
750
  const shouldOverwrite = await p2.confirm({
580
- message: `${highlight(slug + extension)} already exists. Overwrite?`,
751
+ message: `${highlight(isSkill ? slug : slug + (isCommand ? ".md" : ".mdc"))} already exists. Overwrite?`,
581
752
  initialValue: false
582
753
  });
583
754
  if (p2.isCancel(shouldOverwrite) || !shouldOverwrite) {
@@ -588,12 +759,25 @@ var addCommand = (0, import_citty2.defineCommand)({
588
759
  const s = p2.spinner();
589
760
  s.start(`Creating ${itemType}...`);
590
761
  try {
591
- ensureDir(targetDir);
592
- const template = isCommand ? COMMAND_TEMPLATE : RULE_TEMPLATE;
593
- writeFile(filePath, template);
762
+ if (isSkill) {
763
+ ensureDir(targetPath);
764
+ ensureDir((0, import_node_path4.join)(targetPath, "references"));
765
+ writeFile((0, import_node_path4.join)(targetPath, "SKILL.mdc"), SKILL_TEMPLATE);
766
+ writeFile((0, import_node_path4.join)(targetPath, "references", "example.md"), SKILL_REFERENCE_TEMPLATE);
767
+ } else {
768
+ const targetDir = isCommand ? getCommandsDir() : getRulesDir();
769
+ ensureDir(targetDir);
770
+ const template = isCommand ? COMMAND_TEMPLATE : RULE_TEMPLATE;
771
+ writeFile(targetPath, template);
772
+ }
594
773
  s.stop(`${itemType.charAt(0).toUpperCase() + itemType.slice(1)} created`);
595
774
  console.log();
596
- console.log(import_picocolors3.default.dim(" File: ") + highlight(filePath));
775
+ if (isSkill) {
776
+ console.log(import_picocolors3.default.dim(" Directory: ") + highlight(displayPath));
777
+ console.log(import_picocolors3.default.dim(" Main file: ") + highlight((0, import_node_path4.join)(displayPath, "SKILL.mdc")));
778
+ } else {
779
+ console.log(import_picocolors3.default.dim(" File: ") + highlight(displayPath));
780
+ }
597
781
  console.log();
598
782
  p2.outro(
599
783
  import_picocolors3.default.green(`\u2728 ${itemType.charAt(0).toUpperCase() + itemType.slice(1)} created! Edit the file to customize it.`)
@@ -629,6 +813,12 @@ var pullCommand = (0, import_citty3.defineCommand)({
629
813
  description: "Only pull rules",
630
814
  default: false
631
815
  },
816
+ skills: {
817
+ type: "boolean",
818
+ alias: "s",
819
+ description: "Only pull skills",
820
+ default: false
821
+ },
632
822
  force: {
633
823
  type: "boolean",
634
824
  alias: "f",
@@ -637,15 +827,18 @@ var pullCommand = (0, import_citty3.defineCommand)({
637
827
  }
638
828
  },
639
829
  async run({ args }) {
640
- const pullBoth = !args.commands && !args.rules;
641
- const shouldPullCommands = pullBoth || args.commands;
642
- const shouldPullRules = pullBoth || args.rules;
830
+ const pullAll = !args.commands && !args.rules && !args.skills;
831
+ const shouldPullCommands = pullAll || args.commands;
832
+ const shouldPullRules = pullAll || args.rules;
833
+ const shouldPullSkills = pullAll || args.skills;
643
834
  p3.intro(import_picocolors4.default.bgCyan(import_picocolors4.default.black(" cursor-kit pull ")));
644
835
  const commandsDir = getCommandsDir();
645
836
  const rulesDir = getRulesDir();
837
+ const skillsDir = getSkillsDir();
646
838
  const existingCommands = listFiles(commandsDir, ".md");
647
839
  const existingRules = listFiles(rulesDir, ".mdc");
648
- const hasExisting = existingCommands.length > 0 || existingRules.length > 0;
840
+ const existingSkills = listDirs(skillsDir);
841
+ const hasExisting = existingCommands.length > 0 || existingRules.length > 0 || existingSkills.length > 0;
649
842
  if (hasExisting && !args.force) {
650
843
  printInfo("Current status:");
651
844
  if (existingCommands.length > 0) {
@@ -654,6 +847,9 @@ var pullCommand = (0, import_citty3.defineCommand)({
654
847
  if (existingRules.length > 0) {
655
848
  console.log(import_picocolors4.default.dim(` Rules: ${existingRules.length} files`));
656
849
  }
850
+ if (existingSkills.length > 0) {
851
+ console.log(import_picocolors4.default.dim(` Skills: ${existingSkills.length} directories`));
852
+ }
657
853
  console.log();
658
854
  const shouldContinue = await p3.confirm({
659
855
  message: "This will merge with existing files. Continue?",
@@ -683,10 +879,19 @@ var pullCommand = (0, import_citty3.defineCommand)({
683
879
  });
684
880
  s.stop("Rules updated");
685
881
  }
882
+ if (shouldPullSkills) {
883
+ s.start("Pulling skills...");
884
+ await (0, import_giget.downloadTemplate)(`${REPO_URL}/templates/skills#${REPO_REF}`, {
885
+ dir: skillsDir,
886
+ force: true
887
+ });
888
+ s.stop("Skills updated");
889
+ }
686
890
  printDivider();
687
891
  console.log();
688
892
  const newCommands = listFiles(commandsDir, ".md");
689
893
  const newRules = listFiles(rulesDir, ".mdc");
894
+ const newSkills = listDirs(skillsDir);
690
895
  if (shouldPullCommands) {
691
896
  const added = newCommands.length - existingCommands.length;
692
897
  printSuccess(
@@ -699,6 +904,12 @@ var pullCommand = (0, import_citty3.defineCommand)({
699
904
  `Rules: ${highlight(newRules.length.toString())} total` + (added > 0 ? import_picocolors4.default.green(` (+${added} new)`) : "")
700
905
  );
701
906
  }
907
+ if (shouldPullSkills) {
908
+ const added = newSkills.length - existingSkills.length;
909
+ printSuccess(
910
+ `Skills: ${highlight(newSkills.length.toString())} total` + (added > 0 ? import_picocolors4.default.green(` (+${added} new)`) : "")
911
+ );
912
+ }
702
913
  console.log();
703
914
  p3.outro(import_picocolors4.default.green("\u2728 Successfully pulled latest updates!"));
704
915
  } catch (error) {
@@ -710,10 +921,10 @@ var pullCommand = (0, import_citty3.defineCommand)({
710
921
  });
711
922
 
712
923
  // src/commands/list.ts
713
- var import_citty4 = require("citty");
714
924
  var p4 = __toESM(require("@clack/prompts"), 1);
715
- var import_picocolors5 = __toESM(require("picocolors"), 1);
925
+ var import_citty4 = require("citty");
716
926
  var import_node_path5 = require("path");
927
+ var import_picocolors5 = __toESM(require("picocolors"), 1);
717
928
  function extractDescription(content, isCommand) {
718
929
  if (isCommand) {
719
930
  const firstLine = content.trim().split("\n")[0];
@@ -740,10 +951,31 @@ function getItems(dir, extension, isCommand) {
740
951
  };
741
952
  });
742
953
  }
954
+ function getSkills(dir) {
955
+ const skillDirs = listDirs(dir);
956
+ return skillDirs.map((skillName) => {
957
+ const skillPath = (0, import_node_path5.join)(dir, skillName);
958
+ const skillFile = (0, import_node_path5.join)(skillPath, "SKILL.mdc");
959
+ const altSkillFile = (0, import_node_path5.join)(skillPath, "SKILL.md");
960
+ let description;
961
+ if (fileExists(skillFile)) {
962
+ const content = readFile(skillFile);
963
+ description = extractDescription(content, false);
964
+ } else if (fileExists(altSkillFile)) {
965
+ const content = readFile(altSkillFile);
966
+ description = extractDescription(content, false);
967
+ }
968
+ return {
969
+ name: skillName,
970
+ path: skillPath,
971
+ description
972
+ };
973
+ });
974
+ }
743
975
  var listCommand = (0, import_citty4.defineCommand)({
744
976
  meta: {
745
977
  name: "list",
746
- description: "List all commands and rules"
978
+ description: "List all commands, rules, and skills"
747
979
  },
748
980
  args: {
749
981
  commands: {
@@ -758,6 +990,12 @@ var listCommand = (0, import_citty4.defineCommand)({
758
990
  description: "Only list rules",
759
991
  default: false
760
992
  },
993
+ skills: {
994
+ type: "boolean",
995
+ alias: "s",
996
+ description: "Only list skills",
997
+ default: false
998
+ },
761
999
  verbose: {
762
1000
  type: "boolean",
763
1001
  alias: "v",
@@ -766,18 +1004,23 @@ var listCommand = (0, import_citty4.defineCommand)({
766
1004
  }
767
1005
  },
768
1006
  async run({ args }) {
769
- const listBoth = !args.commands && !args.rules;
770
- const shouldListCommands = listBoth || args.commands;
771
- const shouldListRules = listBoth || args.rules;
1007
+ const listAll = !args.commands && !args.rules && !args.skills;
1008
+ const shouldListCommands = listAll || args.commands;
1009
+ const shouldListRules = listAll || args.rules;
1010
+ const shouldListSkills = listAll || args.skills;
772
1011
  p4.intro(import_picocolors5.default.bgCyan(import_picocolors5.default.black(" cursor-kit list ")));
773
1012
  const commandsDir = getCommandsDir();
774
1013
  const rulesDir = getRulesDir();
1014
+ const skillsDir = getSkillsDir();
775
1015
  const commands = shouldListCommands ? getItems(commandsDir, ".md", true) : [];
776
1016
  const rules = shouldListRules ? getItems(rulesDir, ".mdc", false) : [];
777
- if (commands.length === 0 && rules.length === 0) {
1017
+ const skills = shouldListSkills ? getSkills(skillsDir) : [];
1018
+ if (commands.length === 0 && rules.length === 0 && skills.length === 0) {
778
1019
  console.log();
779
- console.log(import_picocolors5.default.yellow(" No commands or rules found."));
780
- console.log(import_picocolors5.default.dim(" Run ") + highlight("cursor-kit init") + import_picocolors5.default.dim(" to get started."));
1020
+ console.log(import_picocolors5.default.yellow(" No commands, rules, or skills found."));
1021
+ console.log(
1022
+ import_picocolors5.default.dim(" Run ") + highlight("cursor-kit init") + import_picocolors5.default.dim(" to get started.")
1023
+ );
781
1024
  console.log();
782
1025
  p4.outro(import_picocolors5.default.dim("Nothing to show"));
783
1026
  return;
@@ -785,7 +1028,9 @@ var listCommand = (0, import_citty4.defineCommand)({
785
1028
  printDivider();
786
1029
  if (shouldListCommands && commands.length > 0) {
787
1030
  console.log();
788
- console.log(import_picocolors5.default.bold(import_picocolors5.default.cyan(" \u{1F4DC} Commands")) + import_picocolors5.default.dim(` (${commands.length})`));
1031
+ console.log(
1032
+ import_picocolors5.default.bold(import_picocolors5.default.cyan(" \u{1F4DC} Commands")) + import_picocolors5.default.dim(` (${commands.length})`)
1033
+ );
789
1034
  console.log();
790
1035
  commands.forEach((cmd) => {
791
1036
  console.log(` ${import_picocolors5.default.green("\u25CF")} ${highlight(cmd.name)}`);
@@ -799,7 +1044,9 @@ var listCommand = (0, import_citty4.defineCommand)({
799
1044
  }
800
1045
  if (shouldListRules && rules.length > 0) {
801
1046
  console.log();
802
- console.log(import_picocolors5.default.bold(import_picocolors5.default.cyan(" \u{1F4CB} Rules")) + import_picocolors5.default.dim(` (${rules.length})`));
1047
+ console.log(
1048
+ import_picocolors5.default.bold(import_picocolors5.default.cyan(" \u{1F4CB} Rules")) + import_picocolors5.default.dim(` (${rules.length})`)
1049
+ );
803
1050
  console.log();
804
1051
  rules.forEach((rule) => {
805
1052
  console.log(` ${import_picocolors5.default.green("\u25CF")} ${highlight(rule.name)}`);
@@ -811,9 +1058,25 @@ var listCommand = (0, import_citty4.defineCommand)({
811
1058
  }
812
1059
  });
813
1060
  }
1061
+ if (shouldListSkills && skills.length > 0) {
1062
+ console.log();
1063
+ console.log(
1064
+ import_picocolors5.default.bold(import_picocolors5.default.cyan(" \u{1F3AF} Skills")) + import_picocolors5.default.dim(` (${skills.length})`)
1065
+ );
1066
+ console.log();
1067
+ skills.forEach((skill) => {
1068
+ console.log(` ${import_picocolors5.default.green("\u25CF")} ${highlight(skill.name)}`);
1069
+ if (skill.description) {
1070
+ console.log(import_picocolors5.default.dim(` ${skill.description}`));
1071
+ }
1072
+ if (args.verbose) {
1073
+ console.log(import_picocolors5.default.dim(` ${skill.path}`));
1074
+ }
1075
+ });
1076
+ }
814
1077
  console.log();
815
1078
  printDivider();
816
- const total = commands.length + rules.length;
1079
+ const total = commands.length + rules.length + skills.length;
817
1080
  p4.outro(import_picocolors5.default.dim(`Total: ${total} item${total !== 1 ? "s" : ""}`));
818
1081
  }
819
1082
  });
@@ -826,18 +1089,18 @@ var import_node_path6 = require("path");
826
1089
  var removeCommand = (0, import_citty5.defineCommand)({
827
1090
  meta: {
828
1091
  name: "remove",
829
- description: "Remove a command or rule"
1092
+ description: "Remove a command, rule, or skill"
830
1093
  },
831
1094
  args: {
832
1095
  type: {
833
1096
  type: "string",
834
1097
  alias: "t",
835
- description: "Type: 'command' or 'rule'"
1098
+ description: "Type: 'command', 'rule', or 'skill'"
836
1099
  },
837
1100
  name: {
838
1101
  type: "string",
839
1102
  alias: "n",
840
- description: "Name of the command or rule to remove"
1103
+ description: "Name of the command, rule, or skill to remove"
841
1104
  },
842
1105
  force: {
843
1106
  type: "boolean",
@@ -850,18 +1113,20 @@ var removeCommand = (0, import_citty5.defineCommand)({
850
1113
  p5.intro(import_picocolors6.default.bgCyan(import_picocolors6.default.black(" cursor-kit remove ")));
851
1114
  const commandsDir = getCommandsDir();
852
1115
  const rulesDir = getRulesDir();
1116
+ const skillsDir = getSkillsDir();
853
1117
  const commands = listFiles(commandsDir, ".md").map((f) => f.replace(".md", ""));
854
1118
  const rules = listFiles(rulesDir, ".mdc").map((f) => f.replace(".mdc", ""));
855
- if (commands.length === 0 && rules.length === 0) {
1119
+ const skills = listDirs(skillsDir);
1120
+ if (commands.length === 0 && rules.length === 0 && skills.length === 0) {
856
1121
  console.log();
857
- console.log(import_picocolors6.default.yellow(" No commands or rules to remove."));
1122
+ console.log(import_picocolors6.default.yellow(" No commands, rules, or skills to remove."));
858
1123
  console.log();
859
1124
  p5.outro(import_picocolors6.default.dim("Nothing to do"));
860
1125
  return;
861
1126
  }
862
1127
  let itemType;
863
1128
  let itemName;
864
- if (args.type && ["command", "rule"].includes(args.type)) {
1129
+ if (args.type && ["command", "rule", "skill"].includes(args.type)) {
865
1130
  itemType = args.type;
866
1131
  } else {
867
1132
  const typeOptions = [];
@@ -879,6 +1144,13 @@ var removeCommand = (0, import_citty5.defineCommand)({
879
1144
  hint: `${rules.length} available`
880
1145
  });
881
1146
  }
1147
+ if (skills.length > 0) {
1148
+ typeOptions.push({
1149
+ value: "skill",
1150
+ label: "Skill",
1151
+ hint: `${skills.length} available`
1152
+ });
1153
+ }
882
1154
  const typeResult = await p5.select({
883
1155
  message: "What do you want to remove?",
884
1156
  options: typeOptions
@@ -890,9 +1162,11 @@ var removeCommand = (0, import_citty5.defineCommand)({
890
1162
  itemType = typeResult;
891
1163
  }
892
1164
  const isCommand = itemType === "command";
893
- const items = isCommand ? commands : rules;
894
- const dir = isCommand ? commandsDir : rulesDir;
895
- const extension = isCommand ? ".md" : ".mdc";
1165
+ const isRule = itemType === "rule";
1166
+ const isSkill = itemType === "skill";
1167
+ const items = isCommand ? commands : isRule ? rules : skills;
1168
+ const dir = isCommand ? commandsDir : isRule ? rulesDir : skillsDir;
1169
+ const extension = isCommand ? ".md" : isRule ? ".mdc" : "";
896
1170
  if (items.length === 0) {
897
1171
  p5.cancel(`No ${itemType}s found`);
898
1172
  process.exit(0);
@@ -914,14 +1188,16 @@ var removeCommand = (0, import_citty5.defineCommand)({
914
1188
  }
915
1189
  itemName = nameResult;
916
1190
  }
917
- const filePath = (0, import_node_path6.join)(dir, `${itemName}${extension}`);
918
- if (!fileExists(filePath)) {
1191
+ const targetPath = isSkill ? (0, import_node_path6.join)(dir, itemName) : (0, import_node_path6.join)(dir, `${itemName}${extension}`);
1192
+ const exists = isSkill ? dirExists(targetPath) : fileExists(targetPath);
1193
+ if (!exists) {
919
1194
  p5.cancel(`${itemType} '${itemName}' not found`);
920
1195
  process.exit(1);
921
1196
  }
922
1197
  if (!args.force) {
1198
+ const displayName = isSkill ? itemName : itemName + extension;
923
1199
  const shouldDelete = await p5.confirm({
924
- message: `Are you sure you want to delete ${highlight(itemName + extension)}?`,
1200
+ message: `Are you sure you want to delete ${highlight(displayName)}?${isSkill ? " (This will remove the entire skill directory)" : ""}`,
925
1201
  initialValue: false
926
1202
  });
927
1203
  if (p5.isCancel(shouldDelete) || !shouldDelete) {
@@ -930,9 +1206,10 @@ var removeCommand = (0, import_citty5.defineCommand)({
930
1206
  }
931
1207
  }
932
1208
  try {
933
- removeFile(filePath);
1209
+ removeFile(targetPath);
1210
+ const displayName = isSkill ? itemName : itemName + extension;
934
1211
  console.log();
935
- printSuccess(`Removed ${highlight(itemName + extension)}`);
1212
+ printSuccess(`Removed ${highlight(displayName)}`);
936
1213
  console.log();
937
1214
  p5.outro(import_picocolors6.default.green("\u2728 Done!"));
938
1215
  } catch (error) {