cursor-kit-cli 1.0.4-beta.2 → 1.1.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/dist/cli.cjs CHANGED
@@ -23,8 +23,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  mod
24
24
  ));
25
25
 
26
+ // node_modules/.pnpm/tsup@8.5.1_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js
27
+ var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
28
+ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
29
+
26
30
  // src/cli.ts
27
31
  var import_citty6 = require("citty");
32
+ var import_node_module = require("module");
28
33
 
29
34
  // src/utils/branding.ts
30
35
  var import_figlet = __toESM(require("figlet"), 1);
@@ -54,7 +59,7 @@ function printDivider() {
54
59
  }
55
60
  function printVersion(version) {
56
61
  console.log(
57
- import_picocolors.default.dim(" v") + cursorGradient(version) + import_picocolors.default.dim(" \u2022 Made with \u2665")
62
+ import_picocolors.default.dim(" ") + cursorGradient(`v${version}`) + import_picocolors.default.dim(" \u2022 Made with \u2665")
58
63
  );
59
64
  console.log();
60
65
  }
@@ -66,7 +71,7 @@ function highlight(text2) {
66
71
  var import_citty = require("citty");
67
72
  var p = __toESM(require("@clack/prompts"), 1);
68
73
  var import_picocolors2 = __toESM(require("picocolors"), 1);
69
- var import_giget = require("giget");
74
+ var import_node_path3 = require("path");
70
75
 
71
76
  // src/utils/fs.ts
72
77
  var import_node_fs = require("fs");
@@ -111,12 +116,190 @@ function getCommandsDir(cwd = process.cwd()) {
111
116
  function getRulesDir(cwd = process.cwd()) {
112
117
  return (0, import_node_path.join)(getCursorDir(cwd), "rules");
113
118
  }
119
+ function getConflictingFiles(dir, files) {
120
+ if (!dirExists(dir)) return [];
121
+ return files.filter((file) => fileExists((0, import_node_path.join)(dir, file)));
122
+ }
123
+
124
+ // src/utils/templates.ts
125
+ var import_node_path2 = require("path");
126
+ var import_node_url = require("url");
114
127
 
115
128
  // src/utils/constants.ts
116
129
  var REPO_URL = "github:duongductrong/cursor-kit";
117
130
  var REPO_REF = "master";
131
+ var REPO_RAW_URL = "https://raw.githubusercontent.com/duongductrong/cursor-kit/master";
132
+ var TEMPLATE_PATHS = {
133
+ commands: "templates/commands",
134
+ rules: "templates/rules"
135
+ };
136
+
137
+ // src/utils/templates.ts
138
+ function getLocalTemplatesDir() {
139
+ const currentDir = (0, import_node_path2.dirname)((0, import_node_url.fileURLToPath)(importMetaUrl));
140
+ return (0, import_node_path2.join)(currentDir, "..", "templates");
141
+ }
142
+ function getLocalManifest() {
143
+ const templatesDir = getLocalTemplatesDir();
144
+ const manifestPath = (0, import_node_path2.join)(templatesDir, "manifest.json");
145
+ if (fileExists(manifestPath)) {
146
+ try {
147
+ return JSON.parse(readFile(manifestPath));
148
+ } catch {
149
+ return null;
150
+ }
151
+ }
152
+ const commandsDir = (0, import_node_path2.join)(templatesDir, "commands");
153
+ const rulesDir = (0, import_node_path2.join)(templatesDir, "rules");
154
+ if (!dirExists(commandsDir) && !dirExists(rulesDir)) {
155
+ return null;
156
+ }
157
+ return {
158
+ commands: dirExists(commandsDir) ? listFiles(commandsDir, ".md") : [],
159
+ rules: dirExists(rulesDir) ? listFiles(rulesDir, ".mdc") : []
160
+ };
161
+ }
162
+ function getLocalTemplateContent(type, filename) {
163
+ const templatesDir = getLocalTemplatesDir();
164
+ const filePath = (0, import_node_path2.join)(templatesDir, type, filename);
165
+ if (fileExists(filePath)) {
166
+ return readFile(filePath);
167
+ }
168
+ return null;
169
+ }
170
+ async function fetchTemplateManifest() {
171
+ const localManifest = getLocalManifest();
172
+ if (localManifest) {
173
+ return localManifest;
174
+ }
175
+ const url = `${REPO_RAW_URL}/templates/manifest.json`;
176
+ const response = await fetch(url);
177
+ if (!response.ok) {
178
+ throw new Error(`Failed to fetch template manifest: ${response.statusText}`);
179
+ }
180
+ return response.json();
181
+ }
182
+ async function fetchTemplateContent(type, filename) {
183
+ const localContent = getLocalTemplateContent(type, filename);
184
+ if (localContent !== null) {
185
+ return localContent;
186
+ }
187
+ const templatePath = TEMPLATE_PATHS[type];
188
+ const url = `${REPO_RAW_URL}/${templatePath}/${filename}`;
189
+ const response = await fetch(url);
190
+ if (!response.ok) {
191
+ throw new Error(`Failed to fetch template ${filename}: ${response.statusText}`);
192
+ }
193
+ return response.text();
194
+ }
195
+ async function fetchMultipleTemplates(type, filenames) {
196
+ const results = /* @__PURE__ */ new Map();
197
+ const fetchPromises = filenames.map(async (filename) => {
198
+ const content = await fetchTemplateContent(type, filename);
199
+ return { filename, content };
200
+ });
201
+ const settled = await Promise.allSettled(fetchPromises);
202
+ for (const result of settled) {
203
+ if (result.status === "fulfilled") {
204
+ results.set(result.value.filename, result.value.content);
205
+ }
206
+ }
207
+ return results;
208
+ }
209
+ function getTemplateLabel(filename) {
210
+ const nameWithoutExt = filename.replace(/\.(md|mdc)$/, "");
211
+ return nameWithoutExt.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
212
+ }
118
213
 
119
214
  // src/commands/init.ts
215
+ async function selectTemplates(type, availableTemplates) {
216
+ const selectionMode = await p.select({
217
+ message: `How would you like to add ${type}?`,
218
+ options: [
219
+ {
220
+ value: "all",
221
+ label: `Add all ${availableTemplates.length} ${type}`,
222
+ hint: "Install everything"
223
+ },
224
+ {
225
+ value: "select",
226
+ label: "Select specific items",
227
+ hint: "Choose which ones to install"
228
+ }
229
+ ]
230
+ });
231
+ if (p.isCancel(selectionMode)) return selectionMode;
232
+ if (selectionMode === "all") {
233
+ return availableTemplates;
234
+ }
235
+ const selectedTemplates = await p.multiselect({
236
+ message: `Select ${type} to add:`,
237
+ options: availableTemplates.map((template) => ({
238
+ value: template,
239
+ label: getTemplateLabel(template),
240
+ hint: template
241
+ })),
242
+ required: true
243
+ });
244
+ return selectedTemplates;
245
+ }
246
+ async function handleConflicts(type, conflictingFiles) {
247
+ console.log();
248
+ console.log(
249
+ import_picocolors2.default.yellow(`\u26A0 ${conflictingFiles.length} existing ${type} found:`)
250
+ );
251
+ for (const file of conflictingFiles) {
252
+ console.log(import_picocolors2.default.dim(` \u2514\u2500 ${file}`));
253
+ }
254
+ console.log();
255
+ const strategy = await p.select({
256
+ message: "How would you like to handle conflicts?",
257
+ options: [
258
+ {
259
+ value: "overwrite",
260
+ label: "Overwrite existing files",
261
+ hint: "Replace all conflicting files"
262
+ },
263
+ {
264
+ value: "merge",
265
+ label: "Merge (keep existing, add new only)",
266
+ hint: "Skip files that already exist"
267
+ },
268
+ {
269
+ value: "cancel",
270
+ label: "Cancel",
271
+ hint: "Abort the operation"
272
+ }
273
+ ]
274
+ });
275
+ return strategy;
276
+ }
277
+ async function installTemplates(type, targetDir, selectedTemplates, conflictStrategy) {
278
+ const result = { added: [], skipped: [] };
279
+ const conflictingFiles = getConflictingFiles(targetDir, selectedTemplates);
280
+ let templatesToInstall;
281
+ if (conflictStrategy === "merge") {
282
+ templatesToInstall = selectedTemplates.filter(
283
+ (t) => !conflictingFiles.includes(t)
284
+ );
285
+ result.skipped = conflictingFiles.filter(
286
+ (f) => selectedTemplates.includes(f)
287
+ );
288
+ } else {
289
+ templatesToInstall = selectedTemplates;
290
+ }
291
+ if (templatesToInstall.length === 0) {
292
+ return result;
293
+ }
294
+ const templates = await fetchMultipleTemplates(type, templatesToInstall);
295
+ ensureDir(targetDir);
296
+ for (const [filename, content] of templates) {
297
+ const filePath = (0, import_node_path3.join)(targetDir, filename);
298
+ writeFile(filePath, content);
299
+ result.added.push(filename);
300
+ }
301
+ return result;
302
+ }
120
303
  var initCommand = (0, import_citty.defineCommand)({
121
304
  meta: {
122
305
  name: "init",
@@ -126,7 +309,7 @@ var initCommand = (0, import_citty.defineCommand)({
126
309
  force: {
127
310
  type: "boolean",
128
311
  alias: "f",
129
- description: "Overwrite existing files",
312
+ description: "Overwrite existing files without prompting",
130
313
  default: false
131
314
  },
132
315
  commands: {
@@ -140,6 +323,12 @@ var initCommand = (0, import_citty.defineCommand)({
140
323
  alias: "r",
141
324
  description: "Only initialize rules",
142
325
  default: false
326
+ },
327
+ all: {
328
+ type: "boolean",
329
+ alias: "a",
330
+ description: "Install all templates without selection prompts",
331
+ default: false
143
332
  }
144
333
  },
145
334
  async run({ args }) {
@@ -151,62 +340,126 @@ var initCommand = (0, import_citty.defineCommand)({
151
340
  const shouldInitCommands = initBoth || args.commands;
152
341
  const shouldInitRules = initBoth || args.rules;
153
342
  p.intro(import_picocolors2.default.bgCyan(import_picocolors2.default.black(" cursor-kit init ")));
154
- const commandsExist = dirExists(commandsDir) && listFiles(commandsDir).length > 0;
155
- const rulesExist = dirExists(rulesDir) && listFiles(rulesDir).length > 0;
156
- if ((commandsExist || rulesExist) && !args.force) {
157
- const existingItems = [];
158
- if (commandsExist) existingItems.push("commands");
159
- if (rulesExist) existingItems.push("rules");
160
- const shouldContinue = await p.confirm({
161
- message: `Existing ${existingItems.join(" and ")} found. Overwrite?`,
162
- initialValue: false
163
- });
164
- if (p.isCancel(shouldContinue) || !shouldContinue) {
165
- p.cancel("Operation cancelled");
166
- process.exit(0);
167
- }
168
- }
169
343
  const s = p.spinner();
344
+ let manifest;
345
+ try {
346
+ s.start("Fetching template manifest...");
347
+ manifest = await fetchTemplateManifest();
348
+ s.stop("Template manifest loaded");
349
+ } catch (error) {
350
+ s.stop("Failed to fetch manifest");
351
+ p.cancel(
352
+ `Error: ${error instanceof Error ? error.message : "Unknown error"}`
353
+ );
354
+ process.exit(1);
355
+ }
356
+ const results = {};
170
357
  try {
171
358
  ensureDir(cursorDir);
172
359
  if (shouldInitCommands) {
173
- s.start("Fetching commands templates...");
174
- await (0, import_giget.downloadTemplate)(`${REPO_URL}/templates/commands#${REPO_REF}`, {
175
- dir: commandsDir,
176
- force: true
177
- });
178
- s.stop("Commands initialized");
360
+ let selectedCommands;
361
+ if (args.all) {
362
+ selectedCommands = manifest.commands;
363
+ } else {
364
+ const selection = await selectTemplates("commands", manifest.commands);
365
+ if (p.isCancel(selection)) {
366
+ p.cancel("Operation cancelled");
367
+ process.exit(0);
368
+ }
369
+ selectedCommands = selection;
370
+ }
371
+ const conflictingCommands = getConflictingFiles(
372
+ commandsDir,
373
+ selectedCommands
374
+ );
375
+ let commandStrategy = "overwrite";
376
+ if (conflictingCommands.length > 0 && !args.force) {
377
+ const strategy = await handleConflicts("commands", conflictingCommands);
378
+ if (p.isCancel(strategy) || strategy === "cancel") {
379
+ p.cancel("Operation cancelled");
380
+ process.exit(0);
381
+ }
382
+ commandStrategy = strategy;
383
+ }
384
+ s.start("Installing commands...");
385
+ results.commands = await installTemplates(
386
+ "commands",
387
+ commandsDir,
388
+ selectedCommands,
389
+ commandStrategy
390
+ );
391
+ s.stop("Commands installed");
179
392
  }
180
393
  if (shouldInitRules) {
181
- s.start("Fetching rules templates...");
182
- await (0, import_giget.downloadTemplate)(`${REPO_URL}/templates/rules#${REPO_REF}`, {
183
- dir: rulesDir,
184
- force: true
185
- });
186
- s.stop("Rules initialized");
394
+ let selectedRules;
395
+ if (args.all) {
396
+ selectedRules = manifest.rules;
397
+ } else {
398
+ const selection = await selectTemplates("rules", manifest.rules);
399
+ if (p.isCancel(selection)) {
400
+ p.cancel("Operation cancelled");
401
+ process.exit(0);
402
+ }
403
+ selectedRules = selection;
404
+ }
405
+ const conflictingRules = getConflictingFiles(rulesDir, selectedRules);
406
+ let ruleStrategy = "overwrite";
407
+ if (conflictingRules.length > 0 && !args.force) {
408
+ const strategy = await handleConflicts("rules", conflictingRules);
409
+ if (p.isCancel(strategy) || strategy === "cancel") {
410
+ p.cancel("Operation cancelled");
411
+ process.exit(0);
412
+ }
413
+ ruleStrategy = strategy;
414
+ }
415
+ s.start("Installing rules...");
416
+ results.rules = await installTemplates(
417
+ "rules",
418
+ rulesDir,
419
+ selectedRules,
420
+ ruleStrategy
421
+ );
422
+ s.stop("Rules installed");
187
423
  }
188
424
  printDivider();
189
425
  console.log();
190
- const commandFiles = listFiles(commandsDir, ".md");
191
- const ruleFiles = listFiles(rulesDir, ".mdc");
192
- if (shouldInitCommands && commandFiles.length > 0) {
193
- printSuccess(
194
- `Commands: ${highlight(commandFiles.length.toString())} templates`
195
- );
196
- commandFiles.forEach((f) => {
197
- console.log(import_picocolors2.default.dim(` \u2514\u2500 ${f}`));
198
- });
426
+ if (results.commands) {
427
+ const { added, skipped } = results.commands;
428
+ if (added.length > 0 || skipped.length > 0) {
429
+ printSuccess(
430
+ `Commands: ${highlight(added.length.toString())} added${skipped.length > 0 ? `, ${import_picocolors2.default.yellow(skipped.length.toString())} skipped` : ""}`
431
+ );
432
+ for (const f of added) {
433
+ console.log(import_picocolors2.default.dim(` \u2514\u2500 ${import_picocolors2.default.green("+")} ${f}`));
434
+ }
435
+ for (const f of skipped) {
436
+ console.log(import_picocolors2.default.dim(` \u2514\u2500 ${import_picocolors2.default.yellow("\u25CB")} ${f} (kept existing)`));
437
+ }
438
+ }
199
439
  }
200
- if (shouldInitRules && ruleFiles.length > 0) {
201
- printSuccess(
202
- `Rules: ${highlight(ruleFiles.length.toString())} templates`
203
- );
204
- ruleFiles.forEach((f) => {
205
- console.log(import_picocolors2.default.dim(` \u2514\u2500 ${f}`));
206
- });
440
+ if (results.rules) {
441
+ const { added, skipped } = results.rules;
442
+ if (added.length > 0 || skipped.length > 0) {
443
+ printSuccess(
444
+ `Rules: ${highlight(added.length.toString())} added${skipped.length > 0 ? `, ${import_picocolors2.default.yellow(skipped.length.toString())} skipped` : ""}`
445
+ );
446
+ for (const f of added) {
447
+ console.log(import_picocolors2.default.dim(` \u2514\u2500 ${import_picocolors2.default.green("+")} ${f}`));
448
+ }
449
+ for (const f of skipped) {
450
+ console.log(import_picocolors2.default.dim(` \u2514\u2500 ${import_picocolors2.default.yellow("\u25CB")} ${f} (kept existing)`));
451
+ }
452
+ }
453
+ }
454
+ const totalAdded = (results.commands?.added.length ?? 0) + (results.rules?.added.length ?? 0);
455
+ const totalSkipped = (results.commands?.skipped.length ?? 0) + (results.rules?.skipped.length ?? 0);
456
+ if (totalAdded === 0 && totalSkipped > 0) {
457
+ console.log();
458
+ p.outro(import_picocolors2.default.yellow("No new templates added (all selected files already exist)"));
459
+ } else {
460
+ console.log();
461
+ p.outro(import_picocolors2.default.green("\u2728 Cursor Kit initialized successfully!"));
207
462
  }
208
- console.log();
209
- p.outro(import_picocolors2.default.green("\u2728 Cursor Kit initialized successfully!"));
210
463
  } catch (error) {
211
464
  s.stop("Failed");
212
465
  p.cancel(
@@ -221,7 +474,7 @@ var initCommand = (0, import_citty.defineCommand)({
221
474
  var import_citty2 = require("citty");
222
475
  var p2 = __toESM(require("@clack/prompts"), 1);
223
476
  var import_picocolors3 = __toESM(require("picocolors"), 1);
224
- var import_node_path2 = require("path");
477
+ var import_node_path4 = require("path");
225
478
  var COMMAND_TEMPLATE = `You are a helpful assistant. Describe what this command does.
226
479
 
227
480
  ## Instructions
@@ -318,7 +571,7 @@ var addCommand = (0, import_citty2.defineCommand)({
318
571
  const isCommand = itemType === "command";
319
572
  const targetDir = isCommand ? getCommandsDir() : getRulesDir();
320
573
  const extension = isCommand ? ".md" : ".mdc";
321
- const filePath = (0, import_node_path2.join)(targetDir, `${slug}${extension}`);
574
+ const filePath = (0, import_node_path4.join)(targetDir, `${slug}${extension}`);
322
575
  if (fileExists(filePath)) {
323
576
  const shouldOverwrite = await p2.confirm({
324
577
  message: `${highlight(slug + extension)} already exists. Overwrite?`,
@@ -354,7 +607,7 @@ var addCommand = (0, import_citty2.defineCommand)({
354
607
  var import_citty3 = require("citty");
355
608
  var p3 = __toESM(require("@clack/prompts"), 1);
356
609
  var import_picocolors4 = __toESM(require("picocolors"), 1);
357
- var import_giget2 = require("giget");
610
+ var import_giget = require("giget");
358
611
  var pullCommand = (0, import_citty3.defineCommand)({
359
612
  meta: {
360
613
  name: "pull",
@@ -413,7 +666,7 @@ var pullCommand = (0, import_citty3.defineCommand)({
413
666
  ensureDir(getCursorDir());
414
667
  if (shouldPullCommands) {
415
668
  s.start("Pulling commands...");
416
- await (0, import_giget2.downloadTemplate)(`${REPO_URL}/templates/commands#${REPO_REF}`, {
669
+ await (0, import_giget.downloadTemplate)(`${REPO_URL}/templates/commands#${REPO_REF}`, {
417
670
  dir: commandsDir,
418
671
  force: true
419
672
  });
@@ -421,7 +674,7 @@ var pullCommand = (0, import_citty3.defineCommand)({
421
674
  }
422
675
  if (shouldPullRules) {
423
676
  s.start("Pulling rules...");
424
- await (0, import_giget2.downloadTemplate)(`${REPO_URL}/templates/rules#${REPO_REF}`, {
677
+ await (0, import_giget.downloadTemplate)(`${REPO_URL}/templates/rules#${REPO_REF}`, {
425
678
  dir: rulesDir,
426
679
  force: true
427
680
  });
@@ -457,7 +710,7 @@ var pullCommand = (0, import_citty3.defineCommand)({
457
710
  var import_citty4 = require("citty");
458
711
  var p4 = __toESM(require("@clack/prompts"), 1);
459
712
  var import_picocolors5 = __toESM(require("picocolors"), 1);
460
- var import_node_path3 = require("path");
713
+ var import_node_path5 = require("path");
461
714
  function extractDescription(content, isCommand) {
462
715
  if (isCommand) {
463
716
  const firstLine = content.trim().split("\n")[0];
@@ -475,7 +728,7 @@ function extractDescription(content, isCommand) {
475
728
  function getItems(dir, extension, isCommand) {
476
729
  const files = listFiles(dir, extension);
477
730
  return files.map((file) => {
478
- const filePath = (0, import_node_path3.join)(dir, file);
731
+ const filePath = (0, import_node_path5.join)(dir, file);
479
732
  const content = fileExists(filePath) ? readFile(filePath) : "";
480
733
  return {
481
734
  name: file.replace(extension, ""),
@@ -566,7 +819,7 @@ var listCommand = (0, import_citty4.defineCommand)({
566
819
  var import_citty5 = require("citty");
567
820
  var p5 = __toESM(require("@clack/prompts"), 1);
568
821
  var import_picocolors6 = __toESM(require("picocolors"), 1);
569
- var import_node_path4 = require("path");
822
+ var import_node_path6 = require("path");
570
823
  var removeCommand = (0, import_citty5.defineCommand)({
571
824
  meta: {
572
825
  name: "remove",
@@ -658,7 +911,7 @@ var removeCommand = (0, import_citty5.defineCommand)({
658
911
  }
659
912
  itemName = nameResult;
660
913
  }
661
- const filePath = (0, import_node_path4.join)(dir, `${itemName}${extension}`);
914
+ const filePath = (0, import_node_path6.join)(dir, `${itemName}${extension}`);
662
915
  if (!fileExists(filePath)) {
663
916
  p5.cancel(`${itemType} '${itemName}' not found`);
664
917
  process.exit(1);
@@ -687,15 +940,17 @@ var removeCommand = (0, import_citty5.defineCommand)({
687
940
  });
688
941
 
689
942
  // src/cli.ts
943
+ var require2 = (0, import_node_module.createRequire)(importMetaUrl);
944
+ var pkg = require2("../package.json");
690
945
  var main = (0, import_citty6.defineCommand)({
691
946
  meta: {
692
947
  name: "cursor-kit",
693
- version: "0.1.0",
948
+ version: pkg.version,
694
949
  description: "CLI toolkit to manage Cursor IDE rules and commands"
695
950
  },
696
951
  setup() {
697
952
  printBanner();
698
- printVersion("0.1.0");
953
+ printVersion(pkg.version);
699
954
  },
700
955
  subCommands: {
701
956
  init: initCommand,