rulesync 0.45.0 → 0.48.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);
@@ -1014,11 +1159,10 @@ Generating configurations for base directory: ${baseDir}`);
1014
1159
  console.warn("\u26A0\uFE0F No configurations generated");
1015
1160
  return;
1016
1161
  }
1017
- console.log(`
1018
- \u{1F389} Successfully generated ${totalOutputs} configuration file(s)!`);
1019
1162
  if (options.verbose) {
1020
1163
  console.log("\nGenerating MCP configurations...");
1021
1164
  }
1165
+ let totalMcpOutputs = 0;
1022
1166
  for (const baseDir of baseDirs) {
1023
1167
  const mcpResults = await generateMcpConfigs(
1024
1168
  process.cwd(),
@@ -1033,6 +1177,7 @@ Generating configurations for base directory: ${baseDir}`);
1033
1177
  for (const result of mcpResults) {
1034
1178
  if (result.status === "success") {
1035
1179
  console.log(`\u2705 Generated ${result.tool} MCP configuration: ${result.path}`);
1180
+ totalMcpOutputs++;
1036
1181
  } else if (result.status === "error") {
1037
1182
  console.error(`\u274C Failed to generate ${result.tool} MCP configuration: ${result.error}`);
1038
1183
  } else if (options.verbose && result.status === "skipped") {
@@ -1040,6 +1185,13 @@ Generating configurations for base directory: ${baseDir}`);
1040
1185
  }
1041
1186
  }
1042
1187
  }
1188
+ const totalGenerated = totalOutputs + totalMcpOutputs;
1189
+ if (totalGenerated > 0) {
1190
+ console.log(
1191
+ `
1192
+ \u{1F389} All done! Generated ${totalGenerated} file(s) total (${totalOutputs} configurations + ${totalMcpOutputs} MCP configurations)`
1193
+ );
1194
+ }
1043
1195
  } catch (error) {
1044
1196
  console.error("\u274C Failed to generate configurations:", error);
1045
1197
  process.exit(1);
@@ -1048,9 +1200,9 @@ Generating configurations for base directory: ${baseDir}`);
1048
1200
 
1049
1201
  // src/cli/commands/gitignore.ts
1050
1202
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
1051
- import { join as join12 } from "path";
1203
+ import { join as join14 } from "path";
1052
1204
  var gitignoreCommand = async () => {
1053
- const gitignorePath = join12(process.cwd(), ".gitignore");
1205
+ const gitignorePath = join14(process.cwd(), ".gitignore");
1054
1206
  const rulesFilesToIgnore = [
1055
1207
  "# Generated by rulesync - AI tool configuration files",
1056
1208
  "**/.github/copilot-instructions.md",
@@ -1067,6 +1219,8 @@ var gitignoreCommand = async () => {
1067
1219
  "**/GEMINI.md",
1068
1220
  "**/.gemini/memories/",
1069
1221
  "**/.aiexclude",
1222
+ "**/.aiignore",
1223
+ "**/.kiro/steering/",
1070
1224
  "**/.mcp.json",
1071
1225
  "!.rulesync/.mcp.json",
1072
1226
  "**/.cursor/mcp.json",
@@ -1104,17 +1258,17 @@ ${linesToAdd.join("\n")}
1104
1258
  };
1105
1259
 
1106
1260
  // src/core/importer.ts
1107
- import { join as join19 } from "path";
1261
+ import { join as join21 } from "path";
1108
1262
  import matter4 from "gray-matter";
1109
1263
 
1110
1264
  // src/parsers/claudecode.ts
1111
- import { basename as basename2, join as join13 } from "path";
1265
+ import { basename as basename2, join as join15 } from "path";
1112
1266
  async function parseClaudeConfiguration(baseDir = process.cwd()) {
1113
1267
  const errors = [];
1114
1268
  const rules = [];
1115
1269
  let ignorePatterns;
1116
1270
  let mcpServers;
1117
- const claudeFilePath = join13(baseDir, "CLAUDE.md");
1271
+ const claudeFilePath = join15(baseDir, "CLAUDE.md");
1118
1272
  if (!await fileExists(claudeFilePath)) {
1119
1273
  errors.push("CLAUDE.md file not found");
1120
1274
  return { rules, errors };
@@ -1125,12 +1279,12 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
1125
1279
  if (mainRule) {
1126
1280
  rules.push(mainRule);
1127
1281
  }
1128
- const memoryDir = join13(baseDir, ".claude", "memories");
1282
+ const memoryDir = join15(baseDir, ".claude", "memories");
1129
1283
  if (await fileExists(memoryDir)) {
1130
1284
  const memoryRules = await parseClaudeMemoryFiles(memoryDir);
1131
1285
  rules.push(...memoryRules);
1132
1286
  }
1133
- const settingsPath = join13(baseDir, ".claude", "settings.json");
1287
+ const settingsPath = join15(baseDir, ".claude", "settings.json");
1134
1288
  if (await fileExists(settingsPath)) {
1135
1289
  const settingsResult = await parseClaudeSettings(settingsPath);
1136
1290
  if (settingsResult.ignorePatterns) {
@@ -1187,7 +1341,7 @@ async function parseClaudeMemoryFiles(memoryDir) {
1187
1341
  const files = await readdir2(memoryDir);
1188
1342
  for (const file of files) {
1189
1343
  if (file.endsWith(".md")) {
1190
- const filePath = join13(memoryDir, file);
1344
+ const filePath = join15(memoryDir, file);
1191
1345
  const content = await readFileContent(filePath);
1192
1346
  if (content.trim()) {
1193
1347
  const filename = basename2(file, ".md");
@@ -1250,11 +1404,11 @@ async function parseClaudeSettings(settingsPath) {
1250
1404
  }
1251
1405
 
1252
1406
  // src/parsers/cline.ts
1253
- import { join as join14 } from "path";
1407
+ import { join as join16 } from "path";
1254
1408
  async function parseClineConfiguration(baseDir = process.cwd()) {
1255
1409
  const errors = [];
1256
1410
  const rules = [];
1257
- const clineFilePath = join14(baseDir, ".cline", "instructions.md");
1411
+ const clineFilePath = join16(baseDir, ".cline", "instructions.md");
1258
1412
  if (await fileExists(clineFilePath)) {
1259
1413
  try {
1260
1414
  const content = await readFileContent(clineFilePath);
@@ -1277,14 +1431,14 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
1277
1431
  errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
1278
1432
  }
1279
1433
  }
1280
- const clinerulesDirPath = join14(baseDir, ".clinerules");
1434
+ const clinerulesDirPath = join16(baseDir, ".clinerules");
1281
1435
  if (await fileExists(clinerulesDirPath)) {
1282
1436
  try {
1283
1437
  const { readdir: readdir2 } = await import("fs/promises");
1284
1438
  const files = await readdir2(clinerulesDirPath);
1285
1439
  for (const file of files) {
1286
1440
  if (file.endsWith(".md")) {
1287
- const filePath = join14(clinerulesDirPath, file);
1441
+ const filePath = join16(clinerulesDirPath, file);
1288
1442
  try {
1289
1443
  const content = await readFileContent(filePath);
1290
1444
  if (content.trim()) {
@@ -1320,12 +1474,12 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
1320
1474
  }
1321
1475
 
1322
1476
  // src/parsers/copilot.ts
1323
- import { basename as basename3, join as join15 } from "path";
1477
+ import { basename as basename3, join as join17 } from "path";
1324
1478
  import matter2 from "gray-matter";
1325
1479
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
1326
1480
  const errors = [];
1327
1481
  const rules = [];
1328
- const copilotFilePath = join15(baseDir, ".github", "copilot-instructions.md");
1482
+ const copilotFilePath = join17(baseDir, ".github", "copilot-instructions.md");
1329
1483
  if (await fileExists(copilotFilePath)) {
1330
1484
  try {
1331
1485
  const rawContent = await readFileContent(copilotFilePath);
@@ -1350,14 +1504,14 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
1350
1504
  errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
1351
1505
  }
1352
1506
  }
1353
- const instructionsDir = join15(baseDir, ".github", "instructions");
1507
+ const instructionsDir = join17(baseDir, ".github", "instructions");
1354
1508
  if (await fileExists(instructionsDir)) {
1355
1509
  try {
1356
1510
  const { readdir: readdir2 } = await import("fs/promises");
1357
1511
  const files = await readdir2(instructionsDir);
1358
1512
  for (const file of files) {
1359
1513
  if (file.endsWith(".instructions.md")) {
1360
- const filePath = join15(instructionsDir, file);
1514
+ const filePath = join17(instructionsDir, file);
1361
1515
  const rawContent = await readFileContent(filePath);
1362
1516
  const parsed = matter2(rawContent);
1363
1517
  const content = parsed.content.trim();
@@ -1392,10 +1546,10 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
1392
1546
  }
1393
1547
 
1394
1548
  // src/parsers/cursor.ts
1395
- import { basename as basename4, join as join16 } from "path";
1549
+ import { basename as basename4, join as join18 } from "path";
1396
1550
  import matter3 from "gray-matter";
1397
1551
  import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
1398
- import { z as z5 } from "zod/v4-mini";
1552
+ import { z as z5 } from "zod/mini";
1399
1553
  var customMatterOptions = {
1400
1554
  engines: {
1401
1555
  yaml: {
@@ -1517,7 +1671,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1517
1671
  const rules = [];
1518
1672
  let ignorePatterns;
1519
1673
  let mcpServers;
1520
- const cursorFilePath = join16(baseDir, ".cursorrules");
1674
+ const cursorFilePath = join18(baseDir, ".cursorrules");
1521
1675
  if (await fileExists(cursorFilePath)) {
1522
1676
  try {
1523
1677
  const rawContent = await readFileContent(cursorFilePath);
@@ -1538,14 +1692,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1538
1692
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
1539
1693
  }
1540
1694
  }
1541
- const cursorRulesDir = join16(baseDir, ".cursor", "rules");
1695
+ const cursorRulesDir = join18(baseDir, ".cursor", "rules");
1542
1696
  if (await fileExists(cursorRulesDir)) {
1543
1697
  try {
1544
1698
  const { readdir: readdir2 } = await import("fs/promises");
1545
1699
  const files = await readdir2(cursorRulesDir);
1546
1700
  for (const file of files) {
1547
1701
  if (file.endsWith(".mdc")) {
1548
- const filePath = join16(cursorRulesDir, file);
1702
+ const filePath = join18(cursorRulesDir, file);
1549
1703
  try {
1550
1704
  const rawContent = await readFileContent(filePath);
1551
1705
  const parsed = matter3(rawContent, customMatterOptions);
@@ -1574,7 +1728,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1574
1728
  if (rules.length === 0) {
1575
1729
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
1576
1730
  }
1577
- const cursorIgnorePath = join16(baseDir, ".cursorignore");
1731
+ const cursorIgnorePath = join18(baseDir, ".cursorignore");
1578
1732
  if (await fileExists(cursorIgnorePath)) {
1579
1733
  try {
1580
1734
  const content = await readFileContent(cursorIgnorePath);
@@ -1587,7 +1741,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1587
1741
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
1588
1742
  }
1589
1743
  }
1590
- const cursorMcpPath = join16(baseDir, ".cursor", "mcp.json");
1744
+ const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
1591
1745
  if (await fileExists(cursorMcpPath)) {
1592
1746
  try {
1593
1747
  const content = await readFileContent(cursorMcpPath);
@@ -1610,13 +1764,13 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1610
1764
  }
1611
1765
 
1612
1766
  // src/parsers/geminicli.ts
1613
- import { basename as basename5, join as join17 } from "path";
1767
+ import { basename as basename5, join as join19 } from "path";
1614
1768
  async function parseGeminiConfiguration(baseDir = process.cwd()) {
1615
1769
  const errors = [];
1616
1770
  const rules = [];
1617
1771
  let ignorePatterns;
1618
1772
  let mcpServers;
1619
- const geminiFilePath = join17(baseDir, "GEMINI.md");
1773
+ const geminiFilePath = join19(baseDir, "GEMINI.md");
1620
1774
  if (!await fileExists(geminiFilePath)) {
1621
1775
  errors.push("GEMINI.md file not found");
1622
1776
  return { rules, errors };
@@ -1627,12 +1781,12 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
1627
1781
  if (mainRule) {
1628
1782
  rules.push(mainRule);
1629
1783
  }
1630
- const memoryDir = join17(baseDir, ".gemini", "memories");
1784
+ const memoryDir = join19(baseDir, ".gemini", "memories");
1631
1785
  if (await fileExists(memoryDir)) {
1632
1786
  const memoryRules = await parseGeminiMemoryFiles(memoryDir);
1633
1787
  rules.push(...memoryRules);
1634
1788
  }
1635
- const settingsPath = join17(baseDir, ".gemini", "settings.json");
1789
+ const settingsPath = join19(baseDir, ".gemini", "settings.json");
1636
1790
  if (await fileExists(settingsPath)) {
1637
1791
  const settingsResult = await parseGeminiSettings(settingsPath);
1638
1792
  if (settingsResult.ignorePatterns) {
@@ -1643,7 +1797,7 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
1643
1797
  }
1644
1798
  errors.push(...settingsResult.errors);
1645
1799
  }
1646
- const aiexcludePath = join17(baseDir, ".aiexclude");
1800
+ const aiexcludePath = join19(baseDir, ".aiexclude");
1647
1801
  if (await fileExists(aiexcludePath)) {
1648
1802
  const aiexcludePatterns = await parseAiexclude(aiexcludePath);
1649
1803
  if (aiexcludePatterns.length > 0) {
@@ -1696,7 +1850,7 @@ async function parseGeminiMemoryFiles(memoryDir) {
1696
1850
  const files = await readdir2(memoryDir);
1697
1851
  for (const file of files) {
1698
1852
  if (file.endsWith(".md")) {
1699
- const filePath = join17(memoryDir, file);
1853
+ const filePath = join19(memoryDir, file);
1700
1854
  const content = await readFileContent(filePath);
1701
1855
  if (content.trim()) {
1702
1856
  const filename = basename5(file, ".md");
@@ -1749,11 +1903,11 @@ async function parseAiexclude(aiexcludePath) {
1749
1903
  }
1750
1904
 
1751
1905
  // src/parsers/roo.ts
1752
- import { join as join18 } from "path";
1906
+ import { join as join20 } from "path";
1753
1907
  async function parseRooConfiguration(baseDir = process.cwd()) {
1754
1908
  const errors = [];
1755
1909
  const rules = [];
1756
- const rooFilePath = join18(baseDir, ".roo", "instructions.md");
1910
+ const rooFilePath = join20(baseDir, ".roo", "instructions.md");
1757
1911
  if (await fileExists(rooFilePath)) {
1758
1912
  try {
1759
1913
  const content = await readFileContent(rooFilePath);
@@ -1776,14 +1930,14 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
1776
1930
  errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
1777
1931
  }
1778
1932
  }
1779
- const rooRulesDir = join18(baseDir, ".roo", "rules");
1933
+ const rooRulesDir = join20(baseDir, ".roo", "rules");
1780
1934
  if (await fileExists(rooRulesDir)) {
1781
1935
  try {
1782
1936
  const { readdir: readdir2 } = await import("fs/promises");
1783
1937
  const files = await readdir2(rooRulesDir);
1784
1938
  for (const file of files) {
1785
1939
  if (file.endsWith(".md")) {
1786
- const filePath = join18(rooRulesDir, file);
1940
+ const filePath = join20(rooRulesDir, file);
1787
1941
  try {
1788
1942
  const content = await readFileContent(filePath);
1789
1943
  if (content.trim()) {
@@ -1884,7 +2038,7 @@ async function importConfiguration(options) {
1884
2038
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
1885
2039
  return { success: false, rulesCreated: 0, errors };
1886
2040
  }
1887
- const rulesDirPath = join19(baseDir, rulesDir);
2041
+ const rulesDirPath = join21(baseDir, rulesDir);
1888
2042
  try {
1889
2043
  const { mkdir: mkdir3 } = await import("fs/promises");
1890
2044
  await mkdir3(rulesDirPath, { recursive: true });
@@ -1898,7 +2052,7 @@ async function importConfiguration(options) {
1898
2052
  try {
1899
2053
  const baseFilename = `${tool}__${rule.filename}`;
1900
2054
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
1901
- const filePath = join19(rulesDirPath, `${filename}.md`);
2055
+ const filePath = join21(rulesDirPath, `${filename}.md`);
1902
2056
  const content = generateRuleFileContent(rule);
1903
2057
  await writeFileContent(filePath, content);
1904
2058
  rulesCreated++;
@@ -1913,7 +2067,7 @@ async function importConfiguration(options) {
1913
2067
  let ignoreFileCreated = false;
1914
2068
  if (ignorePatterns && ignorePatterns.length > 0) {
1915
2069
  try {
1916
- const rulesyncignorePath = join19(baseDir, ".rulesyncignore");
2070
+ const rulesyncignorePath = join21(baseDir, ".rulesyncignore");
1917
2071
  const ignoreContent = `${ignorePatterns.join("\n")}
1918
2072
  `;
1919
2073
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -1929,7 +2083,7 @@ async function importConfiguration(options) {
1929
2083
  let mcpFileCreated = false;
1930
2084
  if (mcpServers && Object.keys(mcpServers).length > 0) {
1931
2085
  try {
1932
- const mcpPath = join19(baseDir, rulesDir, ".mcp.json");
2086
+ const mcpPath = join21(baseDir, rulesDir, ".mcp.json");
1933
2087
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
1934
2088
  `;
1935
2089
  await writeFileContent(mcpPath, mcpContent);
@@ -1943,7 +2097,7 @@ async function importConfiguration(options) {
1943
2097
  }
1944
2098
  }
1945
2099
  return {
1946
- success: rulesCreated > 0 || ignoreFileCreated || mcpFileCreated,
2100
+ success: errors.length === 0 && (rulesCreated > 0 || ignoreFileCreated || mcpFileCreated),
1947
2101
  rulesCreated,
1948
2102
  errors,
1949
2103
  ignoreFileCreated,
@@ -1957,7 +2111,7 @@ function generateRuleFileContent(rule) {
1957
2111
  async function generateUniqueFilename(rulesDir, baseFilename) {
1958
2112
  let filename = baseFilename;
1959
2113
  let counter = 1;
1960
- while (await fileExists(join19(rulesDir, `${filename}.md`))) {
2114
+ while (await fileExists(join21(rulesDir, `${filename}.md`))) {
1961
2115
  filename = `${baseFilename}-${counter}`;
1962
2116
  counter++;
1963
2117
  }
@@ -2022,7 +2176,7 @@ async function importCommand(options = {}) {
2022
2176
  }
2023
2177
 
2024
2178
  // src/cli/commands/init.ts
2025
- import { join as join20 } from "path";
2179
+ import { join as join22 } from "path";
2026
2180
  async function initCommand() {
2027
2181
  const aiRulesDir = ".rulesync";
2028
2182
  console.log("Initializing rulesync...");
@@ -2069,7 +2223,7 @@ globs: ["**/*"]
2069
2223
  - Follow single responsibility principle
2070
2224
  `
2071
2225
  };
2072
- const filepath = join20(aiRulesDir, sampleFile.filename);
2226
+ const filepath = join22(aiRulesDir, sampleFile.filename);
2073
2227
  if (!await fileExists(filepath)) {
2074
2228
  await writeFileContent(filepath, sampleFile.content);
2075
2229
  console.log(`Created ${filepath}`);
@@ -2213,12 +2367,12 @@ async function watchCommand() {
2213
2367
 
2214
2368
  // src/cli/index.ts
2215
2369
  var program = new Command();
2216
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.45.0");
2370
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.48.0");
2217
2371
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
2218
2372
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
2219
2373
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
2220
2374
  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(
2375
+ 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
2376
  "-b, --base-dir <paths>",
2223
2377
  "Base directories to generate files (comma-separated for multiple paths)"
2224
2378
  ).option("-v, --verbose", "Verbose output").action(async (options) => {
@@ -2229,6 +2383,7 @@ program.command("generate").description("Generate configuration files for AI too
2229
2383
  if (options.claudecode) tools.push("claudecode");
2230
2384
  if (options.roo) tools.push("roo");
2231
2385
  if (options.geminicli) tools.push("geminicli");
2386
+ if (options.kiro) tools.push("kiro");
2232
2387
  const generateOptions = {
2233
2388
  verbose: options.verbose,
2234
2389
  delete: options.delete