cursor-kit-cli 1.1.1 → 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 (94) hide show
  1. package/README.md +36 -0
  2. package/bin/cursor-new-instance +74 -0
  3. package/bin/cursor-remove-instance +69 -0
  4. package/dist/cli.cjs +601 -62
  5. package/dist/cli.cjs.map +1 -1
  6. package/dist/cli.js +601 -62
  7. package/dist/cli.js.map +1 -1
  8. package/dist/index.cjs +39 -1
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +9 -1
  11. package/dist/index.d.ts +9 -1
  12. package/dist/index.js +33 -2
  13. package/dist/index.js.map +1 -1
  14. package/package.json +3 -2
  15. package/templates/commands/docs.md +5 -3
  16. package/templates/commands/explain.md +5 -3
  17. package/templates/commands/fix.md +5 -3
  18. package/templates/commands/implement.md +5 -3
  19. package/templates/commands/refactor.md +5 -3
  20. package/templates/commands/review.md +5 -3
  21. package/templates/commands/test.md +5 -3
  22. package/templates/manifest.json +11 -8
  23. package/templates/rules/git.mdc +0 -2
  24. package/templates/rules/toc.mdc +17 -9
  25. package/templates/skills/aesthetic/SKILL.md +121 -0
  26. package/templates/skills/aesthetic/assets/design-guideline-template.md +163 -0
  27. package/templates/skills/aesthetic/assets/design-story-template.md +135 -0
  28. package/templates/skills/aesthetic/references/design-principles.md +62 -0
  29. package/templates/skills/aesthetic/references/design-resources.md +75 -0
  30. package/templates/skills/aesthetic/references/micro-interactions.md +53 -0
  31. package/templates/skills/aesthetic/references/storytelling-design.md +50 -0
  32. package/templates/skills/backend-development/SKILL.mdc +95 -0
  33. package/templates/skills/backend-development/references/backend-api-design.md +495 -0
  34. package/templates/skills/backend-development/references/backend-architecture.md +454 -0
  35. package/templates/skills/backend-development/references/backend-authentication.md +338 -0
  36. package/templates/skills/backend-development/references/backend-code-quality.md +659 -0
  37. package/templates/skills/backend-development/references/backend-debugging.md +904 -0
  38. package/templates/skills/backend-development/references/backend-devops.md +494 -0
  39. package/templates/skills/backend-development/references/backend-mindset.md +387 -0
  40. package/templates/skills/backend-development/references/backend-performance.md +397 -0
  41. package/templates/skills/backend-development/references/backend-security.md +290 -0
  42. package/templates/skills/backend-development/references/backend-technologies.md +256 -0
  43. package/templates/skills/backend-development/references/backend-testing.md +429 -0
  44. package/templates/skills/frontend-design/SKILL.mdc +41 -0
  45. package/templates/skills/frontend-design/references/animejs.md +396 -0
  46. package/templates/skills/frontend-development/SKILL.mdc +399 -0
  47. package/templates/skills/frontend-development/resources/common-patterns.md +331 -0
  48. package/templates/skills/frontend-development/resources/complete-examples.md +872 -0
  49. package/templates/skills/frontend-development/resources/component-patterns.md +502 -0
  50. package/templates/skills/frontend-development/resources/data-fetching.md +767 -0
  51. package/templates/skills/frontend-development/resources/file-organization.md +502 -0
  52. package/templates/skills/frontend-development/resources/loading-and-error-states.md +501 -0
  53. package/templates/skills/frontend-development/resources/performance.md +406 -0
  54. package/templates/skills/frontend-development/resources/routing-guide.md +364 -0
  55. package/templates/skills/frontend-development/resources/styling-guide.md +428 -0
  56. package/templates/skills/frontend-development/resources/typescript-standards.md +418 -0
  57. package/templates/skills/problem-solving/SKILL.mdc +96 -0
  58. package/templates/skills/problem-solving/references/attribution.md +69 -0
  59. package/templates/skills/problem-solving/references/collision-zone-thinking.md +79 -0
  60. package/templates/skills/problem-solving/references/inversion-exercise.md +91 -0
  61. package/templates/skills/problem-solving/references/meta-pattern-recognition.md +87 -0
  62. package/templates/skills/problem-solving/references/scale-game.md +95 -0
  63. package/templates/skills/problem-solving/references/simplification-cascades.md +80 -0
  64. package/templates/skills/problem-solving/references/when-stuck.md +72 -0
  65. package/templates/skills/research/SKILL.mdc +168 -0
  66. package/templates/skills/sequential-thinking/.env.example +8 -0
  67. package/templates/skills/sequential-thinking/README.md +183 -0
  68. package/templates/skills/sequential-thinking/SKILL.mdc +94 -0
  69. package/templates/skills/sequential-thinking/package.json +31 -0
  70. package/templates/skills/sequential-thinking/references/advanced-strategies.md +79 -0
  71. package/templates/skills/sequential-thinking/references/advanced-techniques.md +76 -0
  72. package/templates/skills/sequential-thinking/references/core-patterns.md +95 -0
  73. package/templates/skills/sequential-thinking/references/examples-api.md +88 -0
  74. package/templates/skills/sequential-thinking/references/examples-architecture.md +94 -0
  75. package/templates/skills/sequential-thinking/references/examples-debug.md +90 -0
  76. package/templates/skills/sequential-thinking/scripts/format-thought.js +159 -0
  77. package/templates/skills/sequential-thinking/scripts/process-thought.js +236 -0
  78. package/templates/skills/sequential-thinking/tests/format-thought.test.js +133 -0
  79. package/templates/skills/sequential-thinking/tests/process-thought.test.js +215 -0
  80. package/templates/skills/ui-styling/LICENSE.txt +202 -0
  81. package/templates/skills/ui-styling/SKILL.mdc +321 -0
  82. package/templates/skills/ui-styling/references/canvas-design-system.md +320 -0
  83. package/templates/skills/ui-styling/references/shadcn-accessibility.md +471 -0
  84. package/templates/skills/ui-styling/references/shadcn-components.md +424 -0
  85. package/templates/skills/ui-styling/references/shadcn-theming.md +373 -0
  86. package/templates/skills/ui-styling/references/tailwind-customization.md +483 -0
  87. package/templates/skills/ui-styling/references/tailwind-responsive.md +382 -0
  88. package/templates/skills/ui-styling/references/tailwind-utilities.md +455 -0
  89. package/templates/rules/frontend-design.mdc +0 -48
  90. package/templates/rules/performance.mdc +0 -54
  91. package/templates/rules/react.mdc +0 -58
  92. package/templates/rules/security.mdc +0 -50
  93. package/templates/rules/testing.mdc +0 -54
  94. package/templates/rules/typescript.mdc +0 -36
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { defineCommand as defineCommand6, runMain } from "citty";
4
+ import { defineCommand as defineCommand7, runMain } from "citty";
5
5
  import { createRequire } from "module";
6
6
 
7
7
  // src/utils/branding.ts
@@ -27,6 +27,9 @@ function printSuccess(message) {
27
27
  function printInfo(message) {
28
28
  console.log(pc.cyan("\u2139") + pc.dim(" ") + message);
29
29
  }
30
+ function printWarning(message) {
31
+ console.log(pc.yellow("\u26A0") + pc.dim(" ") + message);
32
+ }
30
33
  function printDivider() {
31
34
  console.log(pc.dim("\u2500".repeat(50)));
32
35
  }
@@ -36,8 +39,8 @@ function printVersion(version) {
36
39
  );
37
40
  console.log();
38
41
  }
39
- function highlight(text2) {
40
- return pc.cyan(text2);
42
+ function highlight(text3) {
43
+ return pc.cyan(text3);
41
44
  }
42
45
 
43
46
  // src/commands/init.ts
@@ -47,7 +50,7 @@ import pc2 from "picocolors";
47
50
  import { join as join3 } from "path";
48
51
 
49
52
  // src/utils/fs.ts
50
- import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, rmSync, statSync } from "fs";
53
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, rmSync, statSync, cpSync } from "fs";
51
54
  import { dirname, join, resolve } from "path";
52
55
  function ensureDir(path) {
53
56
  if (!existsSync(path)) {
@@ -72,6 +75,9 @@ function removeFile(path) {
72
75
  rmSync(path, { recursive: true });
73
76
  }
74
77
  }
78
+ function copyDir(src, dest) {
79
+ cpSync(src, dest, { recursive: true });
80
+ }
75
81
  function listFiles(dir, extension) {
76
82
  if (!dirExists(dir)) return [];
77
83
  const files = readdirSync(dir);
@@ -80,6 +86,13 @@ function listFiles(dir, extension) {
80
86
  }
81
87
  return files;
82
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
+ }
83
96
  function getCursorDir(cwd = process.cwd()) {
84
97
  return join(cwd, ".cursor");
85
98
  }
@@ -89,10 +102,17 @@ function getCommandsDir(cwd = process.cwd()) {
89
102
  function getRulesDir(cwd = process.cwd()) {
90
103
  return join(getCursorDir(cwd), "rules");
91
104
  }
105
+ function getSkillsDir(cwd = process.cwd()) {
106
+ return join(getCursorDir(cwd), "skills");
107
+ }
92
108
  function getConflictingFiles(dir, files) {
93
109
  if (!dirExists(dir)) return [];
94
110
  return files.filter((file) => fileExists(join(dir, file)));
95
111
  }
112
+ function getConflictingDirs(dir, dirs) {
113
+ if (!dirExists(dir)) return [];
114
+ return dirs.filter((d) => dirExists(join(dir, d)));
115
+ }
96
116
 
97
117
  // src/utils/templates.ts
98
118
  import { join as join2, dirname as dirname2 } from "path";
@@ -104,7 +124,8 @@ var REPO_REF = "master";
104
124
  var REPO_RAW_URL = "https://raw.githubusercontent.com/duongductrong/cursor-kit/master";
105
125
  var TEMPLATE_PATHS = {
106
126
  commands: "templates/commands",
107
- rules: "templates/rules"
127
+ rules: "templates/rules",
128
+ skills: "templates/skills"
108
129
  };
109
130
 
110
131
  // src/utils/templates.ts
@@ -124,12 +145,14 @@ function getLocalManifest() {
124
145
  }
125
146
  const commandsDir = join2(templatesDir, "commands");
126
147
  const rulesDir = join2(templatesDir, "rules");
127
- if (!dirExists(commandsDir) && !dirExists(rulesDir)) {
148
+ const skillsDir = join2(templatesDir, "skills");
149
+ if (!dirExists(commandsDir) && !dirExists(rulesDir) && !dirExists(skillsDir)) {
128
150
  return null;
129
151
  }
130
152
  return {
131
153
  commands: dirExists(commandsDir) ? listFiles(commandsDir, ".md") : [],
132
- rules: dirExists(rulesDir) ? listFiles(rulesDir, ".mdc") : []
154
+ rules: dirExists(rulesDir) ? listFiles(rulesDir, ".mdc") : [],
155
+ skills: dirExists(skillsDir) ? listDirs(skillsDir) : []
133
156
  };
134
157
  }
135
158
  function getLocalTemplateContent(type, filename) {
@@ -140,6 +163,22 @@ function getLocalTemplateContent(type, filename) {
140
163
  }
141
164
  return null;
142
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
+ }
143
182
  async function fetchTemplateManifest() {
144
183
  const localManifest = getLocalManifest();
145
184
  if (localManifest) {
@@ -183,9 +222,13 @@ function getTemplateLabel(filename) {
183
222
  const nameWithoutExt = filename.replace(/\.(md|mdc)$/, "");
184
223
  return nameWithoutExt.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
185
224
  }
225
+ function getSkillLabel(skillName) {
226
+ return skillName.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
227
+ }
186
228
 
187
229
  // src/commands/init.ts
188
230
  async function selectTemplates(type, availableTemplates) {
231
+ const labelFn = type === "skills" ? getSkillLabel : getTemplateLabel;
189
232
  const selectionMode = await p.select({
190
233
  message: `How would you like to add ${type}?`,
191
234
  options: [
@@ -209,7 +252,7 @@ async function selectTemplates(type, availableTemplates) {
209
252
  message: `Select ${type} to add:`,
210
253
  options: availableTemplates.map((template) => ({
211
254
  value: template,
212
- label: getTemplateLabel(template),
255
+ label: labelFn(template),
213
256
  hint: template
214
257
  })),
215
258
  required: true
@@ -273,10 +316,36 @@ async function installTemplates(type, targetDir, selectedTemplates, conflictStra
273
316
  }
274
317
  return result;
275
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
+ }
276
345
  var initCommand = defineCommand({
277
346
  meta: {
278
347
  name: "init",
279
- description: "Initialize .cursor/commands and .cursor/rules in your project"
348
+ description: "Initialize .cursor/commands, .cursor/rules, and .cursor/skills in your project"
280
349
  },
281
350
  args: {
282
351
  force: {
@@ -297,6 +366,12 @@ var initCommand = defineCommand({
297
366
  description: "Only initialize rules",
298
367
  default: false
299
368
  },
369
+ skills: {
370
+ type: "boolean",
371
+ alias: "s",
372
+ description: "Only initialize skills",
373
+ default: false
374
+ },
300
375
  all: {
301
376
  type: "boolean",
302
377
  alias: "a",
@@ -309,9 +384,11 @@ var initCommand = defineCommand({
309
384
  const cursorDir = getCursorDir(cwd);
310
385
  const commandsDir = getCommandsDir(cwd);
311
386
  const rulesDir = getRulesDir(cwd);
312
- const initBoth = !args.commands && !args.rules;
313
- const shouldInitCommands = initBoth || args.commands;
314
- 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;
315
392
  p.intro(pc2.bgCyan(pc2.black(" cursor-kit init ")));
316
393
  const s = p.spinner();
317
394
  let manifest;
@@ -394,6 +471,36 @@ var initCommand = defineCommand({
394
471
  );
395
472
  s.stop("Rules installed");
396
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
+ }
397
504
  printDivider();
398
505
  console.log();
399
506
  if (results.commands) {
@@ -424,8 +531,22 @@ var initCommand = defineCommand({
424
531
  }
425
532
  }
426
533
  }
427
- const totalAdded = (results.commands?.added.length ?? 0) + (results.rules?.added.length ?? 0);
428
- 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);
429
550
  if (totalAdded === 0 && totalSkipped > 0) {
430
551
  console.log();
431
552
  p.outro(pc2.yellow("No new templates added (all selected files already exist)"));
@@ -444,10 +565,10 @@ var initCommand = defineCommand({
444
565
  });
445
566
 
446
567
  // src/commands/add.ts
447
- import { defineCommand as defineCommand2 } from "citty";
448
568
  import * as p2 from "@clack/prompts";
449
- import pc3 from "picocolors";
569
+ import { defineCommand as defineCommand2 } from "citty";
450
570
  import { join as join4 } from "path";
571
+ import pc3 from "picocolors";
451
572
  var COMMAND_TEMPLATE = `You are a helpful assistant. Describe what this command does.
452
573
 
453
574
  ## Instructions
@@ -474,31 +595,69 @@ Describe the rule behavior here.
474
595
  - Guideline 1
475
596
  - Guideline 2
476
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
+ `;
477
636
  function generateSlug(name) {
478
637
  return name.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").trim();
479
638
  }
480
639
  var addCommand = defineCommand2({
481
640
  meta: {
482
641
  name: "add",
483
- description: "Add a new command or rule"
642
+ description: "Add a new command, rule, or skill"
484
643
  },
485
644
  args: {
486
645
  type: {
487
646
  type: "string",
488
647
  alias: "t",
489
- description: "Type: 'command' or 'rule'"
648
+ description: "Type: 'command', 'rule', or 'skill'"
490
649
  },
491
650
  name: {
492
651
  type: "string",
493
652
  alias: "n",
494
- description: "Name of the command or rule"
653
+ description: "Name of the command, rule, or skill"
495
654
  }
496
655
  },
497
656
  async run({ args }) {
498
657
  p2.intro(pc3.bgCyan(pc3.black(" cursor-kit add ")));
499
658
  let itemType;
500
659
  let itemName;
501
- if (args.type && ["command", "rule"].includes(args.type)) {
660
+ if (args.type && ["command", "rule", "skill"].includes(args.type)) {
502
661
  itemType = args.type;
503
662
  } else {
504
663
  const typeResult = await p2.select({
@@ -513,6 +672,11 @@ var addCommand = defineCommand2({
513
672
  value: "rule",
514
673
  label: "Rule",
515
674
  hint: "Project-specific AI behavior rules"
675
+ },
676
+ {
677
+ value: "skill",
678
+ label: "Skill",
679
+ hint: "Comprehensive guide with references"
516
680
  }
517
681
  ]
518
682
  });
@@ -527,7 +691,7 @@ var addCommand = defineCommand2({
527
691
  } else {
528
692
  const nameResult = await p2.text({
529
693
  message: `Enter ${itemType} name:`,
530
- placeholder: itemType === "command" ? "my-command" : "my-rule",
694
+ placeholder: itemType === "command" ? "my-command" : itemType === "rule" ? "my-rule" : "my-skill",
531
695
  validate: (value) => {
532
696
  if (!value.trim()) return "Name is required";
533
697
  if (value.length < 2) return "Name must be at least 2 characters";
@@ -542,12 +706,22 @@ var addCommand = defineCommand2({
542
706
  }
543
707
  const slug = generateSlug(itemName);
544
708
  const isCommand = itemType === "command";
545
- const targetDir = isCommand ? getCommandsDir() : getRulesDir();
546
- const extension = isCommand ? ".md" : ".mdc";
547
- const filePath = join4(targetDir, `${slug}${extension}`);
548
- 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)) {
549
723
  const shouldOverwrite = await p2.confirm({
550
- message: `${highlight(slug + extension)} already exists. Overwrite?`,
724
+ message: `${highlight(isSkill ? slug : slug + (isCommand ? ".md" : ".mdc"))} already exists. Overwrite?`,
551
725
  initialValue: false
552
726
  });
553
727
  if (p2.isCancel(shouldOverwrite) || !shouldOverwrite) {
@@ -558,12 +732,25 @@ var addCommand = defineCommand2({
558
732
  const s = p2.spinner();
559
733
  s.start(`Creating ${itemType}...`);
560
734
  try {
561
- ensureDir(targetDir);
562
- const template = isCommand ? COMMAND_TEMPLATE : RULE_TEMPLATE;
563
- 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
+ }
564
746
  s.stop(`${itemType.charAt(0).toUpperCase() + itemType.slice(1)} created`);
565
747
  console.log();
566
- 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
+ }
567
754
  console.log();
568
755
  p2.outro(
569
756
  pc3.green(`\u2728 ${itemType.charAt(0).toUpperCase() + itemType.slice(1)} created! Edit the file to customize it.`)
@@ -599,6 +786,12 @@ var pullCommand = defineCommand3({
599
786
  description: "Only pull rules",
600
787
  default: false
601
788
  },
789
+ skills: {
790
+ type: "boolean",
791
+ alias: "s",
792
+ description: "Only pull skills",
793
+ default: false
794
+ },
602
795
  force: {
603
796
  type: "boolean",
604
797
  alias: "f",
@@ -607,15 +800,18 @@ var pullCommand = defineCommand3({
607
800
  }
608
801
  },
609
802
  async run({ args }) {
610
- const pullBoth = !args.commands && !args.rules;
611
- const shouldPullCommands = pullBoth || args.commands;
612
- 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;
613
807
  p3.intro(pc4.bgCyan(pc4.black(" cursor-kit pull ")));
614
808
  const commandsDir = getCommandsDir();
615
809
  const rulesDir = getRulesDir();
810
+ const skillsDir = getSkillsDir();
616
811
  const existingCommands = listFiles(commandsDir, ".md");
617
812
  const existingRules = listFiles(rulesDir, ".mdc");
618
- 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;
619
815
  if (hasExisting && !args.force) {
620
816
  printInfo("Current status:");
621
817
  if (existingCommands.length > 0) {
@@ -624,6 +820,9 @@ var pullCommand = defineCommand3({
624
820
  if (existingRules.length > 0) {
625
821
  console.log(pc4.dim(` Rules: ${existingRules.length} files`));
626
822
  }
823
+ if (existingSkills.length > 0) {
824
+ console.log(pc4.dim(` Skills: ${existingSkills.length} directories`));
825
+ }
627
826
  console.log();
628
827
  const shouldContinue = await p3.confirm({
629
828
  message: "This will merge with existing files. Continue?",
@@ -653,10 +852,19 @@ var pullCommand = defineCommand3({
653
852
  });
654
853
  s.stop("Rules updated");
655
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
+ }
656
863
  printDivider();
657
864
  console.log();
658
865
  const newCommands = listFiles(commandsDir, ".md");
659
866
  const newRules = listFiles(rulesDir, ".mdc");
867
+ const newSkills = listDirs(skillsDir);
660
868
  if (shouldPullCommands) {
661
869
  const added = newCommands.length - existingCommands.length;
662
870
  printSuccess(
@@ -669,6 +877,12 @@ var pullCommand = defineCommand3({
669
877
  `Rules: ${highlight(newRules.length.toString())} total` + (added > 0 ? pc4.green(` (+${added} new)`) : "")
670
878
  );
671
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
+ }
672
886
  console.log();
673
887
  p3.outro(pc4.green("\u2728 Successfully pulled latest updates!"));
674
888
  } catch (error) {
@@ -680,10 +894,10 @@ var pullCommand = defineCommand3({
680
894
  });
681
895
 
682
896
  // src/commands/list.ts
683
- import { defineCommand as defineCommand4 } from "citty";
684
897
  import * as p4 from "@clack/prompts";
685
- import pc5 from "picocolors";
898
+ import { defineCommand as defineCommand4 } from "citty";
686
899
  import { join as join5 } from "path";
900
+ import pc5 from "picocolors";
687
901
  function extractDescription(content, isCommand) {
688
902
  if (isCommand) {
689
903
  const firstLine = content.trim().split("\n")[0];
@@ -710,10 +924,31 @@ function getItems(dir, extension, isCommand) {
710
924
  };
711
925
  });
712
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
+ }
713
948
  var listCommand = defineCommand4({
714
949
  meta: {
715
950
  name: "list",
716
- description: "List all commands and rules"
951
+ description: "List all commands, rules, and skills"
717
952
  },
718
953
  args: {
719
954
  commands: {
@@ -728,6 +963,12 @@ var listCommand = defineCommand4({
728
963
  description: "Only list rules",
729
964
  default: false
730
965
  },
966
+ skills: {
967
+ type: "boolean",
968
+ alias: "s",
969
+ description: "Only list skills",
970
+ default: false
971
+ },
731
972
  verbose: {
732
973
  type: "boolean",
733
974
  alias: "v",
@@ -736,18 +977,23 @@ var listCommand = defineCommand4({
736
977
  }
737
978
  },
738
979
  async run({ args }) {
739
- const listBoth = !args.commands && !args.rules;
740
- const shouldListCommands = listBoth || args.commands;
741
- 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;
742
984
  p4.intro(pc5.bgCyan(pc5.black(" cursor-kit list ")));
743
985
  const commandsDir = getCommandsDir();
744
986
  const rulesDir = getRulesDir();
987
+ const skillsDir = getSkillsDir();
745
988
  const commands = shouldListCommands ? getItems(commandsDir, ".md", true) : [];
746
989
  const rules = shouldListRules ? getItems(rulesDir, ".mdc", false) : [];
747
- if (commands.length === 0 && rules.length === 0) {
990
+ const skills = shouldListSkills ? getSkills(skillsDir) : [];
991
+ if (commands.length === 0 && rules.length === 0 && skills.length === 0) {
748
992
  console.log();
749
- console.log(pc5.yellow(" No commands or rules found."));
750
- 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
+ );
751
997
  console.log();
752
998
  p4.outro(pc5.dim("Nothing to show"));
753
999
  return;
@@ -755,7 +1001,9 @@ var listCommand = defineCommand4({
755
1001
  printDivider();
756
1002
  if (shouldListCommands && commands.length > 0) {
757
1003
  console.log();
758
- 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
+ );
759
1007
  console.log();
760
1008
  commands.forEach((cmd) => {
761
1009
  console.log(` ${pc5.green("\u25CF")} ${highlight(cmd.name)}`);
@@ -769,7 +1017,9 @@ var listCommand = defineCommand4({
769
1017
  }
770
1018
  if (shouldListRules && rules.length > 0) {
771
1019
  console.log();
772
- 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
+ );
773
1023
  console.log();
774
1024
  rules.forEach((rule) => {
775
1025
  console.log(` ${pc5.green("\u25CF")} ${highlight(rule.name)}`);
@@ -781,9 +1031,25 @@ var listCommand = defineCommand4({
781
1031
  }
782
1032
  });
783
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
+ }
784
1050
  console.log();
785
1051
  printDivider();
786
- const total = commands.length + rules.length;
1052
+ const total = commands.length + rules.length + skills.length;
787
1053
  p4.outro(pc5.dim(`Total: ${total} item${total !== 1 ? "s" : ""}`));
788
1054
  }
789
1055
  });
@@ -796,18 +1062,18 @@ import { join as join6 } from "path";
796
1062
  var removeCommand = defineCommand5({
797
1063
  meta: {
798
1064
  name: "remove",
799
- description: "Remove a command or rule"
1065
+ description: "Remove a command, rule, or skill"
800
1066
  },
801
1067
  args: {
802
1068
  type: {
803
1069
  type: "string",
804
1070
  alias: "t",
805
- description: "Type: 'command' or 'rule'"
1071
+ description: "Type: 'command', 'rule', or 'skill'"
806
1072
  },
807
1073
  name: {
808
1074
  type: "string",
809
1075
  alias: "n",
810
- description: "Name of the command or rule to remove"
1076
+ description: "Name of the command, rule, or skill to remove"
811
1077
  },
812
1078
  force: {
813
1079
  type: "boolean",
@@ -820,18 +1086,20 @@ var removeCommand = defineCommand5({
820
1086
  p5.intro(pc6.bgCyan(pc6.black(" cursor-kit remove ")));
821
1087
  const commandsDir = getCommandsDir();
822
1088
  const rulesDir = getRulesDir();
1089
+ const skillsDir = getSkillsDir();
823
1090
  const commands = listFiles(commandsDir, ".md").map((f) => f.replace(".md", ""));
824
1091
  const rules = listFiles(rulesDir, ".mdc").map((f) => f.replace(".mdc", ""));
825
- if (commands.length === 0 && rules.length === 0) {
1092
+ const skills = listDirs(skillsDir);
1093
+ if (commands.length === 0 && rules.length === 0 && skills.length === 0) {
826
1094
  console.log();
827
- console.log(pc6.yellow(" No commands or rules to remove."));
1095
+ console.log(pc6.yellow(" No commands, rules, or skills to remove."));
828
1096
  console.log();
829
1097
  p5.outro(pc6.dim("Nothing to do"));
830
1098
  return;
831
1099
  }
832
1100
  let itemType;
833
1101
  let itemName;
834
- if (args.type && ["command", "rule"].includes(args.type)) {
1102
+ if (args.type && ["command", "rule", "skill"].includes(args.type)) {
835
1103
  itemType = args.type;
836
1104
  } else {
837
1105
  const typeOptions = [];
@@ -849,6 +1117,13 @@ var removeCommand = defineCommand5({
849
1117
  hint: `${rules.length} available`
850
1118
  });
851
1119
  }
1120
+ if (skills.length > 0) {
1121
+ typeOptions.push({
1122
+ value: "skill",
1123
+ label: "Skill",
1124
+ hint: `${skills.length} available`
1125
+ });
1126
+ }
852
1127
  const typeResult = await p5.select({
853
1128
  message: "What do you want to remove?",
854
1129
  options: typeOptions
@@ -860,9 +1135,11 @@ var removeCommand = defineCommand5({
860
1135
  itemType = typeResult;
861
1136
  }
862
1137
  const isCommand = itemType === "command";
863
- const items = isCommand ? commands : rules;
864
- const dir = isCommand ? commandsDir : rulesDir;
865
- 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" : "";
866
1143
  if (items.length === 0) {
867
1144
  p5.cancel(`No ${itemType}s found`);
868
1145
  process.exit(0);
@@ -884,14 +1161,16 @@ var removeCommand = defineCommand5({
884
1161
  }
885
1162
  itemName = nameResult;
886
1163
  }
887
- const filePath = join6(dir, `${itemName}${extension}`);
888
- 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) {
889
1167
  p5.cancel(`${itemType} '${itemName}' not found`);
890
1168
  process.exit(1);
891
1169
  }
892
1170
  if (!args.force) {
1171
+ const displayName = isSkill ? itemName : itemName + extension;
893
1172
  const shouldDelete = await p5.confirm({
894
- 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)" : ""}`,
895
1174
  initialValue: false
896
1175
  });
897
1176
  if (p5.isCancel(shouldDelete) || !shouldDelete) {
@@ -900,9 +1179,10 @@ var removeCommand = defineCommand5({
900
1179
  }
901
1180
  }
902
1181
  try {
903
- removeFile(filePath);
1182
+ removeFile(targetPath);
1183
+ const displayName = isSkill ? itemName : itemName + extension;
904
1184
  console.log();
905
- printSuccess(`Removed ${highlight(itemName + extension)}`);
1185
+ printSuccess(`Removed ${highlight(displayName)}`);
906
1186
  console.log();
907
1187
  p5.outro(pc6.green("\u2728 Done!"));
908
1188
  } catch (error) {
@@ -912,10 +1192,268 @@ var removeCommand = defineCommand5({
912
1192
  }
913
1193
  });
914
1194
 
1195
+ // src/commands/instance.ts
1196
+ import { defineCommand as defineCommand6 } from "citty";
1197
+ import * as p6 from "@clack/prompts";
1198
+ import pc7 from "picocolors";
1199
+ import { spawn } from "child_process";
1200
+ import { join as join7, dirname as dirname3 } from "path";
1201
+ import { fileURLToPath as fileURLToPath2 } from "url";
1202
+ import { existsSync as existsSync2, chmodSync, readdirSync as readdirSync2 } from "fs";
1203
+ function getBinPath() {
1204
+ const currentDir = dirname3(fileURLToPath2(import.meta.url));
1205
+ const possiblePaths = [
1206
+ join7(currentDir, "..", "..", "bin"),
1207
+ join7(currentDir, "..", "bin")
1208
+ ];
1209
+ for (const binPath of possiblePaths) {
1210
+ if (existsSync2(binPath)) {
1211
+ return binPath;
1212
+ }
1213
+ }
1214
+ return possiblePaths[0];
1215
+ }
1216
+ function ensureExecutable(scriptPath) {
1217
+ try {
1218
+ chmodSync(scriptPath, 493);
1219
+ } catch {
1220
+ }
1221
+ }
1222
+ function getExistingInstances() {
1223
+ const userAppsDir = join7(process.env.HOME ?? "", "Applications");
1224
+ if (!existsSync2(userAppsDir)) return [];
1225
+ try {
1226
+ const apps = readdirSync2(userAppsDir);
1227
+ return apps.filter((app) => app.startsWith("Cursor") && app.endsWith(".app") && app !== "Cursor.app").map((app) => ({
1228
+ name: app.replace(".app", ""),
1229
+ path: join7(userAppsDir, app)
1230
+ }));
1231
+ } catch {
1232
+ return [];
1233
+ }
1234
+ }
1235
+ function runScript(scriptPath, args) {
1236
+ return new Promise((resolve2) => {
1237
+ ensureExecutable(scriptPath);
1238
+ const child = spawn(scriptPath, args, {
1239
+ stdio: "inherit"
1240
+ });
1241
+ child.on("close", (code) => {
1242
+ resolve2(code ?? 1);
1243
+ });
1244
+ child.on("error", () => {
1245
+ resolve2(1);
1246
+ });
1247
+ });
1248
+ }
1249
+ var instanceCommand = defineCommand6({
1250
+ meta: {
1251
+ name: "instance",
1252
+ description: "Manage Cursor IDE instances for multi-account login (macOS only)"
1253
+ },
1254
+ args: {
1255
+ action: {
1256
+ type: "string",
1257
+ alias: "a",
1258
+ description: "Action: 'create' or 'remove'"
1259
+ },
1260
+ name: {
1261
+ type: "string",
1262
+ alias: "n",
1263
+ description: "Name of the instance (e.g. 'Cursor Enterprise')"
1264
+ },
1265
+ list: {
1266
+ type: "boolean",
1267
+ alias: "l",
1268
+ description: "List existing Cursor instances",
1269
+ default: false
1270
+ }
1271
+ },
1272
+ async run({ args }) {
1273
+ p6.intro(pc7.bgCyan(pc7.black(" cursor-kit instance ")));
1274
+ if (process.platform !== "darwin") {
1275
+ console.log();
1276
+ printWarning("This command only works on macOS.");
1277
+ console.log(pc7.dim(" Cursor instance management requires macOS-specific features."));
1278
+ console.log();
1279
+ p6.outro(pc7.dim("Exiting"));
1280
+ process.exit(1);
1281
+ }
1282
+ if (args.list) {
1283
+ const instances = getExistingInstances();
1284
+ printDivider();
1285
+ console.log();
1286
+ if (instances.length === 0) {
1287
+ printInfo("No custom Cursor instances found.");
1288
+ console.log(pc7.dim(" Run ") + highlight("cursor-kit instance") + pc7.dim(" to create one."));
1289
+ } else {
1290
+ console.log(pc7.bold(pc7.cyan(" \u{1F5A5} Cursor Instances")) + pc7.dim(` (${instances.length})`));
1291
+ console.log();
1292
+ for (const instance of instances) {
1293
+ console.log(` ${pc7.green("\u25CF")} ${highlight(instance.name)}`);
1294
+ console.log(pc7.dim(` \u2514\u2500 ${instance.path}`));
1295
+ }
1296
+ }
1297
+ console.log();
1298
+ printDivider();
1299
+ p6.outro(pc7.dim(`Total: ${instances.length} instance${instances.length !== 1 ? "s" : ""}`));
1300
+ return;
1301
+ }
1302
+ const s = p6.spinner();
1303
+ s.start("Checking prerequisites...");
1304
+ const binPath = getBinPath();
1305
+ const createScript = join7(binPath, "cursor-new-instance");
1306
+ const removeScript = join7(binPath, "cursor-remove-instance");
1307
+ const scriptsExist = existsSync2(createScript) && existsSync2(removeScript);
1308
+ if (!scriptsExist) {
1309
+ s.stop("Prerequisites check failed");
1310
+ console.log();
1311
+ printWarning("Required scripts not found.");
1312
+ console.log(pc7.dim(` Expected at: ${binPath}`));
1313
+ console.log();
1314
+ p6.outro(pc7.red("Installation may be corrupted"));
1315
+ process.exit(1);
1316
+ }
1317
+ const originalCursor = "/Applications/Cursor.app";
1318
+ if (!existsSync2(originalCursor)) {
1319
+ s.stop("Prerequisites check failed");
1320
+ console.log();
1321
+ printWarning("Cursor.app not found in /Applications");
1322
+ console.log(pc7.dim(" Please install Cursor IDE first."));
1323
+ console.log();
1324
+ p6.outro(pc7.red("Cursor IDE required"));
1325
+ process.exit(1);
1326
+ }
1327
+ s.stop("Prerequisites verified");
1328
+ const existingInstances = getExistingInstances();
1329
+ let action;
1330
+ let instanceName;
1331
+ if (args.action && ["create", "remove"].includes(args.action)) {
1332
+ action = args.action;
1333
+ } else {
1334
+ const actionResult = await p6.select({
1335
+ message: "What would you like to do?",
1336
+ options: [
1337
+ {
1338
+ value: "create",
1339
+ label: "Create new instance",
1340
+ hint: "Clone Cursor with separate identity"
1341
+ },
1342
+ {
1343
+ value: "remove",
1344
+ label: "Remove instance",
1345
+ hint: existingInstances.length > 0 ? `${existingInstances.length} instance${existingInstances.length !== 1 ? "s" : ""} available` : "No instances to remove"
1346
+ }
1347
+ ]
1348
+ });
1349
+ if (p6.isCancel(actionResult)) {
1350
+ p6.cancel("Operation cancelled");
1351
+ process.exit(0);
1352
+ }
1353
+ action = actionResult;
1354
+ }
1355
+ if (args.name) {
1356
+ instanceName = args.name;
1357
+ } else if (action === "remove" && existingInstances.length > 0) {
1358
+ const instanceResult = await p6.select({
1359
+ message: "Select instance to remove:",
1360
+ options: existingInstances.map((inst) => ({
1361
+ value: inst.name,
1362
+ label: inst.name,
1363
+ hint: inst.path
1364
+ }))
1365
+ });
1366
+ if (p6.isCancel(instanceResult)) {
1367
+ p6.cancel("Operation cancelled");
1368
+ process.exit(0);
1369
+ }
1370
+ instanceName = instanceResult;
1371
+ } else if (action === "remove" && existingInstances.length === 0) {
1372
+ console.log();
1373
+ printInfo("No custom Cursor instances found to remove.");
1374
+ console.log();
1375
+ p6.outro(pc7.dim("Nothing to do"));
1376
+ return;
1377
+ } else {
1378
+ const nameResult = await p6.text({
1379
+ message: "Enter a name for the new instance:",
1380
+ placeholder: "Cursor Enterprise",
1381
+ validate: (value) => {
1382
+ if (!value.trim()) return "Instance name is required";
1383
+ if (value.length < 2) return "Name must be at least 2 characters";
1384
+ const existing = existingInstances.find(
1385
+ (i) => i.name.toLowerCase() === value.toLowerCase()
1386
+ );
1387
+ if (existing) return `Instance "${value}" already exists`;
1388
+ return void 0;
1389
+ }
1390
+ });
1391
+ if (p6.isCancel(nameResult)) {
1392
+ p6.cancel("Operation cancelled");
1393
+ process.exit(0);
1394
+ }
1395
+ instanceName = nameResult;
1396
+ }
1397
+ printDivider();
1398
+ console.log();
1399
+ console.log(pc7.bold(pc7.cyan(" \u{1F4CB} Summary")));
1400
+ console.log();
1401
+ console.log(` ${pc7.dim("Action:")} ${action === "create" ? pc7.green("Create") : pc7.yellow("Remove")}`);
1402
+ console.log(` ${pc7.dim("Instance:")} ${highlight(instanceName)}`);
1403
+ if (action === "create") {
1404
+ const slug = instanceName.toLowerCase().replace(/[^a-z0-9]/g, "");
1405
+ console.log(` ${pc7.dim("Bundle ID:")} ${pc7.dim("com.cursor.")}${highlight(slug)}`);
1406
+ console.log(` ${pc7.dim("Location:")} ${pc7.dim("~/Applications/")}${highlight(instanceName + ".app")}`);
1407
+ } else {
1408
+ const targetPath = join7(process.env.HOME ?? "", "Applications", `${instanceName}.app`);
1409
+ console.log(` ${pc7.dim("Path:")} ${pc7.dim(targetPath)}`);
1410
+ }
1411
+ console.log();
1412
+ printDivider();
1413
+ console.log();
1414
+ const shouldContinue = await p6.confirm({
1415
+ message: action === "create" ? "Create this Cursor instance?" : "Remove this Cursor instance? This cannot be undone.",
1416
+ initialValue: action === "create"
1417
+ });
1418
+ if (p6.isCancel(shouldContinue) || !shouldContinue) {
1419
+ p6.cancel("Operation cancelled");
1420
+ process.exit(0);
1421
+ }
1422
+ console.log();
1423
+ printDivider();
1424
+ console.log();
1425
+ const scriptPath = action === "create" ? createScript : removeScript;
1426
+ const scriptArgs = action === "remove" ? ["--yes", instanceName] : [instanceName];
1427
+ const exitCode = await runScript(scriptPath, scriptArgs);
1428
+ console.log();
1429
+ printDivider();
1430
+ console.log();
1431
+ if (exitCode === 0) {
1432
+ if (action === "create") {
1433
+ printSuccess(`Instance ${highlight(instanceName)} created successfully!`);
1434
+ console.log();
1435
+ console.log(pc7.dim(" Next steps:"));
1436
+ console.log(pc7.dim(" \u2022 The new instance should launch automatically"));
1437
+ console.log(pc7.dim(" \u2022 Sign in with a different Cursor account"));
1438
+ console.log(pc7.dim(" \u2022 Find it in ~/Applications/"));
1439
+ } else {
1440
+ printSuccess(`Instance ${highlight(instanceName)} removed successfully!`);
1441
+ }
1442
+ console.log();
1443
+ p6.outro(pc7.green("\u2728 Done!"));
1444
+ } else {
1445
+ printWarning(`Operation completed with exit code ${exitCode}`);
1446
+ console.log();
1447
+ p6.outro(pc7.yellow("Check the output above for details"));
1448
+ process.exit(exitCode);
1449
+ }
1450
+ }
1451
+ });
1452
+
915
1453
  // src/cli.ts
916
1454
  var require2 = createRequire(import.meta.url);
917
1455
  var pkg = require2("../package.json");
918
- var main = defineCommand6({
1456
+ var main = defineCommand7({
919
1457
  meta: {
920
1458
  name: "cursor-kit",
921
1459
  version: pkg.version,
@@ -930,7 +1468,8 @@ var main = defineCommand6({
930
1468
  add: addCommand,
931
1469
  pull: pullCommand,
932
1470
  list: listCommand,
933
- remove: removeCommand
1471
+ remove: removeCommand,
1472
+ instance: instanceCommand
934
1473
  }
935
1474
  });
936
1475
  runMain(main);