rulesync 0.45.0 → 0.47.0

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/index.js CHANGED
@@ -1,27 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  generateClaudeMcp
4
- } from "./chunk-22GWBUIP.js";
4
+ } from "./chunk-OTCCHS7Q.js";
5
5
  import {
6
6
  generateClineMcp
7
- } from "./chunk-BD37M3ZH.js";
7
+ } from "./chunk-BY6RI77W.js";
8
8
  import {
9
9
  generateCopilotMcp
10
- } from "./chunk-ZORSPGDD.js";
10
+ } from "./chunk-P6KQZULZ.js";
11
11
  import {
12
12
  generateCursorMcp
13
- } from "./chunk-FAZT3ILF.js";
13
+ } from "./chunk-7UVBAWYG.js";
14
14
  import {
15
15
  generateGeminiCliMcp
16
- } from "./chunk-DCSO5MY7.js";
16
+ } from "./chunk-JWN6GRG6.js";
17
+ import {
18
+ generateKiroMcp
19
+ } from "./chunk-D365OP7N.js";
17
20
  import {
18
21
  generateRooMcp
19
- } from "./chunk-PJUNIIF4.js";
22
+ } from "./chunk-L2JTXZZB.js";
20
23
  import {
21
24
  RulesyncTargetsSchema,
22
25
  ToolTargetSchema,
23
26
  ToolTargetsSchema
24
- } from "./chunk-I5XVU7C6.js";
27
+ } from "./chunk-7ZIUEZZQ.js";
25
28
 
26
29
  // src/cli/index.ts
27
30
  import { Command } from "commander";
@@ -40,10 +43,11 @@ function getDefaultConfig() {
40
43
  cline: ".clinerules",
41
44
  claudecode: ".",
42
45
  roo: ".roo/rules",
43
- geminicli: ".gemini/memories"
46
+ geminicli: ".gemini/memories",
47
+ kiro: ".kiro/steering"
44
48
  },
45
49
  watchEnabled: false,
46
- defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli"]
50
+ defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli", "kiro"]
47
51
  };
48
52
  }
49
53
  function resolveTargets(targets, config) {
@@ -90,11 +94,126 @@ async function addCommand(filename) {
90
94
  }
91
95
  }
92
96
 
97
+ // src/generators/ignore/kiro.ts
98
+ import { join as join2 } from "path";
99
+ async function generateKiroIgnoreFiles(rules, config, baseDir) {
100
+ const outputs = [];
101
+ const aiignoreContent = generateAiignoreContent(rules);
102
+ const outputPath = baseDir || process.cwd();
103
+ const filepath = join2(outputPath, ".aiignore");
104
+ outputs.push({
105
+ tool: "kiro",
106
+ filepath,
107
+ content: aiignoreContent
108
+ });
109
+ return outputs;
110
+ }
111
+ function generateAiignoreContent(rules) {
112
+ const lines = [
113
+ "# Generated by rulesync - Kiro AI-specific exclusions",
114
+ "# This file excludes files that can be in Git but shouldn't be read by the AI",
115
+ ""
116
+ ];
117
+ lines.push(
118
+ "# Data files AI shouldn't process",
119
+ "*.csv",
120
+ "*.tsv",
121
+ "*.sqlite",
122
+ "*.db",
123
+ "",
124
+ "# Large binary files",
125
+ "*.zip",
126
+ "*.tar.gz",
127
+ "*.rar",
128
+ "",
129
+ "# Sensitive documentation",
130
+ "internal-docs/",
131
+ "confidential/",
132
+ "",
133
+ "# Test data that might confuse AI",
134
+ "test/fixtures/large-*.json",
135
+ "benchmark-results/",
136
+ "",
137
+ "# Reinforce critical exclusions from .gitignore",
138
+ "*.pem",
139
+ "*.key",
140
+ ".env*",
141
+ ""
142
+ );
143
+ const rulePatterns = extractIgnorePatternsFromRules(rules);
144
+ if (rulePatterns.length > 0) {
145
+ lines.push("# Project-specific exclusions from rulesync rules");
146
+ lines.push(...rulePatterns);
147
+ lines.push("");
148
+ }
149
+ return lines.join("\n");
150
+ }
151
+ function extractIgnorePatternsFromRules(rules) {
152
+ const patterns = [];
153
+ for (const rule of rules) {
154
+ if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
155
+ for (const glob of rule.frontmatter.globs) {
156
+ if (shouldExcludeFromAI(glob)) {
157
+ patterns.push(`# Exclude: ${rule.frontmatter.description}`);
158
+ patterns.push(glob);
159
+ }
160
+ }
161
+ }
162
+ const contentPatterns = extractIgnorePatternsFromContent(rule.content);
163
+ patterns.push(...contentPatterns);
164
+ }
165
+ return patterns;
166
+ }
167
+ function shouldExcludeFromAI(glob) {
168
+ const excludePatterns = [
169
+ // Test and fixture files that might be large or confusing
170
+ "**/test/fixtures/**",
171
+ "**/tests/fixtures/**",
172
+ "**/*.fixture.*",
173
+ // Build and generated files
174
+ "**/dist/**",
175
+ "**/build/**",
176
+ "**/coverage/**",
177
+ // Configuration that might contain sensitive data
178
+ "**/config/production/**",
179
+ "**/config/prod/**",
180
+ "**/*.prod.*",
181
+ // Documentation that might be sensitive
182
+ "**/internal/**",
183
+ "**/private/**",
184
+ "**/confidential/**"
185
+ ];
186
+ return excludePatterns.some((pattern) => {
187
+ const regex = new RegExp(pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*"));
188
+ return regex.test(glob);
189
+ });
190
+ }
191
+ function extractIgnorePatternsFromContent(content) {
192
+ const patterns = [];
193
+ const lines = content.split("\n");
194
+ for (const line of lines) {
195
+ const trimmed = line.trim();
196
+ if (trimmed.startsWith("# IGNORE:") || trimmed.startsWith("# aiignore:")) {
197
+ const pattern = trimmed.replace(/^# (IGNORE|aiignore):\s*/, "").trim();
198
+ if (pattern) {
199
+ patterns.push(pattern);
200
+ }
201
+ }
202
+ if (trimmed.includes("exclude") || trimmed.includes("ignore")) {
203
+ const matches = trimmed.match(/['"`]([^'"`]+\.(log|tmp|cache|temp))['"`]/g);
204
+ if (matches) {
205
+ patterns.push(...matches.map((m) => m.replace(/['"`]/g, "")));
206
+ }
207
+ }
208
+ }
209
+ return patterns;
210
+ }
211
+
93
212
  // src/generators/rules/claudecode.ts
94
- import { join as join4 } from "path";
213
+ import { join as join5 } from "path";
95
214
 
96
215
  // src/types/claudecode.ts
97
- import { z } from "zod/v4-mini";
216
+ import { z } from "zod/mini";
98
217
  var ClaudeSettingsSchema = z.looseObject({
99
218
  permissions: z._default(
100
219
  z.looseObject({
@@ -105,12 +224,8 @@ var ClaudeSettingsSchema = z.looseObject({
105
224
  });
106
225
 
107
226
  // src/utils/file.ts
108
- import { readdir, rm } from "fs/promises";
109
- import { join as join3 } from "path";
110
-
111
- // src/utils/file-ops.ts
112
- import { mkdir as mkdir2, readFile, stat, writeFile as writeFile2 } from "fs/promises";
113
- import { dirname } from "path";
227
+ import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
228
+ import { dirname, join as join3 } from "path";
114
229
  async function ensureDir(dirPath) {
115
230
  try {
116
231
  await stat(dirPath);
@@ -133,16 +248,57 @@ async function fileExists(filepath) {
133
248
  return false;
134
249
  }
135
250
  }
251
+ async function findFiles(dir, extension = ".md") {
252
+ try {
253
+ const files = await readdir(dir);
254
+ return files.filter((file) => file.endsWith(extension)).map((file) => join3(dir, file));
255
+ } catch {
256
+ return [];
257
+ }
258
+ }
259
+ async function removeDirectory(dirPath) {
260
+ const dangerousPaths = [".", "/", "~", "src", "node_modules"];
261
+ if (dangerousPaths.includes(dirPath) || dirPath === "") {
262
+ console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
263
+ return;
264
+ }
265
+ try {
266
+ if (await fileExists(dirPath)) {
267
+ await rm(dirPath, { recursive: true, force: true });
268
+ }
269
+ } catch (error) {
270
+ console.warn(`Failed to remove directory ${dirPath}:`, error);
271
+ }
272
+ }
273
+ async function removeFile(filepath) {
274
+ try {
275
+ if (await fileExists(filepath)) {
276
+ await rm(filepath);
277
+ }
278
+ } catch (error) {
279
+ console.warn(`Failed to remove file ${filepath}:`, error);
280
+ }
281
+ }
282
+ async function removeClaudeGeneratedFiles() {
283
+ const filesToRemove = ["CLAUDE.md", ".claude/memories"];
284
+ for (const fileOrDir of filesToRemove) {
285
+ if (fileOrDir.endsWith("/memories")) {
286
+ await removeDirectory(fileOrDir);
287
+ } else {
288
+ await removeFile(fileOrDir);
289
+ }
290
+ }
291
+ }
136
292
 
137
293
  // src/utils/ignore.ts
138
- import { join as join2 } from "path";
294
+ import { join as join4 } from "path";
139
295
  import micromatch from "micromatch";
140
296
  var cachedIgnorePatterns = null;
141
297
  async function loadIgnorePatterns(baseDir = process.cwd()) {
142
298
  if (cachedIgnorePatterns) {
143
299
  return cachedIgnorePatterns;
144
300
  }
145
- const ignorePath = join2(baseDir, ".rulesyncignore");
301
+ const ignorePath = join4(baseDir, ".rulesyncignore");
146
302
  if (!await fileExists(ignorePath)) {
147
303
  cachedIgnorePatterns = { patterns: [] };
148
304
  return cachedIgnorePatterns;
@@ -185,76 +341,29 @@ function filterIgnoredFiles(files, ignorePatterns) {
185
341
  return files.filter((file) => !isFileIgnored(file, ignorePatterns));
186
342
  }
187
343
 
188
- // src/utils/file.ts
189
- async function findFiles(dir, extension = ".md", ignorePatterns) {
190
- try {
191
- const files = await readdir(dir);
192
- const filtered = files.filter((file) => file.endsWith(extension)).map((file) => join3(dir, file));
193
- if (ignorePatterns && ignorePatterns.length > 0) {
194
- return filterIgnoredFiles(filtered, ignorePatterns);
195
- }
196
- return filtered;
197
- } catch {
198
- return [];
199
- }
200
- }
201
- async function removeDirectory(dirPath) {
202
- const dangerousPaths = [".", "/", "~", "src", "node_modules"];
203
- if (dangerousPaths.includes(dirPath) || dirPath === "") {
204
- console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
205
- return;
206
- }
207
- try {
208
- if (await fileExists(dirPath)) {
209
- await rm(dirPath, { recursive: true, force: true });
210
- }
211
- } catch (error) {
212
- console.warn(`Failed to remove directory ${dirPath}:`, error);
213
- }
214
- }
215
- async function removeFile(filepath) {
216
- try {
217
- if (await fileExists(filepath)) {
218
- await rm(filepath);
219
- }
220
- } catch (error) {
221
- console.warn(`Failed to remove file ${filepath}:`, error);
222
- }
223
- }
224
- async function removeClaudeGeneratedFiles() {
225
- const filesToRemove = ["CLAUDE.md", ".claude/memories"];
226
- for (const fileOrDir of filesToRemove) {
227
- if (fileOrDir.endsWith("/memories")) {
228
- await removeDirectory(fileOrDir);
229
- } else {
230
- await removeFile(fileOrDir);
231
- }
232
- }
233
- }
234
-
235
344
  // src/generators/rules/claudecode.ts
236
345
  async function generateClaudecodeConfig(rules, config, baseDir) {
237
346
  const outputs = [];
238
347
  const rootRules = rules.filter((r) => r.frontmatter.root === true);
239
348
  const detailRules = rules.filter((r) => r.frontmatter.root === false);
240
349
  const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
241
- const claudeOutputDir = baseDir ? join4(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
350
+ const claudeOutputDir = baseDir ? join5(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
242
351
  outputs.push({
243
352
  tool: "claudecode",
244
- filepath: join4(claudeOutputDir, "CLAUDE.md"),
353
+ filepath: join5(claudeOutputDir, "CLAUDE.md"),
245
354
  content: claudeMdContent
246
355
  });
247
356
  for (const rule of detailRules) {
248
357
  const memoryContent = generateMemoryFile(rule);
249
358
  outputs.push({
250
359
  tool: "claudecode",
251
- filepath: join4(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
360
+ filepath: join5(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
252
361
  content: memoryContent
253
362
  });
254
363
  }
255
364
  const ignorePatterns = await loadIgnorePatterns(baseDir);
256
365
  if (ignorePatterns.patterns.length > 0) {
257
- const settingsPath = baseDir ? join4(baseDir, ".claude", "settings.json") : join4(".claude", "settings.json");
366
+ const settingsPath = baseDir ? join5(baseDir, ".claude", "settings.json") : join5(".claude", "settings.json");
258
367
  await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
259
368
  }
260
369
  return outputs;
@@ -318,13 +427,13 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
318
427
  }
319
428
 
320
429
  // src/generators/rules/cline.ts
321
- import { join as join5 } from "path";
430
+ import { join as join6 } from "path";
322
431
  async function generateClineConfig(rules, config, baseDir) {
323
432
  const outputs = [];
324
433
  for (const rule of rules) {
325
434
  const content = generateClineMarkdown(rule);
326
- const outputDir = baseDir ? join5(baseDir, config.outputPaths.cline) : config.outputPaths.cline;
327
- const filepath = join5(outputDir, `${rule.filename}.md`);
435
+ const outputDir = baseDir ? join6(baseDir, config.outputPaths.cline) : config.outputPaths.cline;
436
+ const filepath = join6(outputDir, `${rule.filename}.md`);
328
437
  outputs.push({
329
438
  tool: "cline",
330
439
  filepath,
@@ -333,7 +442,7 @@ async function generateClineConfig(rules, config, baseDir) {
333
442
  }
334
443
  const ignorePatterns = await loadIgnorePatterns(baseDir);
335
444
  if (ignorePatterns.patterns.length > 0) {
336
- const clineIgnorePath = baseDir ? join5(baseDir, ".clineignore") : ".clineignore";
445
+ const clineIgnorePath = baseDir ? join6(baseDir, ".clineignore") : ".clineignore";
337
446
  const clineIgnoreContent = generateClineIgnore(ignorePatterns.patterns);
338
447
  outputs.push({
339
448
  tool: "cline",
@@ -357,14 +466,14 @@ function generateClineIgnore(patterns) {
357
466
  }
358
467
 
359
468
  // src/generators/rules/copilot.ts
360
- import { join as join6 } from "path";
469
+ import { join as join7 } from "path";
361
470
  async function generateCopilotConfig(rules, config, baseDir) {
362
471
  const outputs = [];
363
472
  for (const rule of rules) {
364
473
  const content = generateCopilotMarkdown(rule);
365
474
  const baseFilename = rule.filename.replace(/\.md$/, "");
366
- const outputDir = baseDir ? join6(baseDir, config.outputPaths.copilot) : config.outputPaths.copilot;
367
- const filepath = join6(outputDir, `${baseFilename}.instructions.md`);
475
+ const outputDir = baseDir ? join7(baseDir, config.outputPaths.copilot) : config.outputPaths.copilot;
476
+ const filepath = join7(outputDir, `${baseFilename}.instructions.md`);
368
477
  outputs.push({
369
478
  tool: "copilot",
370
479
  filepath,
@@ -373,7 +482,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
373
482
  }
374
483
  const ignorePatterns = await loadIgnorePatterns(baseDir);
375
484
  if (ignorePatterns.patterns.length > 0) {
376
- const copilotIgnorePath = baseDir ? join6(baseDir, ".copilotignore") : ".copilotignore";
485
+ const copilotIgnorePath = baseDir ? join7(baseDir, ".copilotignore") : ".copilotignore";
377
486
  const copilotIgnoreContent = generateCopilotIgnore(ignorePatterns.patterns);
378
487
  outputs.push({
379
488
  tool: "copilot",
@@ -409,13 +518,13 @@ function generateCopilotIgnore(patterns) {
409
518
  }
410
519
 
411
520
  // src/generators/rules/cursor.ts
412
- import { join as join7 } from "path";
521
+ import { join as join8 } from "path";
413
522
  async function generateCursorConfig(rules, config, baseDir) {
414
523
  const outputs = [];
415
524
  for (const rule of rules) {
416
525
  const content = generateCursorMarkdown(rule);
417
- const outputDir = baseDir ? join7(baseDir, config.outputPaths.cursor) : config.outputPaths.cursor;
418
- const filepath = join7(outputDir, `${rule.filename}.mdc`);
526
+ const outputDir = baseDir ? join8(baseDir, config.outputPaths.cursor) : config.outputPaths.cursor;
527
+ const filepath = join8(outputDir, `${rule.filename}.mdc`);
419
528
  outputs.push({
420
529
  tool: "cursor",
421
530
  filepath,
@@ -424,7 +533,7 @@ async function generateCursorConfig(rules, config, baseDir) {
424
533
  }
425
534
  const ignorePatterns = await loadIgnorePatterns(baseDir);
426
535
  if (ignorePatterns.patterns.length > 0) {
427
- const cursorIgnorePath = baseDir ? join7(baseDir, ".cursorignore") : ".cursorignore";
536
+ const cursorIgnorePath = baseDir ? join8(baseDir, ".cursorignore") : ".cursorignore";
428
537
  const cursorIgnoreContent = generateCursorIgnore(ignorePatterns.patterns);
429
538
  outputs.push({
430
539
  tool: "cursor",
@@ -497,15 +606,15 @@ function generateCursorIgnore(patterns) {
497
606
  }
498
607
 
499
608
  // src/generators/rules/geminicli.ts
500
- import { join as join8 } from "path";
609
+ import { join as join9 } from "path";
501
610
  async function generateGeminiConfig(rules, config, baseDir) {
502
611
  const outputs = [];
503
612
  const rootRule = rules.find((rule) => rule.frontmatter.root === true);
504
613
  const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
505
614
  for (const rule of memoryRules) {
506
615
  const content = generateGeminiMemoryMarkdown(rule);
507
- const outputDir = baseDir ? join8(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
508
- const filepath = join8(outputDir, `${rule.filename}.md`);
616
+ const outputDir = baseDir ? join9(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
617
+ const filepath = join9(outputDir, `${rule.filename}.md`);
509
618
  outputs.push({
510
619
  tool: "geminicli",
511
620
  filepath,
@@ -513,7 +622,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
513
622
  });
514
623
  }
515
624
  const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
516
- const rootFilepath = baseDir ? join8(baseDir, "GEMINI.md") : "GEMINI.md";
625
+ const rootFilepath = baseDir ? join9(baseDir, "GEMINI.md") : "GEMINI.md";
517
626
  outputs.push({
518
627
  tool: "geminicli",
519
628
  filepath: rootFilepath,
@@ -521,7 +630,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
521
630
  });
522
631
  const ignorePatterns = await loadIgnorePatterns(baseDir);
523
632
  if (ignorePatterns.patterns.length > 0) {
524
- const aiexcludePath = baseDir ? join8(baseDir, ".aiexclude") : ".aiexclude";
633
+ const aiexcludePath = baseDir ? join9(baseDir, ".aiexclude") : ".aiexclude";
525
634
  const aiexcludeContent = generateAiexclude(ignorePatterns.patterns);
526
635
  outputs.push({
527
636
  tool: "geminicli",
@@ -568,14 +677,34 @@ function generateAiexclude(patterns) {
568
677
  return lines.join("\n");
569
678
  }
570
679
 
680
+ // src/generators/rules/kiro.ts
681
+ import { join as join10 } from "path";
682
+ async function generateKiroConfig(rules, config, baseDir) {
683
+ const outputs = [];
684
+ for (const rule of rules) {
685
+ const content = generateKiroMarkdown(rule);
686
+ const outputDir = baseDir ? join10(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
687
+ const filepath = join10(outputDir, `${rule.filename}.md`);
688
+ outputs.push({
689
+ tool: "kiro",
690
+ filepath,
691
+ content
692
+ });
693
+ }
694
+ return outputs;
695
+ }
696
+ function generateKiroMarkdown(rule) {
697
+ return rule.content.trim();
698
+ }
699
+
571
700
  // src/generators/rules/roo.ts
572
- import { join as join9 } from "path";
701
+ import { join as join11 } from "path";
573
702
  async function generateRooConfig(rules, config, baseDir) {
574
703
  const outputs = [];
575
704
  for (const rule of rules) {
576
705
  const content = generateRooMarkdown(rule);
577
- const outputDir = baseDir ? join9(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
578
- const filepath = join9(outputDir, `${rule.filename}.md`);
706
+ const outputDir = baseDir ? join11(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
707
+ const filepath = join11(outputDir, `${rule.filename}.md`);
579
708
  outputs.push({
580
709
  tool: "roo",
581
710
  filepath,
@@ -584,7 +713,7 @@ async function generateRooConfig(rules, config, baseDir) {
584
713
  }
585
714
  const ignorePatterns = await loadIgnorePatterns(baseDir);
586
715
  if (ignorePatterns.patterns.length > 0) {
587
- const rooIgnorePath = baseDir ? join9(baseDir, ".rooignore") : ".rooignore";
716
+ const rooIgnorePath = baseDir ? join11(baseDir, ".rooignore") : ".rooignore";
588
717
  const rooIgnoreContent = generateRooIgnore(ignorePatterns.patterns);
589
718
  outputs.push({
590
719
  tool: "roo",
@@ -650,6 +779,11 @@ async function generateForTool(tool, rules, config, baseDir) {
650
779
  return generateRooConfig(rules, config, baseDir);
651
780
  case "geminicli":
652
781
  return generateGeminiConfig(rules, config, baseDir);
782
+ case "kiro": {
783
+ const kiroRulesOutputs = await generateKiroConfig(rules, config, baseDir);
784
+ const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
785
+ return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
786
+ }
653
787
  default:
654
788
  console.warn(`Unknown tool: ${tool}`);
655
789
  return null;
@@ -661,7 +795,7 @@ import { basename } from "path";
661
795
  import matter from "gray-matter";
662
796
 
663
797
  // src/types/config.ts
664
- import { z as z2 } from "zod/v4-mini";
798
+ import { z as z2 } from "zod/mini";
665
799
  var ConfigSchema = z2.object({
666
800
  aiRulesDir: z2.string(),
667
801
  outputPaths: z2.record(ToolTargetSchema, z2.string()),
@@ -670,7 +804,7 @@ var ConfigSchema = z2.object({
670
804
  });
671
805
 
672
806
  // src/types/mcp.ts
673
- import { z as z3 } from "zod/v4-mini";
807
+ import { z as z3 } from "zod/mini";
674
808
  var McpTransportTypeSchema = z3.enum(["stdio", "sse", "http"]);
675
809
  var McpServerBaseSchema = z3.object({
676
810
  command: z3.optional(z3.string()),
@@ -686,7 +820,9 @@ var McpServerBaseSchema = z3.object({
686
820
  transport: z3.optional(McpTransportTypeSchema),
687
821
  type: z3.optional(z3.enum(["sse", "streamable-http"])),
688
822
  alwaysAllow: z3.optional(z3.array(z3.string())),
689
- tools: z3.optional(z3.array(z3.string()))
823
+ tools: z3.optional(z3.array(z3.string())),
824
+ kiroAutoApprove: z3.optional(z3.array(z3.string())),
825
+ kiroAutoBlock: z3.optional(z3.array(z3.string()))
690
826
  });
691
827
  var RulesyncMcpServerSchema = z3.extend(McpServerBaseSchema, {
692
828
  targets: z3.optional(RulesyncTargetsSchema)
@@ -699,7 +835,7 @@ var RulesyncMcpConfigSchema = z3.object({
699
835
  });
700
836
 
701
837
  // src/types/rules.ts
702
- import { z as z4 } from "zod/v4-mini";
838
+ import { z as z4 } from "zod/mini";
703
839
  var RuleFrontmatterSchema = z4.object({
704
840
  root: z4.boolean(),
705
841
  targets: RulesyncTargetsSchema,
@@ -727,7 +863,8 @@ var GenerateOptionsSchema = z4.object({
727
863
  // src/core/parser.ts
728
864
  async function parseRulesFromDirectory(aiRulesDir) {
729
865
  const ignorePatterns = await loadIgnorePatterns();
730
- const ruleFiles = await findFiles(aiRulesDir, ".md", ignorePatterns.patterns);
866
+ const allRuleFiles = await findFiles(aiRulesDir, ".md");
867
+ const ruleFiles = filterIgnoredFiles(allRuleFiles, ignorePatterns.patterns);
731
868
  const rules = [];
732
869
  const errors = [];
733
870
  if (ignorePatterns.patterns.length > 0) {
@@ -888,6 +1025,11 @@ async function generateMcpConfigs(projectRoot, baseDir) {
888
1025
  path: path3.join(targetRoot, ".gemini", "settings.json"),
889
1026
  generate: () => generateGeminiCliMcp(config)
890
1027
  },
1028
+ {
1029
+ tool: "kiro-project",
1030
+ path: path3.join(targetRoot, ".kiro", "mcp.json"),
1031
+ generate: () => generateKiroMcp(config)
1032
+ },
891
1033
  {
892
1034
  tool: "roo-project",
893
1035
  path: path3.join(targetRoot, ".roo", "mcp.json"),
@@ -898,7 +1040,7 @@ async function generateMcpConfigs(projectRoot, baseDir) {
898
1040
  try {
899
1041
  const content = generator.generate();
900
1042
  const parsed = JSON.parse(content);
901
- if (generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("roo")) {
1043
+ if (generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
902
1044
  if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
903
1045
  results.push({
904
1046
  tool: generator.tool,
@@ -984,6 +1126,9 @@ async function generateCommand(options = {}) {
984
1126
  case "geminicli":
985
1127
  deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
986
1128
  break;
1129
+ case "kiro":
1130
+ deleteTasks.push(removeDirectory(config.outputPaths.kiro));
1131
+ break;
987
1132
  }
988
1133
  }
989
1134
  await Promise.all(deleteTasks);
@@ -1048,9 +1193,9 @@ Generating configurations for base directory: ${baseDir}`);
1048
1193
 
1049
1194
  // src/cli/commands/gitignore.ts
1050
1195
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
1051
- import { join as join12 } from "path";
1196
+ import { join as join14 } from "path";
1052
1197
  var gitignoreCommand = async () => {
1053
- const gitignorePath = join12(process.cwd(), ".gitignore");
1198
+ const gitignorePath = join14(process.cwd(), ".gitignore");
1054
1199
  const rulesFilesToIgnore = [
1055
1200
  "# Generated by rulesync - AI tool configuration files",
1056
1201
  "**/.github/copilot-instructions.md",
@@ -1067,6 +1212,8 @@ var gitignoreCommand = async () => {
1067
1212
  "**/GEMINI.md",
1068
1213
  "**/.gemini/memories/",
1069
1214
  "**/.aiexclude",
1215
+ "**/.aiignore",
1216
+ "**/.kiro/steering/",
1070
1217
  "**/.mcp.json",
1071
1218
  "!.rulesync/.mcp.json",
1072
1219
  "**/.cursor/mcp.json",
@@ -1104,17 +1251,17 @@ ${linesToAdd.join("\n")}
1104
1251
  };
1105
1252
 
1106
1253
  // src/core/importer.ts
1107
- import { join as join19 } from "path";
1254
+ import { join as join21 } from "path";
1108
1255
  import matter4 from "gray-matter";
1109
1256
 
1110
1257
  // src/parsers/claudecode.ts
1111
- import { basename as basename2, join as join13 } from "path";
1258
+ import { basename as basename2, join as join15 } from "path";
1112
1259
  async function parseClaudeConfiguration(baseDir = process.cwd()) {
1113
1260
  const errors = [];
1114
1261
  const rules = [];
1115
1262
  let ignorePatterns;
1116
1263
  let mcpServers;
1117
- const claudeFilePath = join13(baseDir, "CLAUDE.md");
1264
+ const claudeFilePath = join15(baseDir, "CLAUDE.md");
1118
1265
  if (!await fileExists(claudeFilePath)) {
1119
1266
  errors.push("CLAUDE.md file not found");
1120
1267
  return { rules, errors };
@@ -1125,12 +1272,12 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
1125
1272
  if (mainRule) {
1126
1273
  rules.push(mainRule);
1127
1274
  }
1128
- const memoryDir = join13(baseDir, ".claude", "memories");
1275
+ const memoryDir = join15(baseDir, ".claude", "memories");
1129
1276
  if (await fileExists(memoryDir)) {
1130
1277
  const memoryRules = await parseClaudeMemoryFiles(memoryDir);
1131
1278
  rules.push(...memoryRules);
1132
1279
  }
1133
- const settingsPath = join13(baseDir, ".claude", "settings.json");
1280
+ const settingsPath = join15(baseDir, ".claude", "settings.json");
1134
1281
  if (await fileExists(settingsPath)) {
1135
1282
  const settingsResult = await parseClaudeSettings(settingsPath);
1136
1283
  if (settingsResult.ignorePatterns) {
@@ -1187,7 +1334,7 @@ async function parseClaudeMemoryFiles(memoryDir) {
1187
1334
  const files = await readdir2(memoryDir);
1188
1335
  for (const file of files) {
1189
1336
  if (file.endsWith(".md")) {
1190
- const filePath = join13(memoryDir, file);
1337
+ const filePath = join15(memoryDir, file);
1191
1338
  const content = await readFileContent(filePath);
1192
1339
  if (content.trim()) {
1193
1340
  const filename = basename2(file, ".md");
@@ -1250,11 +1397,11 @@ async function parseClaudeSettings(settingsPath) {
1250
1397
  }
1251
1398
 
1252
1399
  // src/parsers/cline.ts
1253
- import { join as join14 } from "path";
1400
+ import { join as join16 } from "path";
1254
1401
  async function parseClineConfiguration(baseDir = process.cwd()) {
1255
1402
  const errors = [];
1256
1403
  const rules = [];
1257
- const clineFilePath = join14(baseDir, ".cline", "instructions.md");
1404
+ const clineFilePath = join16(baseDir, ".cline", "instructions.md");
1258
1405
  if (await fileExists(clineFilePath)) {
1259
1406
  try {
1260
1407
  const content = await readFileContent(clineFilePath);
@@ -1277,14 +1424,14 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
1277
1424
  errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
1278
1425
  }
1279
1426
  }
1280
- const clinerulesDirPath = join14(baseDir, ".clinerules");
1427
+ const clinerulesDirPath = join16(baseDir, ".clinerules");
1281
1428
  if (await fileExists(clinerulesDirPath)) {
1282
1429
  try {
1283
1430
  const { readdir: readdir2 } = await import("fs/promises");
1284
1431
  const files = await readdir2(clinerulesDirPath);
1285
1432
  for (const file of files) {
1286
1433
  if (file.endsWith(".md")) {
1287
- const filePath = join14(clinerulesDirPath, file);
1434
+ const filePath = join16(clinerulesDirPath, file);
1288
1435
  try {
1289
1436
  const content = await readFileContent(filePath);
1290
1437
  if (content.trim()) {
@@ -1320,12 +1467,12 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
1320
1467
  }
1321
1468
 
1322
1469
  // src/parsers/copilot.ts
1323
- import { basename as basename3, join as join15 } from "path";
1470
+ import { basename as basename3, join as join17 } from "path";
1324
1471
  import matter2 from "gray-matter";
1325
1472
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
1326
1473
  const errors = [];
1327
1474
  const rules = [];
1328
- const copilotFilePath = join15(baseDir, ".github", "copilot-instructions.md");
1475
+ const copilotFilePath = join17(baseDir, ".github", "copilot-instructions.md");
1329
1476
  if (await fileExists(copilotFilePath)) {
1330
1477
  try {
1331
1478
  const rawContent = await readFileContent(copilotFilePath);
@@ -1350,14 +1497,14 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
1350
1497
  errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
1351
1498
  }
1352
1499
  }
1353
- const instructionsDir = join15(baseDir, ".github", "instructions");
1500
+ const instructionsDir = join17(baseDir, ".github", "instructions");
1354
1501
  if (await fileExists(instructionsDir)) {
1355
1502
  try {
1356
1503
  const { readdir: readdir2 } = await import("fs/promises");
1357
1504
  const files = await readdir2(instructionsDir);
1358
1505
  for (const file of files) {
1359
1506
  if (file.endsWith(".instructions.md")) {
1360
- const filePath = join15(instructionsDir, file);
1507
+ const filePath = join17(instructionsDir, file);
1361
1508
  const rawContent = await readFileContent(filePath);
1362
1509
  const parsed = matter2(rawContent);
1363
1510
  const content = parsed.content.trim();
@@ -1392,10 +1539,10 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
1392
1539
  }
1393
1540
 
1394
1541
  // src/parsers/cursor.ts
1395
- import { basename as basename4, join as join16 } from "path";
1542
+ import { basename as basename4, join as join18 } from "path";
1396
1543
  import matter3 from "gray-matter";
1397
1544
  import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
1398
- import { z as z5 } from "zod/v4-mini";
1545
+ import { z as z5 } from "zod/mini";
1399
1546
  var customMatterOptions = {
1400
1547
  engines: {
1401
1548
  yaml: {
@@ -1517,7 +1664,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1517
1664
  const rules = [];
1518
1665
  let ignorePatterns;
1519
1666
  let mcpServers;
1520
- const cursorFilePath = join16(baseDir, ".cursorrules");
1667
+ const cursorFilePath = join18(baseDir, ".cursorrules");
1521
1668
  if (await fileExists(cursorFilePath)) {
1522
1669
  try {
1523
1670
  const rawContent = await readFileContent(cursorFilePath);
@@ -1538,14 +1685,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1538
1685
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
1539
1686
  }
1540
1687
  }
1541
- const cursorRulesDir = join16(baseDir, ".cursor", "rules");
1688
+ const cursorRulesDir = join18(baseDir, ".cursor", "rules");
1542
1689
  if (await fileExists(cursorRulesDir)) {
1543
1690
  try {
1544
1691
  const { readdir: readdir2 } = await import("fs/promises");
1545
1692
  const files = await readdir2(cursorRulesDir);
1546
1693
  for (const file of files) {
1547
1694
  if (file.endsWith(".mdc")) {
1548
- const filePath = join16(cursorRulesDir, file);
1695
+ const filePath = join18(cursorRulesDir, file);
1549
1696
  try {
1550
1697
  const rawContent = await readFileContent(filePath);
1551
1698
  const parsed = matter3(rawContent, customMatterOptions);
@@ -1574,7 +1721,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1574
1721
  if (rules.length === 0) {
1575
1722
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
1576
1723
  }
1577
- const cursorIgnorePath = join16(baseDir, ".cursorignore");
1724
+ const cursorIgnorePath = join18(baseDir, ".cursorignore");
1578
1725
  if (await fileExists(cursorIgnorePath)) {
1579
1726
  try {
1580
1727
  const content = await readFileContent(cursorIgnorePath);
@@ -1587,7 +1734,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1587
1734
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
1588
1735
  }
1589
1736
  }
1590
- const cursorMcpPath = join16(baseDir, ".cursor", "mcp.json");
1737
+ const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
1591
1738
  if (await fileExists(cursorMcpPath)) {
1592
1739
  try {
1593
1740
  const content = await readFileContent(cursorMcpPath);
@@ -1610,13 +1757,13 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1610
1757
  }
1611
1758
 
1612
1759
  // src/parsers/geminicli.ts
1613
- import { basename as basename5, join as join17 } from "path";
1760
+ import { basename as basename5, join as join19 } from "path";
1614
1761
  async function parseGeminiConfiguration(baseDir = process.cwd()) {
1615
1762
  const errors = [];
1616
1763
  const rules = [];
1617
1764
  let ignorePatterns;
1618
1765
  let mcpServers;
1619
- const geminiFilePath = join17(baseDir, "GEMINI.md");
1766
+ const geminiFilePath = join19(baseDir, "GEMINI.md");
1620
1767
  if (!await fileExists(geminiFilePath)) {
1621
1768
  errors.push("GEMINI.md file not found");
1622
1769
  return { rules, errors };
@@ -1627,12 +1774,12 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
1627
1774
  if (mainRule) {
1628
1775
  rules.push(mainRule);
1629
1776
  }
1630
- const memoryDir = join17(baseDir, ".gemini", "memories");
1777
+ const memoryDir = join19(baseDir, ".gemini", "memories");
1631
1778
  if (await fileExists(memoryDir)) {
1632
1779
  const memoryRules = await parseGeminiMemoryFiles(memoryDir);
1633
1780
  rules.push(...memoryRules);
1634
1781
  }
1635
- const settingsPath = join17(baseDir, ".gemini", "settings.json");
1782
+ const settingsPath = join19(baseDir, ".gemini", "settings.json");
1636
1783
  if (await fileExists(settingsPath)) {
1637
1784
  const settingsResult = await parseGeminiSettings(settingsPath);
1638
1785
  if (settingsResult.ignorePatterns) {
@@ -1643,7 +1790,7 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
1643
1790
  }
1644
1791
  errors.push(...settingsResult.errors);
1645
1792
  }
1646
- const aiexcludePath = join17(baseDir, ".aiexclude");
1793
+ const aiexcludePath = join19(baseDir, ".aiexclude");
1647
1794
  if (await fileExists(aiexcludePath)) {
1648
1795
  const aiexcludePatterns = await parseAiexclude(aiexcludePath);
1649
1796
  if (aiexcludePatterns.length > 0) {
@@ -1696,7 +1843,7 @@ async function parseGeminiMemoryFiles(memoryDir) {
1696
1843
  const files = await readdir2(memoryDir);
1697
1844
  for (const file of files) {
1698
1845
  if (file.endsWith(".md")) {
1699
- const filePath = join17(memoryDir, file);
1846
+ const filePath = join19(memoryDir, file);
1700
1847
  const content = await readFileContent(filePath);
1701
1848
  if (content.trim()) {
1702
1849
  const filename = basename5(file, ".md");
@@ -1749,11 +1896,11 @@ async function parseAiexclude(aiexcludePath) {
1749
1896
  }
1750
1897
 
1751
1898
  // src/parsers/roo.ts
1752
- import { join as join18 } from "path";
1899
+ import { join as join20 } from "path";
1753
1900
  async function parseRooConfiguration(baseDir = process.cwd()) {
1754
1901
  const errors = [];
1755
1902
  const rules = [];
1756
- const rooFilePath = join18(baseDir, ".roo", "instructions.md");
1903
+ const rooFilePath = join20(baseDir, ".roo", "instructions.md");
1757
1904
  if (await fileExists(rooFilePath)) {
1758
1905
  try {
1759
1906
  const content = await readFileContent(rooFilePath);
@@ -1776,14 +1923,14 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
1776
1923
  errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
1777
1924
  }
1778
1925
  }
1779
- const rooRulesDir = join18(baseDir, ".roo", "rules");
1926
+ const rooRulesDir = join20(baseDir, ".roo", "rules");
1780
1927
  if (await fileExists(rooRulesDir)) {
1781
1928
  try {
1782
1929
  const { readdir: readdir2 } = await import("fs/promises");
1783
1930
  const files = await readdir2(rooRulesDir);
1784
1931
  for (const file of files) {
1785
1932
  if (file.endsWith(".md")) {
1786
- const filePath = join18(rooRulesDir, file);
1933
+ const filePath = join20(rooRulesDir, file);
1787
1934
  try {
1788
1935
  const content = await readFileContent(filePath);
1789
1936
  if (content.trim()) {
@@ -1884,7 +2031,7 @@ async function importConfiguration(options) {
1884
2031
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
1885
2032
  return { success: false, rulesCreated: 0, errors };
1886
2033
  }
1887
- const rulesDirPath = join19(baseDir, rulesDir);
2034
+ const rulesDirPath = join21(baseDir, rulesDir);
1888
2035
  try {
1889
2036
  const { mkdir: mkdir3 } = await import("fs/promises");
1890
2037
  await mkdir3(rulesDirPath, { recursive: true });
@@ -1898,7 +2045,7 @@ async function importConfiguration(options) {
1898
2045
  try {
1899
2046
  const baseFilename = `${tool}__${rule.filename}`;
1900
2047
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
1901
- const filePath = join19(rulesDirPath, `${filename}.md`);
2048
+ const filePath = join21(rulesDirPath, `${filename}.md`);
1902
2049
  const content = generateRuleFileContent(rule);
1903
2050
  await writeFileContent(filePath, content);
1904
2051
  rulesCreated++;
@@ -1913,7 +2060,7 @@ async function importConfiguration(options) {
1913
2060
  let ignoreFileCreated = false;
1914
2061
  if (ignorePatterns && ignorePatterns.length > 0) {
1915
2062
  try {
1916
- const rulesyncignorePath = join19(baseDir, ".rulesyncignore");
2063
+ const rulesyncignorePath = join21(baseDir, ".rulesyncignore");
1917
2064
  const ignoreContent = `${ignorePatterns.join("\n")}
1918
2065
  `;
1919
2066
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -1929,7 +2076,7 @@ async function importConfiguration(options) {
1929
2076
  let mcpFileCreated = false;
1930
2077
  if (mcpServers && Object.keys(mcpServers).length > 0) {
1931
2078
  try {
1932
- const mcpPath = join19(baseDir, rulesDir, ".mcp.json");
2079
+ const mcpPath = join21(baseDir, rulesDir, ".mcp.json");
1933
2080
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
1934
2081
  `;
1935
2082
  await writeFileContent(mcpPath, mcpContent);
@@ -1943,7 +2090,7 @@ async function importConfiguration(options) {
1943
2090
  }
1944
2091
  }
1945
2092
  return {
1946
- success: rulesCreated > 0 || ignoreFileCreated || mcpFileCreated,
2093
+ success: errors.length === 0 && (rulesCreated > 0 || ignoreFileCreated || mcpFileCreated),
1947
2094
  rulesCreated,
1948
2095
  errors,
1949
2096
  ignoreFileCreated,
@@ -1957,7 +2104,7 @@ function generateRuleFileContent(rule) {
1957
2104
  async function generateUniqueFilename(rulesDir, baseFilename) {
1958
2105
  let filename = baseFilename;
1959
2106
  let counter = 1;
1960
- while (await fileExists(join19(rulesDir, `${filename}.md`))) {
2107
+ while (await fileExists(join21(rulesDir, `${filename}.md`))) {
1961
2108
  filename = `${baseFilename}-${counter}`;
1962
2109
  counter++;
1963
2110
  }
@@ -2022,7 +2169,7 @@ async function importCommand(options = {}) {
2022
2169
  }
2023
2170
 
2024
2171
  // src/cli/commands/init.ts
2025
- import { join as join20 } from "path";
2172
+ import { join as join22 } from "path";
2026
2173
  async function initCommand() {
2027
2174
  const aiRulesDir = ".rulesync";
2028
2175
  console.log("Initializing rulesync...");
@@ -2069,7 +2216,7 @@ globs: ["**/*"]
2069
2216
  - Follow single responsibility principle
2070
2217
  `
2071
2218
  };
2072
- const filepath = join20(aiRulesDir, sampleFile.filename);
2219
+ const filepath = join22(aiRulesDir, sampleFile.filename);
2073
2220
  if (!await fileExists(filepath)) {
2074
2221
  await writeFileContent(filepath, sampleFile.content);
2075
2222
  console.log(`Created ${filepath}`);
@@ -2213,12 +2360,12 @@ async function watchCommand() {
2213
2360
 
2214
2361
  // src/cli/index.ts
2215
2362
  var program = new Command();
2216
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.45.0");
2363
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.47.0");
2217
2364
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
2218
2365
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
2219
2366
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
2220
2367
  program.command("import").description("Import configurations from AI tools to rulesync format").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("-v, --verbose", "Verbose output").action(importCommand);
2221
- program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--delete", "Delete all existing files in output directories before generating").option(
2368
+ program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--kiro", "Generate only for Kiro IDE").option("--delete", "Delete all existing files in output directories before generating").option(
2222
2369
  "-b, --base-dir <paths>",
2223
2370
  "Base directories to generate files (comma-separated for multiple paths)"
2224
2371
  ).option("-v, --verbose", "Verbose output").action(async (options) => {
@@ -2229,6 +2376,7 @@ program.command("generate").description("Generate configuration files for AI too
2229
2376
  if (options.claudecode) tools.push("claudecode");
2230
2377
  if (options.roo) tools.push("roo");
2231
2378
  if (options.geminicli) tools.push("geminicli");
2379
+ if (options.kiro) tools.push("kiro");
2232
2380
  const generateOptions = {
2233
2381
  verbose: options.verbose,
2234
2382
  delete: options.delete