rulesync 0.54.0 → 0.56.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.
Files changed (35) hide show
  1. package/README.ja.md +67 -5
  2. package/README.md +77 -6
  3. package/dist/{augmentcode-MJYD2Y4S.js → augmentcode-HIZIQG2W.js} +2 -2
  4. package/dist/chunk-7E4U4YAB.js +17 -0
  5. package/dist/{chunk-D7XQ4OHK.js → chunk-7UBF4OLN.js} +1 -1
  6. package/dist/{chunk-VI6SBYFB.js → chunk-AUUSMVCT.js} +2 -1
  7. package/dist/chunk-J3TBR5EP.js +292 -0
  8. package/dist/{chunk-OXKDEZJK.js → chunk-KUGTKMNW.js} +1 -1
  9. package/dist/{chunk-QVPD6ENS.js → chunk-LXTA7DBA.js} +1 -1
  10. package/dist/chunk-OA473EXZ.js +17 -0
  11. package/dist/{chunk-BEPSWIZC.js → chunk-PCATT4UZ.js} +1 -1
  12. package/dist/chunk-VKNCBVZF.js +17 -0
  13. package/dist/chunk-VNT6AHHO.js +17 -0
  14. package/dist/chunk-W2WU253H.js +17 -0
  15. package/dist/chunk-WAX2UANS.js +61 -0
  16. package/dist/{chunk-ORNO5MOO.js → chunk-YTU3SCQO.js} +1 -1
  17. package/dist/{claudecode-CKGUHLRR.js → claudecode-VVI2PTKI.js} +3 -3
  18. package/dist/{cline-Z5C656VR.js → cline-BJLFSLEB.js} +3 -3
  19. package/dist/{codexcli-VFUJKSIJ.js → codexcli-LKWQB3V3.js} +3 -3
  20. package/dist/{copilot-4WQS5TA7.js → copilot-MOR3HHJX.js} +2 -2
  21. package/dist/{cursor-HOB2F2V2.js → cursor-2BVUO64T.js} +3 -2
  22. package/dist/{geminicli-XTMQTIU2.js → geminicli-5YFMKRFL.js} +3 -2
  23. package/dist/index.cjs +898 -493
  24. package/dist/index.js +669 -337
  25. package/dist/{junie-AN6CR7DD.js → junie-5TDJPUXX.js} +3 -2
  26. package/dist/{kiro-PTUZOHQ2.js → kiro-YDHXY2MA.js} +2 -2
  27. package/dist/{roo-WOMS36KU.js → roo-L3QTTIPO.js} +2 -2
  28. package/dist/windsurf-PXDRIQ76.js +10 -0
  29. package/package.json +1 -1
  30. package/dist/chunk-3PHMFVXP.js +0 -66
  31. package/dist/chunk-OY6BYYIX.js +0 -63
  32. package/dist/chunk-PPAQWVXX.js +0 -94
  33. package/dist/chunk-TJKD6LEW.js +0 -90
  34. package/dist/chunk-UHANRG2O.js +0 -54
  35. package/dist/chunk-UZCJNUXO.js +0 -67
package/dist/index.js CHANGED
@@ -1,42 +1,45 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  generateJunieMcp
4
- } from "./chunk-TJKD6LEW.js";
4
+ } from "./chunk-VNT6AHHO.js";
5
5
  import {
6
6
  generateKiroMcp
7
- } from "./chunk-QVPD6ENS.js";
7
+ } from "./chunk-LXTA7DBA.js";
8
8
  import {
9
9
  generateRooMcp
10
- } from "./chunk-BEPSWIZC.js";
10
+ } from "./chunk-PCATT4UZ.js";
11
+ import {
12
+ generateWindsurfMcp
13
+ } from "./chunk-7E4U4YAB.js";
11
14
  import {
12
15
  generateAugmentcodeMcp
13
- } from "./chunk-ORNO5MOO.js";
16
+ } from "./chunk-YTU3SCQO.js";
14
17
  import {
15
18
  generateClaudeMcp
16
- } from "./chunk-OY6BYYIX.js";
19
+ } from "./chunk-VKNCBVZF.js";
17
20
  import {
18
21
  generateClineMcp
19
- } from "./chunk-UHANRG2O.js";
22
+ } from "./chunk-W2WU253H.js";
20
23
  import {
21
24
  generateCodexMcp
22
- } from "./chunk-D7XQ4OHK.js";
23
- import "./chunk-PPAQWVXX.js";
25
+ } from "./chunk-7UBF4OLN.js";
24
26
  import {
25
27
  generateCopilotMcp
26
- } from "./chunk-OXKDEZJK.js";
28
+ } from "./chunk-KUGTKMNW.js";
27
29
  import {
28
30
  generateCursorMcp
29
- } from "./chunk-3PHMFVXP.js";
31
+ } from "./chunk-OA473EXZ.js";
30
32
  import {
31
33
  generateGeminiCliMcp
32
- } from "./chunk-UZCJNUXO.js";
34
+ } from "./chunk-WAX2UANS.js";
35
+ import "./chunk-J3TBR5EP.js";
33
36
  import {
34
37
  ALL_TOOL_TARGETS,
35
38
  RulesyncTargetsSchema,
36
39
  ToolTargetSchema,
37
40
  ToolTargetsSchema,
38
41
  isToolTarget
39
- } from "./chunk-VI6SBYFB.js";
42
+ } from "./chunk-AUUSMVCT.js";
40
43
 
41
44
  // src/cli/index.ts
42
45
  import { Command } from "commander";
@@ -60,7 +63,8 @@ function getDefaultConfig() {
60
63
  roo: ".roo/rules",
61
64
  geminicli: ".gemini/memories",
62
65
  kiro: ".kiro/steering",
63
- junie: "."
66
+ junie: ".",
67
+ windsurf: "."
64
68
  },
65
69
  watchEnabled: false,
66
70
  defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy")
@@ -147,7 +151,8 @@ var OutputPathsSchema = z3.object({
147
151
  roo: z3.optional(z3.string()),
148
152
  geminicli: z3.optional(z3.string()),
149
153
  kiro: z3.optional(z3.string()),
150
- junie: z3.optional(z3.string())
154
+ junie: z3.optional(z3.string()),
155
+ windsurf: z3.optional(z3.string())
151
156
  });
152
157
  var ConfigOptionsSchema = z3.object({
153
158
  aiRulesDir: z3.optional(z3.string()),
@@ -227,6 +232,8 @@ var RuleFrontmatterSchema = z5.object({
227
232
  description: z5.string(),
228
233
  globs: z5.array(z5.string()),
229
234
  cursorRuleType: z5.optional(z5.enum(["always", "manual", "specificFiles", "intelligently"])),
235
+ windsurfActivationMode: z5.optional(z5.enum(["always", "manual", "model-decision", "glob"])),
236
+ windsurfOutputFormat: z5.optional(z5.enum(["single-file", "directory"])),
230
237
  tags: z5.optional(z5.array(z5.string()))
231
238
  });
232
239
  var ParsedRuleSchema = z5.object({
@@ -435,6 +442,36 @@ function mergeWithCliOptions(config, cliOptions) {
435
442
  return merged;
436
443
  }
437
444
 
445
+ // src/utils/error.ts
446
+ function getErrorMessage(error) {
447
+ return error instanceof Error ? error.message : String(error);
448
+ }
449
+ function formatErrorWithContext(error, context) {
450
+ const errorMessage = getErrorMessage(error);
451
+ return `${context}: ${errorMessage}`;
452
+ }
453
+ function createErrorResult(error, context) {
454
+ const errorMessage = context ? formatErrorWithContext(error, context) : getErrorMessage(error);
455
+ return {
456
+ success: false,
457
+ error: errorMessage
458
+ };
459
+ }
460
+ function createSuccessResult(result) {
461
+ return {
462
+ success: true,
463
+ result
464
+ };
465
+ }
466
+ async function safeAsyncOperation(operation, errorContext) {
467
+ try {
468
+ const result = await operation();
469
+ return createSuccessResult(result);
470
+ } catch (error) {
471
+ return createErrorResult(error, errorContext);
472
+ }
473
+ }
474
+
438
475
  // src/utils/file.ts
439
476
  import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
440
477
  import { dirname, join as join2 } from "path";
@@ -445,6 +482,9 @@ async function ensureDir(dirPath) {
445
482
  await mkdir2(dirPath, { recursive: true });
446
483
  }
447
484
  }
485
+ function resolvePath(relativePath, baseDir) {
486
+ return baseDir ? join2(baseDir, relativePath) : relativePath;
487
+ }
448
488
  async function readFileContent(filepath) {
449
489
  return readFile(filepath, "utf-8");
450
490
  }
@@ -735,7 +775,7 @@ export default config;
735
775
  }
736
776
 
737
777
  // src/cli/commands/generate.ts
738
- import { join as join13 } from "path";
778
+ import { join as join11 } from "path";
739
779
 
740
780
  // src/generators/ignore/shared-factory.ts
741
781
  import { join as join3 } from "path";
@@ -1295,6 +1335,131 @@ var ignoreConfigs = {
1295
1335
  ],
1296
1336
  includeCommonPatterns: false,
1297
1337
  projectPatternsHeader: "# \u2500\u2500\u2500\u2500\u2500 Project-specific patterns from rulesync rules \u2500\u2500\u2500\u2500\u2500"
1338
+ },
1339
+ windsurf: {
1340
+ tool: "windsurf",
1341
+ filename: ".codeiumignore",
1342
+ header: [
1343
+ "# Generated by rulesync - Windsurf AI Code Editor ignore file",
1344
+ "# This file controls which files are excluded from Cascade AI analysis and context",
1345
+ "# Uses same syntax as .gitignore patterns",
1346
+ "# Note: Git-ignored files are automatically excluded by Windsurf"
1347
+ ],
1348
+ corePatterns: [
1349
+ "# \u2500\u2500\u2500\u2500\u2500 Security & Credentials (Critical) \u2500\u2500\u2500\u2500\u2500",
1350
+ "# Environment files",
1351
+ ".env*",
1352
+ "!.env.example",
1353
+ "",
1354
+ "# Private keys and certificates",
1355
+ "*.pem",
1356
+ "*.key",
1357
+ "*.crt",
1358
+ "*.p12",
1359
+ "*.pfx",
1360
+ "*.der",
1361
+ "",
1362
+ "# SSH keys",
1363
+ "id_rsa*",
1364
+ "id_dsa*",
1365
+ "*.ppk",
1366
+ "",
1367
+ "# API keys and tokens",
1368
+ "**/apikeys/",
1369
+ "**/*_token*",
1370
+ "**/*_secret*",
1371
+ "**/*api_key*",
1372
+ "",
1373
+ "# Cloud provider credentials",
1374
+ "aws-credentials.json",
1375
+ "gcp-service-account*.json",
1376
+ "azure-credentials.json",
1377
+ "",
1378
+ "# \u2500\u2500\u2500\u2500\u2500 Database & Configuration Files \u2500\u2500\u2500\u2500\u2500",
1379
+ "# Database files",
1380
+ "*.db",
1381
+ "*.sqlite",
1382
+ "*.sqlite3",
1383
+ "",
1384
+ "# Configuration files with secrets",
1385
+ "config/secrets/",
1386
+ "**/database.yml",
1387
+ "",
1388
+ "# \u2500\u2500\u2500\u2500\u2500 Build Artifacts & Dependencies \u2500\u2500\u2500\u2500\u2500",
1389
+ "# Build outputs",
1390
+ "dist/",
1391
+ "build/",
1392
+ "out/",
1393
+ "target/",
1394
+ "",
1395
+ "# Dependencies (already auto-excluded but reinforced)",
1396
+ "node_modules/",
1397
+ ".pnpm-store/",
1398
+ ".yarn/",
1399
+ "vendor/",
1400
+ "",
1401
+ "# \u2500\u2500\u2500\u2500\u2500 Cache & Temporary Files \u2500\u2500\u2500\u2500\u2500",
1402
+ "# Cache directories",
1403
+ ".cache/",
1404
+ ".parcel-cache/",
1405
+ ".next/cache/",
1406
+ "",
1407
+ "# Temporary files",
1408
+ "*.tmp",
1409
+ "*.swp",
1410
+ "*.swo",
1411
+ "*~",
1412
+ "",
1413
+ "# \u2500\u2500\u2500\u2500\u2500 Large Data Files \u2500\u2500\u2500\u2500\u2500",
1414
+ "# Data files",
1415
+ "*.csv",
1416
+ "*.xlsx",
1417
+ "*.json",
1418
+ "data/",
1419
+ "datasets/",
1420
+ "",
1421
+ "# Media files",
1422
+ "*.mp4",
1423
+ "*.avi",
1424
+ "*.mov",
1425
+ "*.png",
1426
+ "*.jpg",
1427
+ "*.jpeg",
1428
+ "*.gif",
1429
+ "",
1430
+ "# Archives",
1431
+ "*.zip",
1432
+ "*.tar.gz",
1433
+ "*.rar",
1434
+ "",
1435
+ "# \u2500\u2500\u2500\u2500\u2500 IDE & Editor Files \u2500\u2500\u2500\u2500\u2500",
1436
+ "# IDE settings (personal)",
1437
+ ".vscode/settings.json",
1438
+ ".idea/",
1439
+ "",
1440
+ "# Editor temporary files",
1441
+ "*.swp",
1442
+ "*.swo",
1443
+ "",
1444
+ "# \u2500\u2500\u2500\u2500\u2500 Test Coverage & Logs \u2500\u2500\u2500\u2500\u2500",
1445
+ "# Test coverage reports",
1446
+ "coverage/",
1447
+ ".nyc_output/",
1448
+ "",
1449
+ "# Logs",
1450
+ "*.log",
1451
+ "",
1452
+ "# \u2500\u2500\u2500\u2500\u2500 Re-include Important Files \u2500\u2500\u2500\u2500\u2500",
1453
+ "# Allow configuration examples",
1454
+ "!.env.example",
1455
+ "!config/*.example.*",
1456
+ "",
1457
+ "# Allow documentation",
1458
+ "!docs/**/*.md",
1459
+ "!README.md"
1460
+ ],
1461
+ includeCommonPatterns: false,
1462
+ projectPatternsHeader: "# \u2500\u2500\u2500\u2500\u2500 Project-specific patterns from rulesync rules \u2500\u2500\u2500\u2500\u2500"
1298
1463
  }
1299
1464
  };
1300
1465
 
@@ -1313,6 +1478,11 @@ async function generateKiroIgnoreFiles(rules, config, baseDir) {
1313
1478
  return generateIgnoreFile(rules, config, ignoreConfigs.kiro, baseDir);
1314
1479
  }
1315
1480
 
1481
+ // src/generators/ignore/windsurf.ts
1482
+ function generateWindsurfIgnore(rules, config, baseDir) {
1483
+ return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
1484
+ }
1485
+
1316
1486
  // src/generators/rules/augmentcode.ts
1317
1487
  import { join as join6 } from "path";
1318
1488
 
@@ -1372,7 +1542,7 @@ function filterIgnoredFiles(files, ignorePatterns) {
1372
1542
 
1373
1543
  // src/generators/rules/shared-helpers.ts
1374
1544
  function resolveOutputDir(config, tool, baseDir) {
1375
- return baseDir ? join5(baseDir, config.outputPaths[tool]) : config.outputPaths[tool];
1545
+ return resolvePath(config.outputPaths[tool], baseDir);
1376
1546
  }
1377
1547
  function createOutputsArray() {
1378
1548
  return [];
@@ -1399,7 +1569,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
1399
1569
  }
1400
1570
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1401
1571
  if (ignorePatterns.patterns.length > 0) {
1402
- const ignorePath = baseDir ? join5(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
1572
+ const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
1403
1573
  const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1404
1574
  outputs.push({
1405
1575
  tool: generatorConfig.tool,
@@ -1417,7 +1587,10 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1417
1587
  if (generatorConfig.generateDetailContent && generatorConfig.detailSubDir) {
1418
1588
  for (const rule of detailRules) {
1419
1589
  const content = generatorConfig.generateDetailContent(rule);
1420
- const filepath = baseDir ? join5(baseDir, generatorConfig.detailSubDir, `${rule.filename}.md`) : join5(generatorConfig.detailSubDir, `${rule.filename}.md`);
1590
+ const filepath = resolvePath(
1591
+ join5(generatorConfig.detailSubDir, `${rule.filename}.md`),
1592
+ baseDir
1593
+ );
1421
1594
  outputs.push({
1422
1595
  tool: generatorConfig.tool,
1423
1596
  filepath,
@@ -1427,7 +1600,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1427
1600
  }
1428
1601
  if (generatorConfig.generateRootContent && generatorConfig.rootFilePath) {
1429
1602
  const rootContent = generatorConfig.generateRootContent(rootRule, detailRules, baseDir);
1430
- const rootFilepath = baseDir ? join5(baseDir, generatorConfig.rootFilePath) : generatorConfig.rootFilePath;
1603
+ const rootFilepath = resolvePath(generatorConfig.rootFilePath, baseDir);
1431
1604
  outputs.push({
1432
1605
  tool: generatorConfig.tool,
1433
1606
  filepath: rootFilepath,
@@ -1436,7 +1609,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1436
1609
  }
1437
1610
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1438
1611
  if (ignorePatterns.patterns.length > 0) {
1439
- const ignorePath = baseDir ? join5(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
1612
+ const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
1440
1613
  const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1441
1614
  outputs.push({
1442
1615
  tool: generatorConfig.tool,
@@ -1466,16 +1639,6 @@ function generateIgnoreFile2(patterns, tool) {
1466
1639
  lines.push(...patterns);
1467
1640
  return lines.join("\n");
1468
1641
  }
1469
- async function generateComplexRulesConfig(rules, config, generatorConfig, baseDir) {
1470
- const unifiedConfig = {
1471
- tool: generatorConfig.tool,
1472
- fileExtension: generatorConfig.fileExtension,
1473
- ignoreFileName: generatorConfig.ignoreFileName,
1474
- generateContent: generatorConfig.generateContent,
1475
- pathResolver: generatorConfig.getOutputPath
1476
- };
1477
- return generateRulesConfig(rules, config, unifiedConfig, baseDir);
1478
- }
1479
1642
 
1480
1643
  // src/generators/rules/augmentcode.ts
1481
1644
  async function generateAugmentcodeConfig(rules, config, baseDir) {
@@ -1541,32 +1704,24 @@ function generateLegacyGuidelinesFile(allRules) {
1541
1704
  // src/generators/rules/claudecode.ts
1542
1705
  import { join as join7 } from "path";
1543
1706
  async function generateClaudecodeConfig(rules, config, baseDir) {
1544
- const outputs = [];
1545
- const rootRules = rules.filter((r) => r.frontmatter.root === true);
1546
- const detailRules = rules.filter((r) => r.frontmatter.root === false);
1547
- const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
1548
- const claudeOutputDir = baseDir ? join7(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
1549
- outputs.push({
1707
+ const generatorConfig = {
1550
1708
  tool: "claudecode",
1551
- filepath: join7(claudeOutputDir, "CLAUDE.md"),
1552
- content: claudeMdContent
1553
- });
1554
- for (const rule of detailRules) {
1555
- const memoryContent = generateMemoryFile(rule);
1556
- outputs.push({
1557
- tool: "claudecode",
1558
- filepath: join7(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
1559
- content: memoryContent
1560
- });
1561
- }
1562
- const ignorePatterns = await loadIgnorePatterns(baseDir);
1563
- if (ignorePatterns.patterns.length > 0) {
1564
- const settingsPath = baseDir ? join7(baseDir, ".claude", "settings.json") : join7(".claude", "settings.json");
1565
- await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
1566
- }
1567
- return outputs;
1709
+ fileExtension: ".md",
1710
+ ignoreFileName: ".aiignore",
1711
+ generateContent: generateMemoryFile,
1712
+ generateRootContent: generateClaudeMarkdown,
1713
+ rootFilePath: "CLAUDE.md",
1714
+ generateDetailContent: generateMemoryFile,
1715
+ detailSubDir: ".claude/memories",
1716
+ updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
1717
+ const settingsPath = resolvePath(join7(".claude", "settings.json"), baseDir2);
1718
+ await updateClaudeSettings(settingsPath, ignorePatterns);
1719
+ return [];
1720
+ }
1721
+ };
1722
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
1568
1723
  }
1569
- function generateClaudeMarkdown(rootRules, detailRules) {
1724
+ function generateClaudeMarkdown(rootRule, detailRules) {
1570
1725
  const lines = [];
1571
1726
  if (detailRules.length > 0) {
1572
1727
  lines.push("Please also reference the following documents as needed:");
@@ -1580,11 +1735,9 @@ function generateClaudeMarkdown(rootRules, detailRules) {
1580
1735
  }
1581
1736
  lines.push("");
1582
1737
  }
1583
- if (rootRules.length > 0) {
1584
- for (const rule of rootRules) {
1585
- lines.push(rule.content);
1586
- lines.push("");
1587
- }
1738
+ if (rootRule) {
1739
+ lines.push(rootRule.content);
1740
+ lines.push("");
1588
1741
  }
1589
1742
  return lines.join("\n");
1590
1743
  }
@@ -1624,134 +1777,8 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
1624
1777
  console.log(`\u2705 Updated Claude Code settings: ${settingsPath}`);
1625
1778
  }
1626
1779
 
1627
- // src/generators/rules/cline.ts
1628
- async function generateClineConfig(rules, config, baseDir) {
1629
- return generateRulesConfig(
1630
- rules,
1631
- config,
1632
- {
1633
- tool: "cline",
1634
- fileExtension: ".md",
1635
- ignoreFileName: ".clineignore",
1636
- generateContent: (rule) => rule.content.trim()
1637
- },
1638
- baseDir
1639
- );
1640
- }
1641
-
1642
- // src/generators/rules/codexcli.ts
1643
- async function generateCodexConfig(rules, config, baseDir) {
1644
- const generatorConfig = {
1645
- tool: "codexcli",
1646
- fileExtension: ".md",
1647
- ignoreFileName: ".codexignore",
1648
- generateContent: generateCodexInstructionsMarkdown,
1649
- pathResolver: (rule, outputDir) => {
1650
- if (rule.frontmatter.root === true) {
1651
- return `${outputDir}/codex.md`;
1652
- }
1653
- return `${outputDir}/${rule.filename}.md`;
1654
- }
1655
- };
1656
- return generateRulesConfig(rules, config, generatorConfig, baseDir);
1657
- }
1658
- function generateCodexInstructionsMarkdown(rule) {
1659
- const lines = [];
1660
- if (rule.frontmatter.root === false && rule.frontmatter.description && rule.frontmatter.description !== "Main instructions" && !rule.frontmatter.description.includes("Project-level Codex CLI instructions")) {
1661
- lines.push(`<!-- ${rule.frontmatter.description} -->`);
1662
- if (rule.content.trim()) {
1663
- lines.push("");
1664
- }
1665
- }
1666
- const content = rule.content.trim();
1667
- if (content) {
1668
- lines.push(content);
1669
- }
1670
- return lines.join("\n");
1671
- }
1672
-
1673
- // src/generators/rules/copilot.ts
1780
+ // src/generators/rules/generator-registry.ts
1674
1781
  import { join as join8 } from "path";
1675
- async function generateCopilotConfig(rules, config, baseDir) {
1676
- return generateComplexRulesConfig(
1677
- rules,
1678
- config,
1679
- {
1680
- tool: "copilot",
1681
- fileExtension: ".instructions.md",
1682
- ignoreFileName: ".copilotignore",
1683
- generateContent: generateCopilotMarkdown,
1684
- getOutputPath: (rule, outputDir) => {
1685
- const baseFilename = rule.filename.replace(/\.md$/, "");
1686
- return join8(outputDir, `${baseFilename}.instructions.md`);
1687
- }
1688
- },
1689
- baseDir
1690
- );
1691
- }
1692
- function generateCopilotMarkdown(rule) {
1693
- const lines = [];
1694
- lines.push("---");
1695
- lines.push(`description: "${rule.frontmatter.description}"`);
1696
- if (rule.frontmatter.globs.length > 0) {
1697
- lines.push(`applyTo: "${rule.frontmatter.globs.join(", ")}"`);
1698
- } else {
1699
- lines.push('applyTo: "**"');
1700
- }
1701
- lines.push("---");
1702
- lines.push(rule.content);
1703
- return lines.join("\n");
1704
- }
1705
-
1706
- // src/generators/rules/cursor.ts
1707
- import { join as join9 } from "path";
1708
- async function generateCursorConfig(rules, config, baseDir) {
1709
- return generateComplexRulesConfig(
1710
- rules,
1711
- config,
1712
- {
1713
- tool: "cursor",
1714
- fileExtension: ".mdc",
1715
- ignoreFileName: ".cursorignore",
1716
- generateContent: generateCursorMarkdown,
1717
- getOutputPath: (rule, outputDir) => {
1718
- return join9(outputDir, `${rule.filename}.mdc`);
1719
- }
1720
- },
1721
- baseDir
1722
- );
1723
- }
1724
- function generateCursorMarkdown(rule) {
1725
- const lines = [];
1726
- const ruleType = determineCursorRuleType(rule.frontmatter);
1727
- lines.push("---");
1728
- switch (ruleType) {
1729
- case "always":
1730
- lines.push("description:");
1731
- lines.push("globs:");
1732
- lines.push("alwaysApply: true");
1733
- break;
1734
- case "manual":
1735
- lines.push("description:");
1736
- lines.push("globs:");
1737
- lines.push("alwaysApply: false");
1738
- break;
1739
- case "specificFiles":
1740
- lines.push("description:");
1741
- lines.push(`globs: ${rule.frontmatter.globs.join(",")}`);
1742
- lines.push("alwaysApply: false");
1743
- break;
1744
- case "intelligently":
1745
- lines.push(`description: ${rule.frontmatter.description}`);
1746
- lines.push("globs:");
1747
- lines.push("alwaysApply: false");
1748
- break;
1749
- }
1750
- lines.push("---");
1751
- lines.push("");
1752
- lines.push(rule.content);
1753
- return lines.join("\n");
1754
- }
1755
1782
  function determineCursorRuleType(frontmatter) {
1756
1783
  if (frontmatter.cursorRuleType) {
1757
1784
  return frontmatter.cursorRuleType;
@@ -1773,6 +1800,293 @@ function determineCursorRuleType(frontmatter) {
1773
1800
  }
1774
1801
  return "intelligently";
1775
1802
  }
1803
+ var GENERATOR_REGISTRY = {
1804
+ // Simple generators - generate one file per rule
1805
+ cline: {
1806
+ type: "simple",
1807
+ tool: "cline",
1808
+ fileExtension: ".md",
1809
+ ignoreFileName: ".clineignore",
1810
+ generateContent: (rule) => rule.content.trim()
1811
+ },
1812
+ roo: {
1813
+ type: "simple",
1814
+ tool: "roo",
1815
+ fileExtension: ".md",
1816
+ ignoreFileName: ".rooignore",
1817
+ generateContent: (rule) => rule.content.trim()
1818
+ },
1819
+ kiro: {
1820
+ type: "simple",
1821
+ tool: "kiro",
1822
+ fileExtension: ".md",
1823
+ ignoreFileName: ".kiroignore",
1824
+ generateContent: (rule) => rule.content.trim()
1825
+ },
1826
+ augmentcode: {
1827
+ type: "simple",
1828
+ tool: "augmentcode",
1829
+ fileExtension: ".md",
1830
+ ignoreFileName: ".aiignore",
1831
+ generateContent: (rule) => rule.content.trim()
1832
+ },
1833
+ "augmentcode-legacy": {
1834
+ type: "simple",
1835
+ tool: "augmentcode-legacy",
1836
+ fileExtension: ".md",
1837
+ ignoreFileName: ".aiignore",
1838
+ generateContent: (rule) => rule.content.trim()
1839
+ },
1840
+ // Complex generators with custom content formatting
1841
+ copilot: {
1842
+ type: "simple",
1843
+ tool: "copilot",
1844
+ fileExtension: ".instructions.md",
1845
+ ignoreFileName: ".copilotignore",
1846
+ generateContent: (rule) => {
1847
+ const lines = [];
1848
+ lines.push("---");
1849
+ lines.push(`description: "${rule.frontmatter.description}"`);
1850
+ if (rule.frontmatter.globs.length > 0) {
1851
+ lines.push(`applyTo: "${rule.frontmatter.globs.join(", ")}"`);
1852
+ } else {
1853
+ lines.push('applyTo: "**"');
1854
+ }
1855
+ lines.push("---");
1856
+ lines.push(rule.content);
1857
+ return lines.join("\n");
1858
+ },
1859
+ pathResolver: (rule, outputDir) => {
1860
+ const baseFilename = rule.filename.replace(/\.md$/, "");
1861
+ return join8(outputDir, `${baseFilename}.instructions.md`);
1862
+ }
1863
+ },
1864
+ cursor: {
1865
+ type: "simple",
1866
+ tool: "cursor",
1867
+ fileExtension: ".mdc",
1868
+ ignoreFileName: ".cursorignore",
1869
+ generateContent: (rule) => {
1870
+ const lines = [];
1871
+ const ruleType = determineCursorRuleType(rule.frontmatter);
1872
+ lines.push("---");
1873
+ switch (ruleType) {
1874
+ case "always":
1875
+ lines.push("description:");
1876
+ lines.push("globs:");
1877
+ lines.push("alwaysApply: true");
1878
+ break;
1879
+ case "manual":
1880
+ lines.push("description:");
1881
+ lines.push("globs:");
1882
+ lines.push("alwaysApply: false");
1883
+ break;
1884
+ case "specificFiles":
1885
+ lines.push("description:");
1886
+ lines.push(`globs: ${rule.frontmatter.globs.join(",")}`);
1887
+ lines.push("alwaysApply: false");
1888
+ break;
1889
+ case "intelligently":
1890
+ lines.push(`description: ${rule.frontmatter.description}`);
1891
+ lines.push("globs:");
1892
+ lines.push("alwaysApply: false");
1893
+ break;
1894
+ }
1895
+ lines.push("---");
1896
+ lines.push("");
1897
+ lines.push(rule.content);
1898
+ return lines.join("\n");
1899
+ },
1900
+ pathResolver: (rule, outputDir) => {
1901
+ return join8(outputDir, `${rule.filename}.mdc`);
1902
+ }
1903
+ },
1904
+ codexcli: {
1905
+ type: "simple",
1906
+ tool: "codexcli",
1907
+ fileExtension: ".md",
1908
+ ignoreFileName: ".codexignore",
1909
+ generateContent: (rule) => rule.content.trim()
1910
+ },
1911
+ windsurf: {
1912
+ type: "simple",
1913
+ tool: "windsurf",
1914
+ fileExtension: ".md",
1915
+ ignoreFileName: ".codeiumignore",
1916
+ generateContent: (rule) => {
1917
+ const lines = [];
1918
+ const activationMode = rule.frontmatter.windsurfActivationMode;
1919
+ const globPattern = rule.frontmatter.globs?.[0];
1920
+ if (activationMode || globPattern) {
1921
+ lines.push("---");
1922
+ if (activationMode) {
1923
+ lines.push(`activation: ${activationMode}`);
1924
+ }
1925
+ if (globPattern && activationMode === "glob") {
1926
+ lines.push(`pattern: "${globPattern}"`);
1927
+ }
1928
+ lines.push("---");
1929
+ lines.push("");
1930
+ }
1931
+ lines.push(rule.content.trim());
1932
+ return lines.join("\n");
1933
+ },
1934
+ pathResolver: (rule, outputDir) => {
1935
+ const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
1936
+ if (outputFormat === "single-file") {
1937
+ return join8(outputDir, ".windsurf-rules");
1938
+ } else {
1939
+ const rulesDir = join8(outputDir, ".windsurf", "rules");
1940
+ return join8(rulesDir, `${rule.filename}.md`);
1941
+ }
1942
+ }
1943
+ },
1944
+ // Complex generators with root + detail pattern
1945
+ claudecode: {
1946
+ type: "complex",
1947
+ tool: "claudecode",
1948
+ fileExtension: ".md",
1949
+ ignoreFileName: ".aiignore",
1950
+ generateContent: (rule) => {
1951
+ const lines = [];
1952
+ if (rule.frontmatter.description) {
1953
+ lines.push(`# ${rule.frontmatter.description}
1954
+ `);
1955
+ }
1956
+ lines.push(rule.content.trim());
1957
+ return lines.join("\n");
1958
+ }
1959
+ // NOTE: Claude Code specific logic is handled in the actual generator file
1960
+ // due to complex settings.json manipulation requirements
1961
+ },
1962
+ geminicli: {
1963
+ type: "complex",
1964
+ tool: "geminicli",
1965
+ fileExtension: ".md",
1966
+ ignoreFileName: ".aiexclude",
1967
+ generateContent: (rule) => {
1968
+ const lines = [];
1969
+ if (rule.frontmatter.description) {
1970
+ lines.push(`# ${rule.frontmatter.description}
1971
+ `);
1972
+ }
1973
+ lines.push(rule.content.trim());
1974
+ return lines.join("\n");
1975
+ }
1976
+ // Complex generation handled by existing generator
1977
+ },
1978
+ junie: {
1979
+ type: "complex",
1980
+ tool: "junie",
1981
+ fileExtension: ".md",
1982
+ ignoreFileName: ".aiignore",
1983
+ generateContent: (rule) => {
1984
+ const lines = [];
1985
+ if (rule.frontmatter.description) {
1986
+ lines.push(`# ${rule.frontmatter.description}
1987
+ `);
1988
+ }
1989
+ lines.push(rule.content.trim());
1990
+ return lines.join("\n");
1991
+ }
1992
+ // Complex generation handled by existing generator
1993
+ }
1994
+ };
1995
+ async function generateFromRegistry(tool, rules, config, baseDir) {
1996
+ const generatorConfig = GENERATOR_REGISTRY[tool];
1997
+ if (!generatorConfig) {
1998
+ throw new Error(`No generator configuration found for tool: ${tool}`);
1999
+ }
2000
+ if (generatorConfig.type === "simple") {
2001
+ const ruleConfig = {
2002
+ tool: generatorConfig.tool,
2003
+ fileExtension: generatorConfig.fileExtension,
2004
+ ignoreFileName: generatorConfig.ignoreFileName,
2005
+ generateContent: generatorConfig.generateContent,
2006
+ ...generatorConfig.pathResolver && { pathResolver: generatorConfig.pathResolver }
2007
+ };
2008
+ return generateRulesConfig(rules, config, ruleConfig, baseDir);
2009
+ } else {
2010
+ const enhancedConfig = {
2011
+ tool: generatorConfig.tool,
2012
+ fileExtension: generatorConfig.fileExtension,
2013
+ ignoreFileName: generatorConfig.ignoreFileName,
2014
+ generateContent: generatorConfig.generateContent,
2015
+ ...generatorConfig.generateRootContent && {
2016
+ generateRootContent: generatorConfig.generateRootContent
2017
+ },
2018
+ ...generatorConfig.rootFilePath && { rootFilePath: generatorConfig.rootFilePath },
2019
+ ...generatorConfig.generateDetailContent && {
2020
+ generateDetailContent: generatorConfig.generateDetailContent
2021
+ },
2022
+ ...generatorConfig.detailSubDir && { detailSubDir: generatorConfig.detailSubDir },
2023
+ ...generatorConfig.updateAdditionalConfig && {
2024
+ updateAdditionalConfig: generatorConfig.updateAdditionalConfig
2025
+ }
2026
+ };
2027
+ return generateComplexRules(rules, config, enhancedConfig, baseDir);
2028
+ }
2029
+ }
2030
+
2031
+ // src/generators/rules/cline.ts
2032
+ async function generateClineConfig(rules, config, baseDir) {
2033
+ return generateFromRegistry("cline", rules, config, baseDir);
2034
+ }
2035
+
2036
+ // src/generators/rules/codexcli.ts
2037
+ async function generateCodexConfig(rules, config, baseDir) {
2038
+ const outputs = [];
2039
+ if (rules.length === 0) {
2040
+ return outputs;
2041
+ }
2042
+ const sortedRules = [...rules].sort((a, b) => {
2043
+ if (a.frontmatter.root === true && b.frontmatter.root !== true) return -1;
2044
+ if (a.frontmatter.root !== true && b.frontmatter.root === true) return 1;
2045
+ return 0;
2046
+ });
2047
+ const concatenatedContent = generateConcatenatedCodexContent(sortedRules);
2048
+ if (concatenatedContent.trim()) {
2049
+ const outputDir = resolveOutputDir(config, "codexcli", baseDir);
2050
+ const filepath = `${outputDir}/codex.md`;
2051
+ outputs.push({
2052
+ tool: "codexcli",
2053
+ filepath,
2054
+ content: concatenatedContent
2055
+ });
2056
+ }
2057
+ const ignorePatterns = await loadIgnorePatterns(baseDir);
2058
+ if (ignorePatterns.patterns.length > 0) {
2059
+ const ignorePath = resolvePath(".codexignore", baseDir);
2060
+ const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
2061
+ outputs.push({
2062
+ tool: "codexcli",
2063
+ filepath: ignorePath,
2064
+ content: ignoreContent
2065
+ });
2066
+ }
2067
+ return outputs;
2068
+ }
2069
+ function generateConcatenatedCodexContent(rules) {
2070
+ const sections = [];
2071
+ for (const rule of rules) {
2072
+ const content = rule.content.trim();
2073
+ if (!content) {
2074
+ continue;
2075
+ }
2076
+ sections.push(content);
2077
+ }
2078
+ return sections.join("\n\n---\n\n");
2079
+ }
2080
+
2081
+ // src/generators/rules/copilot.ts
2082
+ async function generateCopilotConfig(rules, config, baseDir) {
2083
+ return generateFromRegistry("copilot", rules, config, baseDir);
2084
+ }
2085
+
2086
+ // src/generators/rules/cursor.ts
2087
+ async function generateCursorConfig(rules, config, baseDir) {
2088
+ return generateFromRegistry("cursor", rules, config, baseDir);
2089
+ }
1776
2090
 
1777
2091
  // src/generators/rules/geminicli.ts
1778
2092
  async function generateGeminiConfig(rules, config, baseDir) {
@@ -1844,38 +2158,18 @@ function generateGuidelinesMarkdown(rootRule, detailRules) {
1844
2158
  }
1845
2159
 
1846
2160
  // src/generators/rules/kiro.ts
1847
- import { join as join10 } from "path";
1848
2161
  async function generateKiroConfig(rules, config, baseDir) {
1849
- const outputs = [];
1850
- for (const rule of rules) {
1851
- const content = generateKiroMarkdown(rule);
1852
- const outputDir = baseDir ? join10(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
1853
- const filepath = join10(outputDir, `${rule.filename}.md`);
1854
- outputs.push({
1855
- tool: "kiro",
1856
- filepath,
1857
- content
1858
- });
1859
- }
1860
- return outputs;
1861
- }
1862
- function generateKiroMarkdown(rule) {
1863
- return rule.content.trim();
2162
+ return generateFromRegistry("kiro", rules, config, baseDir);
1864
2163
  }
1865
2164
 
1866
2165
  // src/generators/rules/roo.ts
1867
2166
  async function generateRooConfig(rules, config, baseDir) {
1868
- return generateRulesConfig(
1869
- rules,
1870
- config,
1871
- {
1872
- tool: "roo",
1873
- fileExtension: ".md",
1874
- ignoreFileName: ".rooignore",
1875
- generateContent: (rule) => rule.content.trim()
1876
- },
1877
- baseDir
1878
- );
2167
+ return generateFromRegistry("roo", rules, config, baseDir);
2168
+ }
2169
+
2170
+ // src/generators/rules/windsurf.ts
2171
+ async function generateWindsurfConfig(rules, config, baseDir) {
2172
+ return generateFromRegistry("windsurf", rules, config, baseDir);
1879
2173
  }
1880
2174
 
1881
2175
  // src/core/generator.ts
@@ -1950,6 +2244,11 @@ async function generateForTool(tool, rules, config, baseDir) {
1950
2244
  const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
1951
2245
  return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
1952
2246
  }
2247
+ case "windsurf": {
2248
+ const windsurfRulesOutputs = await generateWindsurfConfig(rules, config, baseDir);
2249
+ const windsurfIgnoreOutputs = await generateWindsurfIgnore(rules, config, baseDir);
2250
+ return [...windsurfRulesOutputs, ...windsurfIgnoreOutputs];
2251
+ }
1953
2252
  default:
1954
2253
  console.warn(`Unknown tool: ${tool}`);
1955
2254
  return null;
@@ -2152,6 +2451,11 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
2152
2451
  tool: "roo-project",
2153
2452
  path: path4.join(targetRoot, ".roo", "mcp.json"),
2154
2453
  generate: () => generateRooMcp(config)
2454
+ },
2455
+ {
2456
+ tool: "windsurf-project",
2457
+ path: path4.join(targetRoot, "mcp_config.json"),
2458
+ generate: () => generateWindsurfMcp(config)
2155
2459
  }
2156
2460
  ];
2157
2461
  const filteredGenerators = targetTools ? generators.filter((g) => {
@@ -2168,7 +2472,7 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
2168
2472
  try {
2169
2473
  const content = generator.generate();
2170
2474
  const parsed = JSON.parse(content);
2171
- if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("codexcli") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("junie") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
2475
+ if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("codexcli") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("junie") || generator.tool.includes("kiro") || generator.tool.includes("roo") || generator.tool.includes("windsurf")) {
2172
2476
  if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
2173
2477
  results.push({
2174
2478
  tool: generator.tool,
@@ -2280,12 +2584,12 @@ async function generateCommand(options = {}) {
2280
2584
  for (const tool of targetTools) {
2281
2585
  switch (tool) {
2282
2586
  case "augmentcode":
2283
- deleteTasks.push(removeDirectory(join13(".augment", "rules")));
2284
- deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
2587
+ deleteTasks.push(removeDirectory(join11(".augment", "rules")));
2588
+ deleteTasks.push(removeDirectory(join11(".augment", "ignore")));
2285
2589
  break;
2286
2590
  case "augmentcode-legacy":
2287
2591
  deleteTasks.push(removeClaudeGeneratedFiles());
2288
- deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
2592
+ deleteTasks.push(removeDirectory(join11(".augment", "ignore")));
2289
2593
  break;
2290
2594
  case "copilot":
2291
2595
  deleteTasks.push(removeDirectory(config.outputPaths.copilot));
@@ -2308,6 +2612,9 @@ async function generateCommand(options = {}) {
2308
2612
  case "kiro":
2309
2613
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
2310
2614
  break;
2615
+ case "windsurf":
2616
+ deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
2617
+ break;
2311
2618
  }
2312
2619
  }
2313
2620
  await Promise.all(deleteTasks);
@@ -2380,9 +2687,9 @@ Generating configurations for base directory: ${baseDir}`);
2380
2687
 
2381
2688
  // src/cli/commands/gitignore.ts
2382
2689
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
2383
- import { join as join14 } from "path";
2690
+ import { join as join12 } from "path";
2384
2691
  var gitignoreCommand = async () => {
2385
- const gitignorePath = join14(process.cwd(), ".gitignore");
2692
+ const gitignorePath = join12(process.cwd(), ".gitignore");
2386
2693
  const rulesFilesToIgnore = [
2387
2694
  "# Generated by rulesync - AI tool configuration files",
2388
2695
  "**/.github/copilot-instructions.md",
@@ -2446,11 +2753,11 @@ ${linesToAdd.join("\n")}
2446
2753
  };
2447
2754
 
2448
2755
  // src/core/importer.ts
2449
- import { join as join21 } from "path";
2450
- import matter5 from "gray-matter";
2756
+ import { join as join19 } from "path";
2757
+ import matter6 from "gray-matter";
2451
2758
 
2452
2759
  // src/parsers/augmentcode.ts
2453
- import { basename as basename2, join as join15 } from "path";
2760
+ import { basename as basename2, join as join13 } from "path";
2454
2761
  import matter2 from "gray-matter";
2455
2762
 
2456
2763
  // src/utils/parser-helpers.ts
@@ -2472,36 +2779,63 @@ function addRules(result, rules) {
2472
2779
  }
2473
2780
  result.rules.push(...rules);
2474
2781
  }
2475
- function handleParseError(error, context) {
2476
- const errorMessage = error instanceof Error ? error.message : String(error);
2477
- return `${context}: ${errorMessage}`;
2478
- }
2479
2782
  async function safeReadFile(operation, errorContext) {
2480
2783
  try {
2481
2784
  const result = await operation();
2482
- return { success: true, result };
2785
+ return createSuccessResult(result);
2483
2786
  } catch (error) {
2484
- return {
2485
- success: false,
2486
- error: handleParseError(error, errorContext)
2487
- };
2787
+ return createErrorResult(error, errorContext);
2488
2788
  }
2489
2789
  }
2490
2790
 
2491
2791
  // src/parsers/augmentcode.ts
2492
2792
  async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
2793
+ return parseUnifiedAugmentcode(baseDir, {
2794
+ rulesDir: ".augment/rules",
2795
+ targetName: "augmentcode",
2796
+ filenamePrefix: "augmentcode"
2797
+ });
2798
+ }
2799
+ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
2800
+ return parseUnifiedAugmentcode(baseDir, {
2801
+ legacyFilePath: ".augment-guidelines",
2802
+ targetName: "augmentcode-legacy",
2803
+ filenamePrefix: "augmentcode-legacy"
2804
+ });
2805
+ }
2806
+ async function parseUnifiedAugmentcode(baseDir, config) {
2493
2807
  const result = createParseResult();
2494
- const rulesDir = join15(baseDir, ".augment", "rules");
2495
- if (await fileExists(rulesDir)) {
2496
- const rulesResult = await parseAugmentRules(rulesDir);
2497
- addRules(result, rulesResult.rules);
2498
- result.errors.push(...rulesResult.errors);
2499
- } else {
2500
- addError(result, "No AugmentCode configuration found. Expected .augment/rules/ directory.");
2808
+ if (config.rulesDir) {
2809
+ const rulesDir = join13(baseDir, config.rulesDir);
2810
+ if (await fileExists(rulesDir)) {
2811
+ const rulesResult = await parseAugmentRules(rulesDir, config);
2812
+ addRules(result, rulesResult.rules);
2813
+ result.errors.push(...rulesResult.errors);
2814
+ } else {
2815
+ addError(
2816
+ result,
2817
+ `No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
2818
+ );
2819
+ }
2820
+ }
2821
+ if (config.legacyFilePath) {
2822
+ const legacyPath = join13(baseDir, config.legacyFilePath);
2823
+ if (await fileExists(legacyPath)) {
2824
+ const legacyResult = await parseAugmentGuidelines(legacyPath, config);
2825
+ if (legacyResult.rule) {
2826
+ addRule(result, legacyResult.rule);
2827
+ }
2828
+ result.errors.push(...legacyResult.errors);
2829
+ } else {
2830
+ addError(
2831
+ result,
2832
+ `No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
2833
+ );
2834
+ }
2501
2835
  }
2502
2836
  return { rules: result.rules || [], errors: result.errors };
2503
2837
  }
2504
- async function parseAugmentRules(rulesDir) {
2838
+ async function parseAugmentRules(rulesDir, config) {
2505
2839
  const rules = [];
2506
2840
  const errors = [];
2507
2841
  try {
@@ -2509,7 +2843,7 @@ async function parseAugmentRules(rulesDir) {
2509
2843
  const files = await readdir2(rulesDir);
2510
2844
  for (const file of files) {
2511
2845
  if (file.endsWith(".md") || file.endsWith(".mdc")) {
2512
- const filePath = join15(rulesDir, file);
2846
+ const filePath = join13(rulesDir, file);
2513
2847
  try {
2514
2848
  const rawContent = await readFileContent(filePath);
2515
2849
  const parsed = matter2(rawContent);
@@ -2521,7 +2855,7 @@ async function parseAugmentRules(rulesDir) {
2521
2855
  const filename = basename2(file, file.endsWith(".mdc") ? ".mdc" : ".md");
2522
2856
  const frontmatter = {
2523
2857
  root: isRoot,
2524
- targets: ["augmentcode"],
2858
+ targets: [config.targetName],
2525
2859
  description,
2526
2860
  globs: ["**/*"],
2527
2861
  // AugmentCode doesn't use specific globs in the same way
@@ -2530,7 +2864,7 @@ async function parseAugmentRules(rulesDir) {
2530
2864
  rules.push({
2531
2865
  frontmatter,
2532
2866
  content: parsed.content.trim(),
2533
- filename: `augmentcode-${ruleType}-${filename}`,
2867
+ filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
2534
2868
  filepath: filePath
2535
2869
  });
2536
2870
  } catch (error) {
@@ -2541,50 +2875,33 @@ async function parseAugmentRules(rulesDir) {
2541
2875
  }
2542
2876
  } catch (error) {
2543
2877
  const errorMessage = error instanceof Error ? error.message : String(error);
2544
- errors.push(`Failed to read .augment/rules/ directory: ${errorMessage}`);
2878
+ errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
2545
2879
  }
2546
2880
  return { rules, errors };
2547
2881
  }
2548
-
2549
- // src/parsers/augmentcode-legacy.ts
2550
- import { join as join16 } from "path";
2551
- async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
2552
- const result = createParseResult();
2553
- const guidelinesPath = join16(baseDir, ".augment-guidelines");
2554
- if (await fileExists(guidelinesPath)) {
2555
- const guidelinesResult = await parseAugmentGuidelines(guidelinesPath);
2556
- if (guidelinesResult.rule) {
2557
- addRule(result, guidelinesResult.rule);
2558
- }
2559
- result.errors.push(...guidelinesResult.errors);
2560
- } else {
2561
- addError(
2562
- result,
2563
- "No AugmentCode legacy configuration found. Expected .augment-guidelines file."
2564
- );
2565
- }
2566
- return { rules: result.rules || [], errors: result.errors };
2567
- }
2568
- async function parseAugmentGuidelines(guidelinesPath) {
2569
- const parseResult = await safeReadFile(async () => {
2570
- const content = await readFileContent(guidelinesPath);
2571
- if (content.trim()) {
2572
- const frontmatter = {
2573
- root: true,
2574
- // Legacy guidelines become root rules
2575
- targets: ["augmentcode-legacy"],
2576
- description: "Legacy AugmentCode guidelines",
2577
- globs: ["**/*"]
2578
- };
2579
- return {
2580
- frontmatter,
2581
- content: content.trim(),
2582
- filename: "augmentcode-legacy-guidelines",
2583
- filepath: guidelinesPath
2584
- };
2585
- }
2586
- return null;
2587
- }, "Failed to parse .augment-guidelines");
2882
+ async function parseAugmentGuidelines(guidelinesPath, config) {
2883
+ const parseResult = await safeReadFile(
2884
+ async () => {
2885
+ const content = await readFileContent(guidelinesPath);
2886
+ if (content.trim()) {
2887
+ const frontmatter = {
2888
+ root: true,
2889
+ // Legacy guidelines become root rules
2890
+ targets: [config.targetName],
2891
+ description: "Legacy AugmentCode guidelines",
2892
+ globs: ["**/*"]
2893
+ };
2894
+ return {
2895
+ frontmatter,
2896
+ content: content.trim(),
2897
+ filename: `${config.filenamePrefix}-guidelines`,
2898
+ filepath: guidelinesPath
2899
+ };
2900
+ }
2901
+ return null;
2902
+ },
2903
+ `Failed to parse ${config.legacyFilePath || guidelinesPath}`
2904
+ );
2588
2905
  if (parseResult.success) {
2589
2906
  return { rule: parseResult.result || null, errors: [] };
2590
2907
  } else {
@@ -2593,33 +2910,36 @@ async function parseAugmentGuidelines(guidelinesPath) {
2593
2910
  }
2594
2911
 
2595
2912
  // src/parsers/shared-helpers.ts
2596
- import { basename as basename3, join as join17 } from "path";
2913
+ import { basename as basename3, join as join14 } from "path";
2597
2914
  import matter3 from "gray-matter";
2598
2915
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2599
2916
  const errors = [];
2600
2917
  const rules = [];
2601
2918
  if (config.mainFile) {
2602
- const mainFilePath = join17(baseDir, config.mainFile.path);
2919
+ const mainFile = config.mainFile;
2920
+ const mainFilePath = resolvePath(mainFile.path, baseDir);
2603
2921
  if (await fileExists(mainFilePath)) {
2604
- try {
2922
+ const result = await safeAsyncOperation(async () => {
2605
2923
  const rawContent = await readFileContent(mainFilePath);
2606
2924
  let content;
2607
2925
  let frontmatter;
2608
- if (config.mainFile.useFrontmatter) {
2926
+ if (mainFile.useFrontmatter) {
2609
2927
  const parsed = matter3(rawContent);
2610
2928
  content = parsed.content.trim();
2929
+ const parsedFrontmatter = parsed.data;
2611
2930
  frontmatter = {
2612
- root: false,
2931
+ root: mainFile.isRoot ?? false,
2613
2932
  targets: [config.tool],
2614
- description: config.mainFile.description,
2615
- globs: ["**/*"]
2933
+ description: parsedFrontmatter.description || mainFile.description,
2934
+ globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
2935
+ ...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
2616
2936
  };
2617
2937
  } else {
2618
2938
  content = rawContent.trim();
2619
2939
  frontmatter = {
2620
- root: false,
2940
+ root: mainFile.isRoot ?? false,
2621
2941
  targets: [config.tool],
2622
- description: config.mainFile.description,
2942
+ description: mainFile.description,
2623
2943
  globs: ["**/*"]
2624
2944
  };
2625
2945
  }
@@ -2627,43 +2947,52 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2627
2947
  rules.push({
2628
2948
  frontmatter,
2629
2949
  content,
2630
- filename: "instructions",
2950
+ filename: mainFile.filenameOverride || "instructions",
2631
2951
  filepath: mainFilePath
2632
2952
  });
2633
2953
  }
2634
- } catch (error) {
2635
- const errorMessage = error instanceof Error ? error.message : String(error);
2636
- errors.push(`Failed to parse ${config.mainFile.path}: ${errorMessage}`);
2954
+ }, `Failed to parse ${mainFile.path}`);
2955
+ if (!result.success) {
2956
+ errors.push(result.error);
2637
2957
  }
2638
2958
  }
2639
2959
  }
2640
2960
  if (config.directories) {
2641
2961
  for (const dirConfig of config.directories) {
2642
- const dirPath = join17(baseDir, dirConfig.directory);
2962
+ const dirPath = resolvePath(dirConfig.directory, baseDir);
2643
2963
  if (await fileExists(dirPath)) {
2644
- try {
2964
+ const result = await safeAsyncOperation(async () => {
2645
2965
  const { readdir: readdir2 } = await import("fs/promises");
2646
2966
  const files = await readdir2(dirPath);
2647
2967
  for (const file of files) {
2648
2968
  if (file.endsWith(dirConfig.filePattern)) {
2649
- const filePath = join17(dirPath, file);
2650
- try {
2969
+ const filePath = join14(dirPath, file);
2970
+ const fileResult = await safeAsyncOperation(async () => {
2651
2971
  const rawContent = await readFileContent(filePath);
2652
2972
  let content;
2973
+ let frontmatter;
2974
+ const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
2653
2975
  if (dirConfig.filePattern === ".instructions.md") {
2654
2976
  const parsed = matter3(rawContent);
2655
2977
  content = parsed.content.trim();
2978
+ const parsedFrontmatter = parsed.data;
2979
+ frontmatter = {
2980
+ root: false,
2981
+ targets: [config.tool],
2982
+ description: parsedFrontmatter.description || `${dirConfig.description}: ${filename}`,
2983
+ globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
2984
+ ...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
2985
+ };
2656
2986
  } else {
2657
2987
  content = rawContent.trim();
2658
- }
2659
- if (content) {
2660
- const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
2661
- const frontmatter = {
2988
+ frontmatter = {
2662
2989
  root: false,
2663
2990
  targets: [config.tool],
2664
2991
  description: `${dirConfig.description}: ${filename}`,
2665
2992
  globs: ["**/*"]
2666
2993
  };
2994
+ }
2995
+ if (content) {
2667
2996
  rules.push({
2668
2997
  frontmatter,
2669
2998
  content,
@@ -2671,15 +3000,15 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2671
3000
  filepath: filePath
2672
3001
  });
2673
3002
  }
2674
- } catch (error) {
2675
- const errorMessage = error instanceof Error ? error.message : String(error);
2676
- errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
3003
+ }, `Failed to parse ${filePath}`);
3004
+ if (!fileResult.success) {
3005
+ errors.push(fileResult.error);
2677
3006
  }
2678
3007
  }
2679
3008
  }
2680
- } catch (error) {
2681
- const errorMessage = error instanceof Error ? error.message : String(error);
2682
- errors.push(`Failed to parse ${dirConfig.directory} files: ${errorMessage}`);
3009
+ }, `Failed to parse ${dirConfig.directory} files`);
3010
+ if (!result.success) {
3011
+ errors.push(result.error);
2683
3012
  }
2684
3013
  }
2685
3014
  }
@@ -2694,7 +3023,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
2694
3023
  const rules = [];
2695
3024
  let ignorePatterns;
2696
3025
  let mcpServers;
2697
- const mainFilePath = join17(baseDir, config.mainFileName);
3026
+ const mainFilePath = resolvePath(config.mainFileName, baseDir);
2698
3027
  if (!await fileExists(mainFilePath)) {
2699
3028
  errors.push(`${config.mainFileName} file not found`);
2700
3029
  return { rules, errors };
@@ -2705,12 +3034,12 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
2705
3034
  if (mainRule) {
2706
3035
  rules.push(mainRule);
2707
3036
  }
2708
- const memoryDir = join17(baseDir, config.memoryDirPath);
3037
+ const memoryDir = resolvePath(config.memoryDirPath, baseDir);
2709
3038
  if (await fileExists(memoryDir)) {
2710
3039
  const memoryRules = await parseMemoryFiles(memoryDir, config);
2711
3040
  rules.push(...memoryRules);
2712
3041
  }
2713
- const settingsPath = join17(baseDir, config.settingsPath);
3042
+ const settingsPath = resolvePath(config.settingsPath, baseDir);
2714
3043
  if (await fileExists(settingsPath)) {
2715
3044
  const settingsResult = await parseSettingsFile(settingsPath, config.tool);
2716
3045
  if (settingsResult.ignorePatterns) {
@@ -2722,7 +3051,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
2722
3051
  errors.push(...settingsResult.errors);
2723
3052
  }
2724
3053
  if (config.additionalIgnoreFile) {
2725
- const additionalIgnorePath = join17(baseDir, config.additionalIgnoreFile.path);
3054
+ const additionalIgnorePath = resolvePath(config.additionalIgnoreFile.path, baseDir);
2726
3055
  if (await fileExists(additionalIgnorePath)) {
2727
3056
  const additionalPatterns = await config.additionalIgnoreFile.parser(additionalIgnorePath);
2728
3057
  if (additionalPatterns.length > 0) {
@@ -2731,8 +3060,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
2731
3060
  }
2732
3061
  }
2733
3062
  } catch (error) {
2734
- const errorMessage = error instanceof Error ? error.message : String(error);
2735
- errors.push(`Failed to parse ${config.tool} configuration: ${errorMessage}`);
3063
+ errors.push(`Failed to parse ${config.tool} configuration: ${getErrorMessage(error)}`);
2736
3064
  }
2737
3065
  return {
2738
3066
  rules,
@@ -2776,7 +3104,7 @@ async function parseMemoryFiles(memoryDir, config) {
2776
3104
  const files = await readdir2(memoryDir);
2777
3105
  for (const file of files) {
2778
3106
  if (file.endsWith(".md")) {
2779
- const filePath = join17(memoryDir, file);
3107
+ const filePath = join14(memoryDir, file);
2780
3108
  const content = await readFileContent(filePath);
2781
3109
  if (content.trim()) {
2782
3110
  const filename = basename3(file, ".md");
@@ -2828,8 +3156,7 @@ async function parseSettingsFile(settingsPath, tool) {
2828
3156
  mcpServers = parseResult.data.mcpServers;
2829
3157
  }
2830
3158
  } catch (error) {
2831
- const errorMessage = error instanceof Error ? error.message : String(error);
2832
- errors.push(`Failed to parse settings.json: ${errorMessage}`);
3159
+ errors.push(`Failed to parse settings.json: ${getErrorMessage(error)}`);
2833
3160
  }
2834
3161
  return {
2835
3162
  errors,
@@ -2872,7 +3199,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
2872
3199
  }
2873
3200
 
2874
3201
  // src/parsers/codexcli.ts
2875
- import { join as join18 } from "path";
3202
+ import { join as join15 } from "path";
2876
3203
 
2877
3204
  // src/parsers/copilot.ts
2878
3205
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
@@ -2895,7 +3222,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
2895
3222
  }
2896
3223
 
2897
3224
  // src/parsers/cursor.ts
2898
- import { basename as basename4, join as join19 } from "path";
3225
+ import { basename as basename4, join as join16 } from "path";
2899
3226
  import matter4 from "gray-matter";
2900
3227
  import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
2901
3228
  import { z as z6 } from "zod/mini";
@@ -3020,7 +3347,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3020
3347
  const rules = [];
3021
3348
  let ignorePatterns;
3022
3349
  let mcpServers;
3023
- const cursorFilePath = join19(baseDir, ".cursorrules");
3350
+ const cursorFilePath = join16(baseDir, ".cursorrules");
3024
3351
  if (await fileExists(cursorFilePath)) {
3025
3352
  try {
3026
3353
  const rawContent = await readFileContent(cursorFilePath);
@@ -3041,14 +3368,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3041
3368
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
3042
3369
  }
3043
3370
  }
3044
- const cursorRulesDir = join19(baseDir, ".cursor", "rules");
3371
+ const cursorRulesDir = join16(baseDir, ".cursor", "rules");
3045
3372
  if (await fileExists(cursorRulesDir)) {
3046
3373
  try {
3047
3374
  const { readdir: readdir2 } = await import("fs/promises");
3048
3375
  const files = await readdir2(cursorRulesDir);
3049
3376
  for (const file of files) {
3050
3377
  if (file.endsWith(".mdc")) {
3051
- const filePath = join19(cursorRulesDir, file);
3378
+ const filePath = join16(cursorRulesDir, file);
3052
3379
  try {
3053
3380
  const rawContent = await readFileContent(filePath);
3054
3381
  const parsed = matter4(rawContent, customMatterOptions);
@@ -3077,7 +3404,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3077
3404
  if (rules.length === 0) {
3078
3405
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
3079
3406
  }
3080
- const cursorIgnorePath = join19(baseDir, ".cursorignore");
3407
+ const cursorIgnorePath = join16(baseDir, ".cursorignore");
3081
3408
  if (await fileExists(cursorIgnorePath)) {
3082
3409
  try {
3083
3410
  const content = await readFileContent(cursorIgnorePath);
@@ -3090,7 +3417,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3090
3417
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
3091
3418
  }
3092
3419
  }
3093
- const cursorMcpPath = join19(baseDir, ".cursor", "mcp.json");
3420
+ const cursorMcpPath = join16(baseDir, ".cursor", "mcp.json");
3094
3421
  if (await fileExists(cursorMcpPath)) {
3095
3422
  try {
3096
3423
  const content = await readFileContent(cursorMcpPath);
@@ -3139,11 +3466,11 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
3139
3466
  }
3140
3467
 
3141
3468
  // src/parsers/junie.ts
3142
- import { join as join20 } from "path";
3469
+ import { join as join17 } from "path";
3143
3470
  async function parseJunieConfiguration(baseDir = process.cwd()) {
3144
3471
  const errors = [];
3145
3472
  const rules = [];
3146
- const guidelinesPath = join20(baseDir, ".junie", "guidelines.md");
3473
+ const guidelinesPath = join17(baseDir, ".junie", "guidelines.md");
3147
3474
  if (!await fileExists(guidelinesPath)) {
3148
3475
  errors.push(".junie/guidelines.md file not found");
3149
3476
  return { rules, errors };
@@ -3194,6 +3521,11 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
3194
3521
  });
3195
3522
  }
3196
3523
 
3524
+ // src/parsers/windsurf.ts
3525
+ import { readFile as readFile2 } from "fs/promises";
3526
+ import { join as join18 } from "path";
3527
+ import matter5 from "gray-matter";
3528
+
3197
3529
  // src/core/importer.ts
3198
3530
  async function importConfiguration(options) {
3199
3531
  const { tool, baseDir = process.cwd(), rulesDir = ".rulesync", verbose = false } = options;
@@ -3278,7 +3610,7 @@ async function importConfiguration(options) {
3278
3610
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
3279
3611
  return { success: false, rulesCreated: 0, errors };
3280
3612
  }
3281
- const rulesDirPath = join21(baseDir, rulesDir);
3613
+ const rulesDirPath = join19(baseDir, rulesDir);
3282
3614
  try {
3283
3615
  const { mkdir: mkdir3 } = await import("fs/promises");
3284
3616
  await mkdir3(rulesDirPath, { recursive: true });
@@ -3292,7 +3624,7 @@ async function importConfiguration(options) {
3292
3624
  try {
3293
3625
  const baseFilename = rule.filename;
3294
3626
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
3295
- const filePath = join21(rulesDirPath, `${filename}.md`);
3627
+ const filePath = join19(rulesDirPath, `${filename}.md`);
3296
3628
  const content = generateRuleFileContent(rule);
3297
3629
  await writeFileContent(filePath, content);
3298
3630
  rulesCreated++;
@@ -3307,7 +3639,7 @@ async function importConfiguration(options) {
3307
3639
  let ignoreFileCreated = false;
3308
3640
  if (ignorePatterns && ignorePatterns.length > 0) {
3309
3641
  try {
3310
- const rulesyncignorePath = join21(baseDir, ".rulesyncignore");
3642
+ const rulesyncignorePath = join19(baseDir, ".rulesyncignore");
3311
3643
  const ignoreContent = `${ignorePatterns.join("\n")}
3312
3644
  `;
3313
3645
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -3323,7 +3655,7 @@ async function importConfiguration(options) {
3323
3655
  let mcpFileCreated = false;
3324
3656
  if (mcpServers && Object.keys(mcpServers).length > 0) {
3325
3657
  try {
3326
- const mcpPath = join21(baseDir, rulesDir, ".mcp.json");
3658
+ const mcpPath = join19(baseDir, rulesDir, ".mcp.json");
3327
3659
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
3328
3660
  `;
3329
3661
  await writeFileContent(mcpPath, mcpContent);
@@ -3345,13 +3677,13 @@ async function importConfiguration(options) {
3345
3677
  };
3346
3678
  }
3347
3679
  function generateRuleFileContent(rule) {
3348
- const frontmatter = matter5.stringify("", rule.frontmatter);
3680
+ const frontmatter = matter6.stringify("", rule.frontmatter);
3349
3681
  return frontmatter + rule.content;
3350
3682
  }
3351
3683
  async function generateUniqueFilename(rulesDir, baseFilename) {
3352
3684
  let filename = baseFilename;
3353
3685
  let counter = 1;
3354
- while (await fileExists(join21(rulesDir, `${filename}.md`))) {
3686
+ while (await fileExists(join19(rulesDir, `${filename}.md`))) {
3355
3687
  filename = `${baseFilename}-${counter}`;
3356
3688
  counter++;
3357
3689
  }
@@ -3418,7 +3750,7 @@ async function importCommand(options = {}) {
3418
3750
  }
3419
3751
 
3420
3752
  // src/cli/commands/init.ts
3421
- import { join as join22 } from "path";
3753
+ import { join as join20 } from "path";
3422
3754
  async function initCommand() {
3423
3755
  const aiRulesDir = ".rulesync";
3424
3756
  console.log("Initializing rulesync...");
@@ -3465,7 +3797,7 @@ globs: ["**/*"]
3465
3797
  - Follow single responsibility principle
3466
3798
  `
3467
3799
  };
3468
- const filepath = join22(aiRulesDir, sampleFile.filename);
3800
+ const filepath = join20(aiRulesDir, sampleFile.filename);
3469
3801
  if (!await fileExists(filepath)) {
3470
3802
  await writeFileContent(filepath, sampleFile.content);
3471
3803
  console.log(`Created ${filepath}`);
@@ -3609,7 +3941,7 @@ async function watchCommand() {
3609
3941
 
3610
3942
  // src/cli/index.ts
3611
3943
  var program = new Command();
3612
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.54.0");
3944
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.56.0");
3613
3945
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
3614
3946
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
3615
3947
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);