rulesync 0.44.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;
@@ -265,9 +374,10 @@ function generateClaudeMarkdown(rootRules, detailRules) {
265
374
  lines.push("Please also reference the following documents as needed:");
266
375
  lines.push("");
267
376
  for (const rule of detailRules) {
268
- const globsText = rule.frontmatter.globs.length > 0 ? rule.frontmatter.globs.join(", ") : "";
377
+ const escapedDescription = rule.frontmatter.description.replace(/"/g, '\\"');
378
+ const globsText = rule.frontmatter.globs.join(",");
269
379
  lines.push(
270
- `@.claude/memories/${rule.filename}.md ${rule.frontmatter.description} ${globsText}`.trim()
380
+ `@.claude/memories/${rule.filename}.md description: "${escapedDescription}" globs: "${globsText}"`
271
381
  );
272
382
  }
273
383
  lines.push("");
@@ -317,13 +427,13 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
317
427
  }
318
428
 
319
429
  // src/generators/rules/cline.ts
320
- import { join as join5 } from "path";
430
+ import { join as join6 } from "path";
321
431
  async function generateClineConfig(rules, config, baseDir) {
322
432
  const outputs = [];
323
433
  for (const rule of rules) {
324
434
  const content = generateClineMarkdown(rule);
325
- const outputDir = baseDir ? join5(baseDir, config.outputPaths.cline) : config.outputPaths.cline;
326
- 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`);
327
437
  outputs.push({
328
438
  tool: "cline",
329
439
  filepath,
@@ -332,7 +442,7 @@ async function generateClineConfig(rules, config, baseDir) {
332
442
  }
333
443
  const ignorePatterns = await loadIgnorePatterns(baseDir);
334
444
  if (ignorePatterns.patterns.length > 0) {
335
- const clineIgnorePath = baseDir ? join5(baseDir, ".clineignore") : ".clineignore";
445
+ const clineIgnorePath = baseDir ? join6(baseDir, ".clineignore") : ".clineignore";
336
446
  const clineIgnoreContent = generateClineIgnore(ignorePatterns.patterns);
337
447
  outputs.push({
338
448
  tool: "cline",
@@ -356,14 +466,14 @@ function generateClineIgnore(patterns) {
356
466
  }
357
467
 
358
468
  // src/generators/rules/copilot.ts
359
- import { join as join6 } from "path";
469
+ import { join as join7 } from "path";
360
470
  async function generateCopilotConfig(rules, config, baseDir) {
361
471
  const outputs = [];
362
472
  for (const rule of rules) {
363
473
  const content = generateCopilotMarkdown(rule);
364
474
  const baseFilename = rule.filename.replace(/\.md$/, "");
365
- const outputDir = baseDir ? join6(baseDir, config.outputPaths.copilot) : config.outputPaths.copilot;
366
- 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`);
367
477
  outputs.push({
368
478
  tool: "copilot",
369
479
  filepath,
@@ -372,7 +482,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
372
482
  }
373
483
  const ignorePatterns = await loadIgnorePatterns(baseDir);
374
484
  if (ignorePatterns.patterns.length > 0) {
375
- const copilotIgnorePath = baseDir ? join6(baseDir, ".copilotignore") : ".copilotignore";
485
+ const copilotIgnorePath = baseDir ? join7(baseDir, ".copilotignore") : ".copilotignore";
376
486
  const copilotIgnoreContent = generateCopilotIgnore(ignorePatterns.patterns);
377
487
  outputs.push({
378
488
  tool: "copilot",
@@ -408,13 +518,13 @@ function generateCopilotIgnore(patterns) {
408
518
  }
409
519
 
410
520
  // src/generators/rules/cursor.ts
411
- import { join as join7 } from "path";
521
+ import { join as join8 } from "path";
412
522
  async function generateCursorConfig(rules, config, baseDir) {
413
523
  const outputs = [];
414
524
  for (const rule of rules) {
415
525
  const content = generateCursorMarkdown(rule);
416
- const outputDir = baseDir ? join7(baseDir, config.outputPaths.cursor) : config.outputPaths.cursor;
417
- 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`);
418
528
  outputs.push({
419
529
  tool: "cursor",
420
530
  filepath,
@@ -423,7 +533,7 @@ async function generateCursorConfig(rules, config, baseDir) {
423
533
  }
424
534
  const ignorePatterns = await loadIgnorePatterns(baseDir);
425
535
  if (ignorePatterns.patterns.length > 0) {
426
- const cursorIgnorePath = baseDir ? join7(baseDir, ".cursorignore") : ".cursorignore";
536
+ const cursorIgnorePath = baseDir ? join8(baseDir, ".cursorignore") : ".cursorignore";
427
537
  const cursorIgnoreContent = generateCursorIgnore(ignorePatterns.patterns);
428
538
  outputs.push({
429
539
  tool: "cursor",
@@ -496,15 +606,15 @@ function generateCursorIgnore(patterns) {
496
606
  }
497
607
 
498
608
  // src/generators/rules/geminicli.ts
499
- import { join as join8 } from "path";
609
+ import { join as join9 } from "path";
500
610
  async function generateGeminiConfig(rules, config, baseDir) {
501
611
  const outputs = [];
502
612
  const rootRule = rules.find((rule) => rule.frontmatter.root === true);
503
613
  const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
504
614
  for (const rule of memoryRules) {
505
615
  const content = generateGeminiMemoryMarkdown(rule);
506
- const outputDir = baseDir ? join8(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
507
- 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`);
508
618
  outputs.push({
509
619
  tool: "geminicli",
510
620
  filepath,
@@ -512,7 +622,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
512
622
  });
513
623
  }
514
624
  const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
515
- const rootFilepath = baseDir ? join8(baseDir, "GEMINI.md") : "GEMINI.md";
625
+ const rootFilepath = baseDir ? join9(baseDir, "GEMINI.md") : "GEMINI.md";
516
626
  outputs.push({
517
627
  tool: "geminicli",
518
628
  filepath: rootFilepath,
@@ -520,7 +630,7 @@ async function generateGeminiConfig(rules, config, baseDir) {
520
630
  });
521
631
  const ignorePatterns = await loadIgnorePatterns(baseDir);
522
632
  if (ignorePatterns.patterns.length > 0) {
523
- const aiexcludePath = baseDir ? join8(baseDir, ".aiexclude") : ".aiexclude";
633
+ const aiexcludePath = baseDir ? join9(baseDir, ".aiexclude") : ".aiexclude";
524
634
  const aiexcludeContent = generateAiexclude(ignorePatterns.patterns);
525
635
  outputs.push({
526
636
  tool: "geminicli",
@@ -567,14 +677,34 @@ function generateAiexclude(patterns) {
567
677
  return lines.join("\n");
568
678
  }
569
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
+
570
700
  // src/generators/rules/roo.ts
571
- import { join as join9 } from "path";
701
+ import { join as join11 } from "path";
572
702
  async function generateRooConfig(rules, config, baseDir) {
573
703
  const outputs = [];
574
704
  for (const rule of rules) {
575
705
  const content = generateRooMarkdown(rule);
576
- const outputDir = baseDir ? join9(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
577
- 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`);
578
708
  outputs.push({
579
709
  tool: "roo",
580
710
  filepath,
@@ -583,7 +713,7 @@ async function generateRooConfig(rules, config, baseDir) {
583
713
  }
584
714
  const ignorePatterns = await loadIgnorePatterns(baseDir);
585
715
  if (ignorePatterns.patterns.length > 0) {
586
- const rooIgnorePath = baseDir ? join9(baseDir, ".rooignore") : ".rooignore";
716
+ const rooIgnorePath = baseDir ? join11(baseDir, ".rooignore") : ".rooignore";
587
717
  const rooIgnoreContent = generateRooIgnore(ignorePatterns.patterns);
588
718
  outputs.push({
589
719
  tool: "roo",
@@ -649,6 +779,11 @@ async function generateForTool(tool, rules, config, baseDir) {
649
779
  return generateRooConfig(rules, config, baseDir);
650
780
  case "geminicli":
651
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
+ }
652
787
  default:
653
788
  console.warn(`Unknown tool: ${tool}`);
654
789
  return null;
@@ -660,7 +795,7 @@ import { basename } from "path";
660
795
  import matter from "gray-matter";
661
796
 
662
797
  // src/types/config.ts
663
- import { z as z2 } from "zod/v4-mini";
798
+ import { z as z2 } from "zod/mini";
664
799
  var ConfigSchema = z2.object({
665
800
  aiRulesDir: z2.string(),
666
801
  outputPaths: z2.record(ToolTargetSchema, z2.string()),
@@ -669,7 +804,7 @@ var ConfigSchema = z2.object({
669
804
  });
670
805
 
671
806
  // src/types/mcp.ts
672
- import { z as z3 } from "zod/v4-mini";
807
+ import { z as z3 } from "zod/mini";
673
808
  var McpTransportTypeSchema = z3.enum(["stdio", "sse", "http"]);
674
809
  var McpServerBaseSchema = z3.object({
675
810
  command: z3.optional(z3.string()),
@@ -685,7 +820,9 @@ var McpServerBaseSchema = z3.object({
685
820
  transport: z3.optional(McpTransportTypeSchema),
686
821
  type: z3.optional(z3.enum(["sse", "streamable-http"])),
687
822
  alwaysAllow: z3.optional(z3.array(z3.string())),
688
- 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()))
689
826
  });
690
827
  var RulesyncMcpServerSchema = z3.extend(McpServerBaseSchema, {
691
828
  targets: z3.optional(RulesyncTargetsSchema)
@@ -698,7 +835,7 @@ var RulesyncMcpConfigSchema = z3.object({
698
835
  });
699
836
 
700
837
  // src/types/rules.ts
701
- import { z as z4 } from "zod/v4-mini";
838
+ import { z as z4 } from "zod/mini";
702
839
  var RuleFrontmatterSchema = z4.object({
703
840
  root: z4.boolean(),
704
841
  targets: RulesyncTargetsSchema,
@@ -726,7 +863,8 @@ var GenerateOptionsSchema = z4.object({
726
863
  // src/core/parser.ts
727
864
  async function parseRulesFromDirectory(aiRulesDir) {
728
865
  const ignorePatterns = await loadIgnorePatterns();
729
- const ruleFiles = await findFiles(aiRulesDir, ".md", ignorePatterns.patterns);
866
+ const allRuleFiles = await findFiles(aiRulesDir, ".md");
867
+ const ruleFiles = filterIgnoredFiles(allRuleFiles, ignorePatterns.patterns);
730
868
  const rules = [];
731
869
  const errors = [];
732
870
  if (ignorePatterns.patterns.length > 0) {
@@ -887,6 +1025,11 @@ async function generateMcpConfigs(projectRoot, baseDir) {
887
1025
  path: path3.join(targetRoot, ".gemini", "settings.json"),
888
1026
  generate: () => generateGeminiCliMcp(config)
889
1027
  },
1028
+ {
1029
+ tool: "kiro-project",
1030
+ path: path3.join(targetRoot, ".kiro", "mcp.json"),
1031
+ generate: () => generateKiroMcp(config)
1032
+ },
890
1033
  {
891
1034
  tool: "roo-project",
892
1035
  path: path3.join(targetRoot, ".roo", "mcp.json"),
@@ -897,7 +1040,7 @@ async function generateMcpConfigs(projectRoot, baseDir) {
897
1040
  try {
898
1041
  const content = generator.generate();
899
1042
  const parsed = JSON.parse(content);
900
- 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")) {
901
1044
  if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
902
1045
  results.push({
903
1046
  tool: generator.tool,
@@ -983,6 +1126,9 @@ async function generateCommand(options = {}) {
983
1126
  case "geminicli":
984
1127
  deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
985
1128
  break;
1129
+ case "kiro":
1130
+ deleteTasks.push(removeDirectory(config.outputPaths.kiro));
1131
+ break;
986
1132
  }
987
1133
  }
988
1134
  await Promise.all(deleteTasks);
@@ -1047,9 +1193,9 @@ Generating configurations for base directory: ${baseDir}`);
1047
1193
 
1048
1194
  // src/cli/commands/gitignore.ts
1049
1195
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
1050
- import { join as join12 } from "path";
1196
+ import { join as join14 } from "path";
1051
1197
  var gitignoreCommand = async () => {
1052
- const gitignorePath = join12(process.cwd(), ".gitignore");
1198
+ const gitignorePath = join14(process.cwd(), ".gitignore");
1053
1199
  const rulesFilesToIgnore = [
1054
1200
  "# Generated by rulesync - AI tool configuration files",
1055
1201
  "**/.github/copilot-instructions.md",
@@ -1066,6 +1212,8 @@ var gitignoreCommand = async () => {
1066
1212
  "**/GEMINI.md",
1067
1213
  "**/.gemini/memories/",
1068
1214
  "**/.aiexclude",
1215
+ "**/.aiignore",
1216
+ "**/.kiro/steering/",
1069
1217
  "**/.mcp.json",
1070
1218
  "!.rulesync/.mcp.json",
1071
1219
  "**/.cursor/mcp.json",
@@ -1103,17 +1251,17 @@ ${linesToAdd.join("\n")}
1103
1251
  };
1104
1252
 
1105
1253
  // src/core/importer.ts
1106
- import { join as join19 } from "path";
1254
+ import { join as join21 } from "path";
1107
1255
  import matter4 from "gray-matter";
1108
1256
 
1109
1257
  // src/parsers/claudecode.ts
1110
- import { basename as basename2, join as join13 } from "path";
1258
+ import { basename as basename2, join as join15 } from "path";
1111
1259
  async function parseClaudeConfiguration(baseDir = process.cwd()) {
1112
1260
  const errors = [];
1113
1261
  const rules = [];
1114
1262
  let ignorePatterns;
1115
1263
  let mcpServers;
1116
- const claudeFilePath = join13(baseDir, "CLAUDE.md");
1264
+ const claudeFilePath = join15(baseDir, "CLAUDE.md");
1117
1265
  if (!await fileExists(claudeFilePath)) {
1118
1266
  errors.push("CLAUDE.md file not found");
1119
1267
  return { rules, errors };
@@ -1124,12 +1272,12 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
1124
1272
  if (mainRule) {
1125
1273
  rules.push(mainRule);
1126
1274
  }
1127
- const memoryDir = join13(baseDir, ".claude", "memories");
1275
+ const memoryDir = join15(baseDir, ".claude", "memories");
1128
1276
  if (await fileExists(memoryDir)) {
1129
1277
  const memoryRules = await parseClaudeMemoryFiles(memoryDir);
1130
1278
  rules.push(...memoryRules);
1131
1279
  }
1132
- const settingsPath = join13(baseDir, ".claude", "settings.json");
1280
+ const settingsPath = join15(baseDir, ".claude", "settings.json");
1133
1281
  if (await fileExists(settingsPath)) {
1134
1282
  const settingsResult = await parseClaudeSettings(settingsPath);
1135
1283
  if (settingsResult.ignorePatterns) {
@@ -1186,7 +1334,7 @@ async function parseClaudeMemoryFiles(memoryDir) {
1186
1334
  const files = await readdir2(memoryDir);
1187
1335
  for (const file of files) {
1188
1336
  if (file.endsWith(".md")) {
1189
- const filePath = join13(memoryDir, file);
1337
+ const filePath = join15(memoryDir, file);
1190
1338
  const content = await readFileContent(filePath);
1191
1339
  if (content.trim()) {
1192
1340
  const filename = basename2(file, ".md");
@@ -1249,11 +1397,11 @@ async function parseClaudeSettings(settingsPath) {
1249
1397
  }
1250
1398
 
1251
1399
  // src/parsers/cline.ts
1252
- import { join as join14 } from "path";
1400
+ import { join as join16 } from "path";
1253
1401
  async function parseClineConfiguration(baseDir = process.cwd()) {
1254
1402
  const errors = [];
1255
1403
  const rules = [];
1256
- const clineFilePath = join14(baseDir, ".cline", "instructions.md");
1404
+ const clineFilePath = join16(baseDir, ".cline", "instructions.md");
1257
1405
  if (await fileExists(clineFilePath)) {
1258
1406
  try {
1259
1407
  const content = await readFileContent(clineFilePath);
@@ -1276,14 +1424,14 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
1276
1424
  errors.push(`Failed to parse .cline/instructions.md: ${errorMessage}`);
1277
1425
  }
1278
1426
  }
1279
- const clinerulesDirPath = join14(baseDir, ".clinerules");
1427
+ const clinerulesDirPath = join16(baseDir, ".clinerules");
1280
1428
  if (await fileExists(clinerulesDirPath)) {
1281
1429
  try {
1282
1430
  const { readdir: readdir2 } = await import("fs/promises");
1283
1431
  const files = await readdir2(clinerulesDirPath);
1284
1432
  for (const file of files) {
1285
1433
  if (file.endsWith(".md")) {
1286
- const filePath = join14(clinerulesDirPath, file);
1434
+ const filePath = join16(clinerulesDirPath, file);
1287
1435
  try {
1288
1436
  const content = await readFileContent(filePath);
1289
1437
  if (content.trim()) {
@@ -1319,12 +1467,12 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
1319
1467
  }
1320
1468
 
1321
1469
  // src/parsers/copilot.ts
1322
- import { basename as basename3, join as join15 } from "path";
1470
+ import { basename as basename3, join as join17 } from "path";
1323
1471
  import matter2 from "gray-matter";
1324
1472
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
1325
1473
  const errors = [];
1326
1474
  const rules = [];
1327
- const copilotFilePath = join15(baseDir, ".github", "copilot-instructions.md");
1475
+ const copilotFilePath = join17(baseDir, ".github", "copilot-instructions.md");
1328
1476
  if (await fileExists(copilotFilePath)) {
1329
1477
  try {
1330
1478
  const rawContent = await readFileContent(copilotFilePath);
@@ -1349,14 +1497,14 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
1349
1497
  errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
1350
1498
  }
1351
1499
  }
1352
- const instructionsDir = join15(baseDir, ".github", "instructions");
1500
+ const instructionsDir = join17(baseDir, ".github", "instructions");
1353
1501
  if (await fileExists(instructionsDir)) {
1354
1502
  try {
1355
1503
  const { readdir: readdir2 } = await import("fs/promises");
1356
1504
  const files = await readdir2(instructionsDir);
1357
1505
  for (const file of files) {
1358
1506
  if (file.endsWith(".instructions.md")) {
1359
- const filePath = join15(instructionsDir, file);
1507
+ const filePath = join17(instructionsDir, file);
1360
1508
  const rawContent = await readFileContent(filePath);
1361
1509
  const parsed = matter2(rawContent);
1362
1510
  const content = parsed.content.trim();
@@ -1391,10 +1539,10 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
1391
1539
  }
1392
1540
 
1393
1541
  // src/parsers/cursor.ts
1394
- import { basename as basename4, join as join16 } from "path";
1542
+ import { basename as basename4, join as join18 } from "path";
1395
1543
  import matter3 from "gray-matter";
1396
1544
  import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
1397
- import { z as z5 } from "zod/v4-mini";
1545
+ import { z as z5 } from "zod/mini";
1398
1546
  var customMatterOptions = {
1399
1547
  engines: {
1400
1548
  yaml: {
@@ -1516,7 +1664,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1516
1664
  const rules = [];
1517
1665
  let ignorePatterns;
1518
1666
  let mcpServers;
1519
- const cursorFilePath = join16(baseDir, ".cursorrules");
1667
+ const cursorFilePath = join18(baseDir, ".cursorrules");
1520
1668
  if (await fileExists(cursorFilePath)) {
1521
1669
  try {
1522
1670
  const rawContent = await readFileContent(cursorFilePath);
@@ -1537,14 +1685,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1537
1685
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
1538
1686
  }
1539
1687
  }
1540
- const cursorRulesDir = join16(baseDir, ".cursor", "rules");
1688
+ const cursorRulesDir = join18(baseDir, ".cursor", "rules");
1541
1689
  if (await fileExists(cursorRulesDir)) {
1542
1690
  try {
1543
1691
  const { readdir: readdir2 } = await import("fs/promises");
1544
1692
  const files = await readdir2(cursorRulesDir);
1545
1693
  for (const file of files) {
1546
1694
  if (file.endsWith(".mdc")) {
1547
- const filePath = join16(cursorRulesDir, file);
1695
+ const filePath = join18(cursorRulesDir, file);
1548
1696
  try {
1549
1697
  const rawContent = await readFileContent(filePath);
1550
1698
  const parsed = matter3(rawContent, customMatterOptions);
@@ -1573,7 +1721,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1573
1721
  if (rules.length === 0) {
1574
1722
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
1575
1723
  }
1576
- const cursorIgnorePath = join16(baseDir, ".cursorignore");
1724
+ const cursorIgnorePath = join18(baseDir, ".cursorignore");
1577
1725
  if (await fileExists(cursorIgnorePath)) {
1578
1726
  try {
1579
1727
  const content = await readFileContent(cursorIgnorePath);
@@ -1586,7 +1734,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1586
1734
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
1587
1735
  }
1588
1736
  }
1589
- const cursorMcpPath = join16(baseDir, ".cursor", "mcp.json");
1737
+ const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
1590
1738
  if (await fileExists(cursorMcpPath)) {
1591
1739
  try {
1592
1740
  const content = await readFileContent(cursorMcpPath);
@@ -1609,13 +1757,13 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
1609
1757
  }
1610
1758
 
1611
1759
  // src/parsers/geminicli.ts
1612
- import { basename as basename5, join as join17 } from "path";
1760
+ import { basename as basename5, join as join19 } from "path";
1613
1761
  async function parseGeminiConfiguration(baseDir = process.cwd()) {
1614
1762
  const errors = [];
1615
1763
  const rules = [];
1616
1764
  let ignorePatterns;
1617
1765
  let mcpServers;
1618
- const geminiFilePath = join17(baseDir, "GEMINI.md");
1766
+ const geminiFilePath = join19(baseDir, "GEMINI.md");
1619
1767
  if (!await fileExists(geminiFilePath)) {
1620
1768
  errors.push("GEMINI.md file not found");
1621
1769
  return { rules, errors };
@@ -1626,12 +1774,12 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
1626
1774
  if (mainRule) {
1627
1775
  rules.push(mainRule);
1628
1776
  }
1629
- const memoryDir = join17(baseDir, ".gemini", "memories");
1777
+ const memoryDir = join19(baseDir, ".gemini", "memories");
1630
1778
  if (await fileExists(memoryDir)) {
1631
1779
  const memoryRules = await parseGeminiMemoryFiles(memoryDir);
1632
1780
  rules.push(...memoryRules);
1633
1781
  }
1634
- const settingsPath = join17(baseDir, ".gemini", "settings.json");
1782
+ const settingsPath = join19(baseDir, ".gemini", "settings.json");
1635
1783
  if (await fileExists(settingsPath)) {
1636
1784
  const settingsResult = await parseGeminiSettings(settingsPath);
1637
1785
  if (settingsResult.ignorePatterns) {
@@ -1642,7 +1790,7 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
1642
1790
  }
1643
1791
  errors.push(...settingsResult.errors);
1644
1792
  }
1645
- const aiexcludePath = join17(baseDir, ".aiexclude");
1793
+ const aiexcludePath = join19(baseDir, ".aiexclude");
1646
1794
  if (await fileExists(aiexcludePath)) {
1647
1795
  const aiexcludePatterns = await parseAiexclude(aiexcludePath);
1648
1796
  if (aiexcludePatterns.length > 0) {
@@ -1695,7 +1843,7 @@ async function parseGeminiMemoryFiles(memoryDir) {
1695
1843
  const files = await readdir2(memoryDir);
1696
1844
  for (const file of files) {
1697
1845
  if (file.endsWith(".md")) {
1698
- const filePath = join17(memoryDir, file);
1846
+ const filePath = join19(memoryDir, file);
1699
1847
  const content = await readFileContent(filePath);
1700
1848
  if (content.trim()) {
1701
1849
  const filename = basename5(file, ".md");
@@ -1748,11 +1896,11 @@ async function parseAiexclude(aiexcludePath) {
1748
1896
  }
1749
1897
 
1750
1898
  // src/parsers/roo.ts
1751
- import { join as join18 } from "path";
1899
+ import { join as join20 } from "path";
1752
1900
  async function parseRooConfiguration(baseDir = process.cwd()) {
1753
1901
  const errors = [];
1754
1902
  const rules = [];
1755
- const rooFilePath = join18(baseDir, ".roo", "instructions.md");
1903
+ const rooFilePath = join20(baseDir, ".roo", "instructions.md");
1756
1904
  if (await fileExists(rooFilePath)) {
1757
1905
  try {
1758
1906
  const content = await readFileContent(rooFilePath);
@@ -1775,14 +1923,14 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
1775
1923
  errors.push(`Failed to parse .roo/instructions.md: ${errorMessage}`);
1776
1924
  }
1777
1925
  }
1778
- const rooRulesDir = join18(baseDir, ".roo", "rules");
1926
+ const rooRulesDir = join20(baseDir, ".roo", "rules");
1779
1927
  if (await fileExists(rooRulesDir)) {
1780
1928
  try {
1781
1929
  const { readdir: readdir2 } = await import("fs/promises");
1782
1930
  const files = await readdir2(rooRulesDir);
1783
1931
  for (const file of files) {
1784
1932
  if (file.endsWith(".md")) {
1785
- const filePath = join18(rooRulesDir, file);
1933
+ const filePath = join20(rooRulesDir, file);
1786
1934
  try {
1787
1935
  const content = await readFileContent(filePath);
1788
1936
  if (content.trim()) {
@@ -1883,7 +2031,7 @@ async function importConfiguration(options) {
1883
2031
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
1884
2032
  return { success: false, rulesCreated: 0, errors };
1885
2033
  }
1886
- const rulesDirPath = join19(baseDir, rulesDir);
2034
+ const rulesDirPath = join21(baseDir, rulesDir);
1887
2035
  try {
1888
2036
  const { mkdir: mkdir3 } = await import("fs/promises");
1889
2037
  await mkdir3(rulesDirPath, { recursive: true });
@@ -1897,7 +2045,7 @@ async function importConfiguration(options) {
1897
2045
  try {
1898
2046
  const baseFilename = `${tool}__${rule.filename}`;
1899
2047
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
1900
- const filePath = join19(rulesDirPath, `${filename}.md`);
2048
+ const filePath = join21(rulesDirPath, `${filename}.md`);
1901
2049
  const content = generateRuleFileContent(rule);
1902
2050
  await writeFileContent(filePath, content);
1903
2051
  rulesCreated++;
@@ -1912,7 +2060,7 @@ async function importConfiguration(options) {
1912
2060
  let ignoreFileCreated = false;
1913
2061
  if (ignorePatterns && ignorePatterns.length > 0) {
1914
2062
  try {
1915
- const rulesyncignorePath = join19(baseDir, ".rulesyncignore");
2063
+ const rulesyncignorePath = join21(baseDir, ".rulesyncignore");
1916
2064
  const ignoreContent = `${ignorePatterns.join("\n")}
1917
2065
  `;
1918
2066
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -1928,7 +2076,7 @@ async function importConfiguration(options) {
1928
2076
  let mcpFileCreated = false;
1929
2077
  if (mcpServers && Object.keys(mcpServers).length > 0) {
1930
2078
  try {
1931
- const mcpPath = join19(baseDir, rulesDir, ".mcp.json");
2079
+ const mcpPath = join21(baseDir, rulesDir, ".mcp.json");
1932
2080
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
1933
2081
  `;
1934
2082
  await writeFileContent(mcpPath, mcpContent);
@@ -1942,7 +2090,7 @@ async function importConfiguration(options) {
1942
2090
  }
1943
2091
  }
1944
2092
  return {
1945
- success: rulesCreated > 0 || ignoreFileCreated || mcpFileCreated,
2093
+ success: errors.length === 0 && (rulesCreated > 0 || ignoreFileCreated || mcpFileCreated),
1946
2094
  rulesCreated,
1947
2095
  errors,
1948
2096
  ignoreFileCreated,
@@ -1956,7 +2104,7 @@ function generateRuleFileContent(rule) {
1956
2104
  async function generateUniqueFilename(rulesDir, baseFilename) {
1957
2105
  let filename = baseFilename;
1958
2106
  let counter = 1;
1959
- while (await fileExists(join19(rulesDir, `${filename}.md`))) {
2107
+ while (await fileExists(join21(rulesDir, `${filename}.md`))) {
1960
2108
  filename = `${baseFilename}-${counter}`;
1961
2109
  counter++;
1962
2110
  }
@@ -2021,7 +2169,7 @@ async function importCommand(options = {}) {
2021
2169
  }
2022
2170
 
2023
2171
  // src/cli/commands/init.ts
2024
- import { join as join20 } from "path";
2172
+ import { join as join22 } from "path";
2025
2173
  async function initCommand() {
2026
2174
  const aiRulesDir = ".rulesync";
2027
2175
  console.log("Initializing rulesync...");
@@ -2033,14 +2181,13 @@ async function initCommand() {
2033
2181
  console.log("2. Run 'rulesync generate' to create configuration files");
2034
2182
  }
2035
2183
  async function createSampleFiles(aiRulesDir) {
2036
- const sampleFiles = [
2037
- {
2038
- filename: "overview.md",
2039
- content: `---
2184
+ const sampleFile = {
2185
+ filename: "overview.md",
2186
+ content: `---
2040
2187
  root: true
2041
2188
  targets: ["*"]
2042
2189
  description: "Project overview and general development guidelines"
2043
- globs: ["**/*.ts", "**/*.js", "**/*.tsx", "**/*.jsx"]
2190
+ globs: ["**/*"]
2044
2191
  ---
2045
2192
 
2046
2193
  # Project Overview
@@ -2068,96 +2215,13 @@ globs: ["**/*.ts", "**/*.js", "**/*.tsx", "**/*.jsx"]
2068
2215
  - Implement proper error handling
2069
2216
  - Follow single responsibility principle
2070
2217
  `
2071
- },
2072
- {
2073
- filename: "frontend.md",
2074
- content: `---
2075
- root: false
2076
- targets: ["*"]
2077
- description: "Frontend development rules and best practices"
2078
- globs: ["src/components/**/*.tsx", "src/pages/**/*.tsx", "**/*.css", "**/*.scss"]
2079
- ---
2080
-
2081
- # Frontend Development Rules
2082
-
2083
- ## React Components
2084
-
2085
- - Use functional components with hooks
2086
- - Follow PascalCase naming for components
2087
- - Use TypeScript interfaces for props
2088
- - Implement proper error boundaries
2089
-
2090
- ## Styling
2091
-
2092
- - Use CSS modules or styled-components
2093
- - Follow BEM methodology for CSS classes
2094
- - Prefer flexbox and grid for layouts
2095
- - Use semantic HTML elements
2096
-
2097
- ## State Management
2098
-
2099
- - Use React hooks for local state
2100
- - Consider Redux or Zustand for global state
2101
- - Avoid prop drilling with context API
2102
- - Keep state as close to where it's used as possible
2103
-
2104
- ## Performance
2105
-
2106
- - Use React.memo for expensive components
2107
- - Implement lazy loading for routes
2108
- - Optimize images and assets
2109
- - Use proper key props in lists
2110
- `
2111
- },
2112
- {
2113
- filename: "backend.md",
2114
- content: `---
2115
- root: false
2116
- targets: ["*"]
2117
- description: "Backend development rules and API guidelines"
2118
- globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
2119
- ---
2120
-
2121
- # Backend Development Rules
2122
-
2123
- ## API Design
2124
-
2125
- - Follow RESTful conventions
2126
- - Use consistent HTTP status codes
2127
- - Implement proper error handling with meaningful messages
2128
- - Use API versioning when necessary
2129
-
2130
- ## Database
2131
-
2132
- - Use proper indexing for performance
2133
- - Implement database migrations
2134
- - Follow naming conventions for tables and columns
2135
- - Use transactions for data consistency
2136
-
2137
- ## Security
2138
-
2139
- - Validate all input data
2140
- - Use proper authentication and authorization
2141
- - Implement rate limiting
2142
- - Sanitize database queries to prevent SQL injection
2143
-
2144
- ## Code Organization
2145
-
2146
- - Use service layer pattern
2147
- - Implement proper logging
2148
- - Use environment variables for configuration
2149
- - Write comprehensive tests for business logic
2150
- `
2151
- }
2152
- ];
2153
- for (const file of sampleFiles) {
2154
- const filepath = join20(aiRulesDir, file.filename);
2155
- if (!await fileExists(filepath)) {
2156
- await writeFileContent(filepath, file.content);
2157
- console.log(`Created ${filepath}`);
2158
- } else {
2159
- console.log(`Skipped ${filepath} (already exists)`);
2160
- }
2218
+ };
2219
+ const filepath = join22(aiRulesDir, sampleFile.filename);
2220
+ if (!await fileExists(filepath)) {
2221
+ await writeFileContent(filepath, sampleFile.content);
2222
+ console.log(`Created ${filepath}`);
2223
+ } else {
2224
+ console.log(`Skipped ${filepath} (already exists)`);
2161
2225
  }
2162
2226
  }
2163
2227
 
@@ -2296,12 +2360,12 @@ async function watchCommand() {
2296
2360
 
2297
2361
  // src/cli/index.ts
2298
2362
  var program = new Command();
2299
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.44.0");
2363
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.47.0");
2300
2364
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
2301
2365
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
2302
2366
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
2303
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);
2304
- 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(
2305
2369
  "-b, --base-dir <paths>",
2306
2370
  "Base directories to generate files (comma-separated for multiple paths)"
2307
2371
  ).option("-v, --verbose", "Verbose output").action(async (options) => {
@@ -2312,6 +2376,7 @@ program.command("generate").description("Generate configuration files for AI too
2312
2376
  if (options.claudecode) tools.push("claudecode");
2313
2377
  if (options.roo) tools.push("roo");
2314
2378
  if (options.geminicli) tools.push("geminicli");
2379
+ if (options.kiro) tools.push("kiro");
2315
2380
  const generateOptions = {
2316
2381
  verbose: options.verbose,
2317
2382
  delete: options.delete