cursor-kit-cli 1.3.0-beta → 1.3.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.
package/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 duongductrong
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md CHANGED
@@ -5,8 +5,8 @@
5
5
  <h1 align="center">✦ Cursor Kit ✦</h1>
6
6
 
7
7
  <p align="center">
8
- <b>Supercharge your Cursor IDE with rules & commands</b><br/>
9
- <sub>A CLI toolkit to manage, share, and sync Cursor IDE configurations</sub>
8
+ <b>Supercharge your AI IDE with rules & commands</b><br/>
9
+ <sub>A CLI toolkit to manage, share, and sync Cursor IDE and GitHub Copilot configurations</sub>
10
10
  </p>
11
11
 
12
12
  <p align="center">
@@ -44,6 +44,7 @@ ck init
44
44
  - **📋 Rules** - Project-specific AI behavior guidelines
45
45
  - **🎓 Skills** - Comprehensive guides with references for specialized domains
46
46
  - **🔄 Sync** - Keep configurations updated from the community
47
+ - **🎯 Multi-Target** - Support for both Cursor IDE and GitHub Copilot
47
48
  - **🖥️ Multi-Instance** - Run multiple Cursor accounts simultaneously (macOS)
48
49
  - **🎨 Beautiful CLI** - Delightful terminal experience
49
50
 
@@ -51,17 +52,23 @@ ck init
51
52
 
52
53
  ### `init`
53
54
 
54
- Initialize `.cursor/commands`, `.cursor/rules`, and `.cursor/skills` in your project with curated templates.
55
+ Initialize `.cursor/commands`, `.cursor/rules`, and `.cursor/skills` in your project with curated templates. Supports both Cursor IDE and GitHub Copilot.
55
56
 
56
57
  ```bash
57
- cursor-kit init # Initialize commands, rules, and skills
58
- cursor-kit init -c # Only initialize commands
59
- cursor-kit init -r # Only initialize rules
60
- cursor-kit init -s # Only initialize skills
61
- cursor-kit init -f # Force overwrite existing files
62
- cursor-kit init -a # Install all templates without selection prompts
58
+ cursor-kit init # Interactive: choose Cursor or GitHub Copilot
59
+ cursor-kit init -t cursor # Initialize for Cursor IDE (.cursor/)
60
+ cursor-kit init -t github-copilot # Initialize for GitHub Copilot (.github/copilot-instructions/)
61
+ cursor-kit init -c # Only initialize commands
62
+ cursor-kit init -r # Only initialize rules
63
+ cursor-kit init -s # Only initialize skills
64
+ cursor-kit init -f # Force overwrite existing files
65
+ cursor-kit init -a # Install all templates without selection prompts
63
66
  ```
64
67
 
68
+ **Target options:**
69
+ - `cursor` (default) - Creates `.cursor/` directory structure for Cursor IDE
70
+ - `github-copilot` - Creates `.github/copilot-instructions.md` and related structure for GitHub Copilot
71
+
65
72
  ### `add`
66
73
 
67
74
  Interactively create a new command, rule, or skill with a starter template.
@@ -152,7 +159,9 @@ cursor-kit instance -a remove -n "Cursor Personal"
152
159
 
153
160
  ## 📁 Directory Structure
154
161
 
155
- After running `cursor-kit init`, your project will have:
162
+ After running `cursor-kit init`, your project will have different structures depending on the target:
163
+
164
+ ### Cursor IDE (default)
156
165
 
157
166
  ```
158
167
  your-project/
@@ -197,6 +206,36 @@ your-project/
197
206
  └── references/
198
207
  ```
199
208
 
209
+ ### GitHub Copilot
210
+
211
+ ```
212
+ your-project/
213
+ └── .github/
214
+ ├── copilot-instructions.md # Main instructions file
215
+ └── copilot-instructions/ # Organized instructions
216
+ ├── commands/ # Prompt templates (.md)
217
+ │ ├── docs.md
218
+ │ ├── explain.md
219
+ │ ├── fix.md
220
+ │ ├── implement.md
221
+ │ ├── refactor.md
222
+ │ ├── review.md
223
+ │ └── test.md
224
+ ├── rules/ # AI behavior rules (.md)
225
+ │ ├── coding-style.md
226
+ │ ├── git.md
227
+ │ └── toc.md
228
+ └── skills/ # Comprehensive guides with references
229
+ ├── aesthetic/
230
+ │ ├── SKILL.md
231
+ │ ├── assets/
232
+ │ └── references/
233
+ ├── backend-development/
234
+ │ ├── SKILL.md
235
+ │ └── references/
236
+ └── ... (other skills)
237
+ ```
238
+
200
239
  ## 🎯 Included Templates
201
240
 
202
241
  ### Commands
package/dist/cli.cjs CHANGED
@@ -201,7 +201,7 @@ function getLocalManifest() {
201
201
  }
202
202
  return {
203
203
  commands: dirExists(commandsDir) ? listFiles(commandsDir, ".md") : [],
204
- rules: dirExists(rulesDir) ? listFiles(rulesDir, ".mdc") : [],
204
+ rules: dirExists(rulesDir) ? listFiles(rulesDir, ".md") : [],
205
205
  skills: dirExists(skillsDir) ? listDirs(skillsDir) : []
206
206
  };
207
207
  }
@@ -211,6 +211,12 @@ function getLocalTemplateContent(type, filename) {
211
211
  if (fileExists(filePath)) {
212
212
  return readFile(filePath);
213
213
  }
214
+ if (type === "rules" && filename.endsWith(".md")) {
215
+ const mdcPath = filePath.replace(/\.md$/, ".mdc");
216
+ if (fileExists(mdcPath)) {
217
+ return readFile(mdcPath);
218
+ }
219
+ }
214
220
  return null;
215
221
  }
216
222
  function getLocalSkillDir(skillName) {
@@ -221,12 +227,29 @@ function getLocalSkillDir(skillName) {
221
227
  }
222
228
  return null;
223
229
  }
224
- function copyLocalSkill(skillName, targetDir) {
230
+ function convertMdToMdc(filename) {
231
+ return filename.replace(/\.md$/, ".mdc");
232
+ }
233
+ function transformTocContentForCursor(content) {
234
+ content = content.replace(/\]\(\.\.\/skills\/([^/]+)\/SKILL\.md\)/g, "](../skills/$1/SKILL.mdc)");
235
+ content = content.replace(/\]\(\.\/([^)]+)\.md\)/g, "](./$1.mdc)");
236
+ return content;
237
+ }
238
+ function copyLocalSkill(skillName, targetDir, convertToMdc = false) {
225
239
  const sourcePath = getLocalSkillDir(skillName);
226
240
  if (!sourcePath) return false;
227
241
  const destPath = (0, import_node_path2.join)(targetDir, skillName);
228
242
  ensureDir(destPath);
229
243
  copyDir(sourcePath, destPath);
244
+ if (convertToMdc) {
245
+ const skillMdPath = (0, import_node_path2.join)(destPath, "SKILL.md");
246
+ const skillMdcPath = (0, import_node_path2.join)(destPath, "SKILL.mdc");
247
+ if (fileExists(skillMdPath)) {
248
+ const content = readFile(skillMdPath);
249
+ writeFile(skillMdcPath, content);
250
+ deleteFile(skillMdPath);
251
+ }
252
+ }
230
253
  return true;
231
254
  }
232
255
  async function fetchTemplateManifest() {
@@ -372,7 +395,7 @@ async function installCopilotRules(rulesDir, selectedRules) {
372
395
  for (const [filename, content] of rulesMap) {
373
396
  const frontmatter = extractFrontmatter(content);
374
397
  const cleanContent = stripFrontmatter(content);
375
- const mdFilename = filename.replace(/\.mdc$/, ".md");
398
+ const mdFilename = filename.endsWith(".md") ? filename : filename.replace(/\.mdc$/, ".md");
376
399
  const filePath = (0, import_node_path3.join)(rulesDir, mdFilename);
377
400
  writeFile(filePath, cleanContent);
378
401
  installed.push(mdFilename);
@@ -392,7 +415,11 @@ async function installCopilotSkills(skillsDir, selectedSkills) {
392
415
  const skillTargetDir = (0, import_node_path3.join)(skillsDir, skillName);
393
416
  const skillMdcPath = (0, import_node_path3.join)(skillTargetDir, "SKILL.mdc");
394
417
  const skillMdPath = (0, import_node_path3.join)(skillTargetDir, "SKILL.md");
395
- if (fileExists(skillMdcPath)) {
418
+ if (fileExists(skillMdPath)) {
419
+ const content = readFile(skillMdPath);
420
+ const cleanContent = stripFrontmatter(content);
421
+ writeFile(skillMdPath, cleanContent);
422
+ } else if (fileExists(skillMdcPath)) {
396
423
  const content = readFile(skillMdcPath);
397
424
  const cleanContent = stripFrontmatter(content);
398
425
  writeFile(skillMdPath, cleanContent);
@@ -619,16 +646,25 @@ async function handleConflicts(type, conflictingFiles) {
619
646
  });
620
647
  return strategy;
621
648
  }
622
- async function installTemplates(type, targetDir, selectedTemplates, conflictStrategy) {
649
+ async function installTemplates(type, targetDir, selectedTemplates, conflictStrategy, target) {
623
650
  const result = { added: [], skipped: [] };
624
- const conflictingFiles = getConflictingFiles(targetDir, selectedTemplates);
651
+ const expectedFilenames = selectedTemplates.map((filename) => {
652
+ if (target === "cursor" && type === "rules" && filename.endsWith(".md")) {
653
+ return convertMdToMdc(filename);
654
+ }
655
+ return filename;
656
+ });
657
+ const conflictingFiles = getConflictingFiles(targetDir, expectedFilenames);
625
658
  let templatesToInstall;
626
659
  if (conflictStrategy === "merge") {
627
660
  templatesToInstall = selectedTemplates.filter(
628
- (t) => !conflictingFiles.includes(t)
661
+ (t) => {
662
+ const expectedName = target === "cursor" && type === "rules" && t.endsWith(".md") ? convertMdToMdc(t) : t;
663
+ return !conflictingFiles.includes(expectedName);
664
+ }
629
665
  );
630
666
  result.skipped = conflictingFiles.filter(
631
- (f) => selectedTemplates.includes(f)
667
+ (f) => expectedFilenames.includes(f)
632
668
  );
633
669
  } else {
634
670
  templatesToInstall = selectedTemplates;
@@ -639,13 +675,18 @@ async function installTemplates(type, targetDir, selectedTemplates, conflictStra
639
675
  const templates = await fetchMultipleTemplates(type, templatesToInstall);
640
676
  ensureDir(targetDir);
641
677
  for (const [filename, content] of templates) {
642
- const filePath = (0, import_node_path4.join)(targetDir, filename);
643
- writeFile(filePath, content);
644
- result.added.push(filename);
678
+ const outputFilename = target === "cursor" && type === "rules" && filename.endsWith(".md") ? convertMdToMdc(filename) : filename;
679
+ let transformedContent = content;
680
+ if (target === "cursor" && type === "rules" && filename === "toc.md") {
681
+ transformedContent = transformTocContentForCursor(content);
682
+ }
683
+ const filePath = (0, import_node_path4.join)(targetDir, outputFilename);
684
+ writeFile(filePath, transformedContent);
685
+ result.added.push(outputFilename);
645
686
  }
646
687
  return result;
647
688
  }
648
- async function installSkills(targetDir, selectedSkills, conflictStrategy) {
689
+ async function installSkills(targetDir, selectedSkills, conflictStrategy, target) {
649
690
  const result = { added: [], skipped: [] };
650
691
  const conflictingDirs = getConflictingDirs(targetDir, selectedSkills);
651
692
  let skillsToInstall;
@@ -663,8 +704,9 @@ async function installSkills(targetDir, selectedSkills, conflictStrategy) {
663
704
  return result;
664
705
  }
665
706
  ensureDir(targetDir);
707
+ const convertToMdc = target === "cursor";
666
708
  for (const skillName of skillsToInstall) {
667
- const success = copyLocalSkill(skillName, targetDir);
709
+ const success = copyLocalSkill(skillName, targetDir, convertToMdc);
668
710
  if (success) {
669
711
  result.added.push(skillName);
670
712
  }
@@ -793,7 +835,8 @@ var initCommand = (0, import_citty.defineCommand)({
793
835
  "commands",
794
836
  commandsDir,
795
837
  selectedCommands,
796
- commandStrategy
838
+ commandStrategy,
839
+ target
797
840
  );
798
841
  s.stop("Commands installed");
799
842
  }
@@ -809,7 +852,10 @@ var initCommand = (0, import_citty.defineCommand)({
809
852
  }
810
853
  selectedRules = selection;
811
854
  }
812
- const conflictingRules = getConflictingFiles(rulesDir, selectedRules);
855
+ const expectedRuleFilenames = selectedRules.map((filename) => {
856
+ return target === "cursor" && filename.endsWith(".md") ? convertMdToMdc(filename) : filename;
857
+ });
858
+ const conflictingRules = getConflictingFiles(rulesDir, expectedRuleFilenames);
813
859
  let ruleStrategy = "overwrite";
814
860
  if (conflictingRules.length > 0 && !args.force) {
815
861
  const strategy = await handleConflicts("rules", conflictingRules);
@@ -824,7 +870,8 @@ var initCommand = (0, import_citty.defineCommand)({
824
870
  "rules",
825
871
  rulesDir,
826
872
  selectedRules,
827
- ruleStrategy
873
+ ruleStrategy,
874
+ target
828
875
  );
829
876
  s.stop("Rules installed");
830
877
  }
@@ -854,7 +901,8 @@ var initCommand = (0, import_citty.defineCommand)({
854
901
  results.skills = await installSkills(
855
902
  skillsDir,
856
903
  selectedSkills,
857
- skillStrategy
904
+ skillStrategy,
905
+ target
858
906
  );
859
907
  s.stop("Skills installed");
860
908
  }
@@ -1072,13 +1120,16 @@ var addCommand = (0, import_citty2.defineCommand)({
1072
1120
  displayPath = targetPath;
1073
1121
  } else {
1074
1122
  const targetDir = isCommand ? getCommandsDir() : getRulesDir();
1075
- const extension = isCommand ? ".md" : ".mdc";
1123
+ const extension = ".md";
1076
1124
  targetPath = (0, import_node_path5.join)(targetDir, `${slug}${extension}`);
1077
1125
  displayPath = targetPath;
1078
1126
  }
1079
- if (isSkill ? dirExists(targetPath) : fileExists(targetPath)) {
1127
+ const checkPath = isSkill ? targetPath : isCommand ? targetPath : (0, import_node_path5.join)(getRulesDir(), `${slug}.mdc`);
1128
+ const exists = isSkill ? dirExists(targetPath) : fileExists(targetPath) || !isCommand && fileExists(checkPath);
1129
+ if (exists) {
1130
+ const displayName = isSkill ? slug : isCommand ? `${slug}.md` : `${slug}.md/.mdc`;
1080
1131
  const shouldOverwrite = await p3.confirm({
1081
- message: `${highlight(isSkill ? slug : slug + (isCommand ? ".md" : ".mdc"))} already exists. Overwrite?`,
1132
+ message: `${highlight(displayName)} already exists. Overwrite?`,
1082
1133
  initialValue: false
1083
1134
  });
1084
1135
  if (p3.isCancel(shouldOverwrite) || !shouldOverwrite) {
@@ -1092,21 +1143,37 @@ var addCommand = (0, import_citty2.defineCommand)({
1092
1143
  if (isSkill) {
1093
1144
  ensureDir(targetPath);
1094
1145
  ensureDir((0, import_node_path5.join)(targetPath, "references"));
1095
- writeFile((0, import_node_path5.join)(targetPath, "SKILL.mdc"), SKILL_TEMPLATE);
1146
+ const skillMdPath = (0, import_node_path5.join)(targetPath, "SKILL.md");
1147
+ writeFile(skillMdPath, SKILL_TEMPLATE);
1096
1148
  writeFile((0, import_node_path5.join)(targetPath, "references", "example.md"), SKILL_REFERENCE_TEMPLATE);
1149
+ const skillMdcPath = (0, import_node_path5.join)(targetPath, "SKILL.mdc");
1150
+ const content = readFile(skillMdPath);
1151
+ writeFile(skillMdcPath, content);
1152
+ deleteFile(skillMdPath);
1097
1153
  } else {
1098
1154
  const targetDir = isCommand ? getCommandsDir() : getRulesDir();
1099
1155
  ensureDir(targetDir);
1100
1156
  const template = isCommand ? COMMAND_TEMPLATE : RULE_TEMPLATE;
1101
- writeFile(targetPath, template);
1157
+ if (isCommand) {
1158
+ writeFile(targetPath, template);
1159
+ } else {
1160
+ writeFile(targetPath, template);
1161
+ const mdcPath = (0, import_node_path5.join)(targetDir, convertMdToMdc(`${slug}.md`));
1162
+ const content = readFile(targetPath);
1163
+ writeFile(mdcPath, content);
1164
+ deleteFile(targetPath);
1165
+ }
1102
1166
  }
1103
1167
  s.stop(`${itemType.charAt(0).toUpperCase() + itemType.slice(1)} created`);
1104
1168
  console.log();
1105
1169
  if (isSkill) {
1106
1170
  console.log(import_picocolors4.default.dim(" Directory: ") + highlight(displayPath));
1107
1171
  console.log(import_picocolors4.default.dim(" Main file: ") + highlight((0, import_node_path5.join)(displayPath, "SKILL.mdc")));
1108
- } else {
1172
+ } else if (isCommand) {
1109
1173
  console.log(import_picocolors4.default.dim(" File: ") + highlight(displayPath));
1174
+ } else {
1175
+ const mdcPath = (0, import_node_path5.join)(getRulesDir(), convertMdToMdc(`${slug}.md`));
1176
+ console.log(import_picocolors4.default.dim(" File: ") + highlight(mdcPath));
1110
1177
  }
1111
1178
  console.log();
1112
1179
  p3.outro(