rulesync 0.55.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 +532 -318
  24. package/dist/index.js +304 -163
  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({
@@ -768,7 +775,7 @@ export default config;
768
775
  }
769
776
 
770
777
  // src/cli/commands/generate.ts
771
- import { join as join13 } from "path";
778
+ import { join as join11 } from "path";
772
779
 
773
780
  // src/generators/ignore/shared-factory.ts
774
781
  import { join as join3 } from "path";
@@ -1328,6 +1335,131 @@ var ignoreConfigs = {
1328
1335
  ],
1329
1336
  includeCommonPatterns: false,
1330
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"
1331
1463
  }
1332
1464
  };
1333
1465
 
@@ -1346,6 +1478,11 @@ async function generateKiroIgnoreFiles(rules, config, baseDir) {
1346
1478
  return generateIgnoreFile(rules, config, ignoreConfigs.kiro, baseDir);
1347
1479
  }
1348
1480
 
1481
+ // src/generators/ignore/windsurf.ts
1482
+ function generateWindsurfIgnore(rules, config, baseDir) {
1483
+ return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
1484
+ }
1485
+
1349
1486
  // src/generators/rules/augmentcode.ts
1350
1487
  import { join as join6 } from "path";
1351
1488
 
@@ -1502,16 +1639,6 @@ function generateIgnoreFile2(patterns, tool) {
1502
1639
  lines.push(...patterns);
1503
1640
  return lines.join("\n");
1504
1641
  }
1505
- async function generateComplexRulesConfig(rules, config, generatorConfig, baseDir) {
1506
- const unifiedConfig = {
1507
- tool: generatorConfig.tool,
1508
- fileExtension: generatorConfig.fileExtension,
1509
- ignoreFileName: generatorConfig.ignoreFileName,
1510
- generateContent: generatorConfig.generateContent,
1511
- pathResolver: generatorConfig.getOutputPath
1512
- };
1513
- return generateRulesConfig(rules, config, unifiedConfig, baseDir);
1514
- }
1515
1642
 
1516
1643
  // src/generators/rules/augmentcode.ts
1517
1644
  async function generateAugmentcodeConfig(rules, config, baseDir) {
@@ -1582,7 +1709,7 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
1582
1709
  fileExtension: ".md",
1583
1710
  ignoreFileName: ".aiignore",
1584
1711
  generateContent: generateMemoryFile,
1585
- generateRootContent: (rootRule, detailRules) => generateClaudeMarkdown(rootRule ? [rootRule] : [], detailRules),
1712
+ generateRootContent: generateClaudeMarkdown,
1586
1713
  rootFilePath: "CLAUDE.md",
1587
1714
  generateDetailContent: generateMemoryFile,
1588
1715
  detailSubDir: ".claude/memories",
@@ -1594,7 +1721,7 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
1594
1721
  };
1595
1722
  return generateComplexRules(rules, config, generatorConfig, baseDir);
1596
1723
  }
1597
- function generateClaudeMarkdown(rootRules, detailRules) {
1724
+ function generateClaudeMarkdown(rootRule, detailRules) {
1598
1725
  const lines = [];
1599
1726
  if (detailRules.length > 0) {
1600
1727
  lines.push("Please also reference the following documents as needed:");
@@ -1608,11 +1735,9 @@ function generateClaudeMarkdown(rootRules, detailRules) {
1608
1735
  }
1609
1736
  lines.push("");
1610
1737
  }
1611
- if (rootRules.length > 0) {
1612
- for (const rule of rootRules) {
1613
- lines.push(rule.content);
1614
- lines.push("");
1615
- }
1738
+ if (rootRule) {
1739
+ lines.push(rootRule.content);
1740
+ lines.push("");
1616
1741
  }
1617
1742
  return lines.join("\n");
1618
1743
  }
@@ -1654,6 +1779,27 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
1654
1779
 
1655
1780
  // src/generators/rules/generator-registry.ts
1656
1781
  import { join as join8 } from "path";
1782
+ function determineCursorRuleType(frontmatter) {
1783
+ if (frontmatter.cursorRuleType) {
1784
+ return frontmatter.cursorRuleType;
1785
+ }
1786
+ const isDescriptionEmpty = !frontmatter.description || frontmatter.description.trim() === "";
1787
+ const isGlobsEmpty = frontmatter.globs.length === 0;
1788
+ const isGlobsExactlyAllFiles = frontmatter.globs.length === 1 && frontmatter.globs[0] === "**/*";
1789
+ if (isGlobsExactlyAllFiles) {
1790
+ return "always";
1791
+ }
1792
+ if (isDescriptionEmpty && isGlobsEmpty) {
1793
+ return "manual";
1794
+ }
1795
+ if (isDescriptionEmpty && !isGlobsEmpty) {
1796
+ return "specificFiles";
1797
+ }
1798
+ if (!isDescriptionEmpty && isGlobsEmpty) {
1799
+ return "intelligently";
1800
+ }
1801
+ return "intelligently";
1802
+ }
1657
1803
  var GENERATOR_REGISTRY = {
1658
1804
  // Simple generators - generate one file per rule
1659
1805
  cline: {
@@ -1718,9 +1864,42 @@ var GENERATOR_REGISTRY = {
1718
1864
  cursor: {
1719
1865
  type: "simple",
1720
1866
  tool: "cursor",
1721
- fileExtension: ".md",
1867
+ fileExtension: ".mdc",
1722
1868
  ignoreFileName: ".cursorignore",
1723
- generateContent: (rule) => rule.content.trim()
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
+ }
1724
1903
  },
1725
1904
  codexcli: {
1726
1905
  type: "simple",
@@ -1729,6 +1908,39 @@ var GENERATOR_REGISTRY = {
1729
1908
  ignoreFileName: ".codexignore",
1730
1909
  generateContent: (rule) => rule.content.trim()
1731
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
+ },
1732
1944
  // Complex generators with root + detail pattern
1733
1945
  claudecode: {
1734
1946
  type: "complex",
@@ -1867,107 +2079,13 @@ function generateConcatenatedCodexContent(rules) {
1867
2079
  }
1868
2080
 
1869
2081
  // src/generators/rules/copilot.ts
1870
- import { join as join9 } from "path";
1871
2082
  async function generateCopilotConfig(rules, config, baseDir) {
1872
- return generateComplexRulesConfig(
1873
- rules,
1874
- config,
1875
- {
1876
- tool: "copilot",
1877
- fileExtension: ".instructions.md",
1878
- ignoreFileName: ".copilotignore",
1879
- generateContent: generateCopilotMarkdown,
1880
- getOutputPath: (rule, outputDir) => {
1881
- const baseFilename = rule.filename.replace(/\.md$/, "");
1882
- return join9(outputDir, `${baseFilename}.instructions.md`);
1883
- }
1884
- },
1885
- baseDir
1886
- );
1887
- }
1888
- function generateCopilotMarkdown(rule) {
1889
- const lines = [];
1890
- lines.push("---");
1891
- lines.push(`description: "${rule.frontmatter.description}"`);
1892
- if (rule.frontmatter.globs.length > 0) {
1893
- lines.push(`applyTo: "${rule.frontmatter.globs.join(", ")}"`);
1894
- } else {
1895
- lines.push('applyTo: "**"');
1896
- }
1897
- lines.push("---");
1898
- lines.push(rule.content);
1899
- return lines.join("\n");
2083
+ return generateFromRegistry("copilot", rules, config, baseDir);
1900
2084
  }
1901
2085
 
1902
2086
  // src/generators/rules/cursor.ts
1903
- import { join as join10 } from "path";
1904
2087
  async function generateCursorConfig(rules, config, baseDir) {
1905
- return generateComplexRulesConfig(
1906
- rules,
1907
- config,
1908
- {
1909
- tool: "cursor",
1910
- fileExtension: ".mdc",
1911
- ignoreFileName: ".cursorignore",
1912
- generateContent: generateCursorMarkdown,
1913
- getOutputPath: (rule, outputDir) => {
1914
- return join10(outputDir, `${rule.filename}.mdc`);
1915
- }
1916
- },
1917
- baseDir
1918
- );
1919
- }
1920
- function generateCursorMarkdown(rule) {
1921
- const lines = [];
1922
- const ruleType = determineCursorRuleType(rule.frontmatter);
1923
- lines.push("---");
1924
- switch (ruleType) {
1925
- case "always":
1926
- lines.push("description:");
1927
- lines.push("globs:");
1928
- lines.push("alwaysApply: true");
1929
- break;
1930
- case "manual":
1931
- lines.push("description:");
1932
- lines.push("globs:");
1933
- lines.push("alwaysApply: false");
1934
- break;
1935
- case "specificFiles":
1936
- lines.push("description:");
1937
- lines.push(`globs: ${rule.frontmatter.globs.join(",")}`);
1938
- lines.push("alwaysApply: false");
1939
- break;
1940
- case "intelligently":
1941
- lines.push(`description: ${rule.frontmatter.description}`);
1942
- lines.push("globs:");
1943
- lines.push("alwaysApply: false");
1944
- break;
1945
- }
1946
- lines.push("---");
1947
- lines.push("");
1948
- lines.push(rule.content);
1949
- return lines.join("\n");
1950
- }
1951
- function determineCursorRuleType(frontmatter) {
1952
- if (frontmatter.cursorRuleType) {
1953
- return frontmatter.cursorRuleType;
1954
- }
1955
- const isDescriptionEmpty = !frontmatter.description || frontmatter.description.trim() === "";
1956
- const isGlobsEmpty = frontmatter.globs.length === 0;
1957
- const isGlobsExactlyAllFiles = frontmatter.globs.length === 1 && frontmatter.globs[0] === "**/*";
1958
- if (isGlobsExactlyAllFiles) {
1959
- return "always";
1960
- }
1961
- if (isDescriptionEmpty && isGlobsEmpty) {
1962
- return "manual";
1963
- }
1964
- if (isDescriptionEmpty && !isGlobsEmpty) {
1965
- return "specificFiles";
1966
- }
1967
- if (!isDescriptionEmpty && isGlobsEmpty) {
1968
- return "intelligently";
1969
- }
1970
- return "intelligently";
2088
+ return generateFromRegistry("cursor", rules, config, baseDir);
1971
2089
  }
1972
2090
 
1973
2091
  // src/generators/rules/geminicli.ts
@@ -2049,6 +2167,11 @@ async function generateRooConfig(rules, config, baseDir) {
2049
2167
  return generateFromRegistry("roo", rules, config, baseDir);
2050
2168
  }
2051
2169
 
2170
+ // src/generators/rules/windsurf.ts
2171
+ async function generateWindsurfConfig(rules, config, baseDir) {
2172
+ return generateFromRegistry("windsurf", rules, config, baseDir);
2173
+ }
2174
+
2052
2175
  // src/core/generator.ts
2053
2176
  async function generateConfigurations(rules, config, targetTools, baseDir) {
2054
2177
  const outputs = createOutputsArray();
@@ -2121,6 +2244,11 @@ async function generateForTool(tool, rules, config, baseDir) {
2121
2244
  const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
2122
2245
  return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
2123
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
+ }
2124
2252
  default:
2125
2253
  console.warn(`Unknown tool: ${tool}`);
2126
2254
  return null;
@@ -2323,6 +2451,11 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
2323
2451
  tool: "roo-project",
2324
2452
  path: path4.join(targetRoot, ".roo", "mcp.json"),
2325
2453
  generate: () => generateRooMcp(config)
2454
+ },
2455
+ {
2456
+ tool: "windsurf-project",
2457
+ path: path4.join(targetRoot, "mcp_config.json"),
2458
+ generate: () => generateWindsurfMcp(config)
2326
2459
  }
2327
2460
  ];
2328
2461
  const filteredGenerators = targetTools ? generators.filter((g) => {
@@ -2339,7 +2472,7 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
2339
2472
  try {
2340
2473
  const content = generator.generate();
2341
2474
  const parsed = JSON.parse(content);
2342
- 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")) {
2343
2476
  if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
2344
2477
  results.push({
2345
2478
  tool: generator.tool,
@@ -2451,12 +2584,12 @@ async function generateCommand(options = {}) {
2451
2584
  for (const tool of targetTools) {
2452
2585
  switch (tool) {
2453
2586
  case "augmentcode":
2454
- deleteTasks.push(removeDirectory(join13(".augment", "rules")));
2455
- deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
2587
+ deleteTasks.push(removeDirectory(join11(".augment", "rules")));
2588
+ deleteTasks.push(removeDirectory(join11(".augment", "ignore")));
2456
2589
  break;
2457
2590
  case "augmentcode-legacy":
2458
2591
  deleteTasks.push(removeClaudeGeneratedFiles());
2459
- deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
2592
+ deleteTasks.push(removeDirectory(join11(".augment", "ignore")));
2460
2593
  break;
2461
2594
  case "copilot":
2462
2595
  deleteTasks.push(removeDirectory(config.outputPaths.copilot));
@@ -2479,6 +2612,9 @@ async function generateCommand(options = {}) {
2479
2612
  case "kiro":
2480
2613
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
2481
2614
  break;
2615
+ case "windsurf":
2616
+ deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
2617
+ break;
2482
2618
  }
2483
2619
  }
2484
2620
  await Promise.all(deleteTasks);
@@ -2551,9 +2687,9 @@ Generating configurations for base directory: ${baseDir}`);
2551
2687
 
2552
2688
  // src/cli/commands/gitignore.ts
2553
2689
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
2554
- import { join as join14 } from "path";
2690
+ import { join as join12 } from "path";
2555
2691
  var gitignoreCommand = async () => {
2556
- const gitignorePath = join14(process.cwd(), ".gitignore");
2692
+ const gitignorePath = join12(process.cwd(), ".gitignore");
2557
2693
  const rulesFilesToIgnore = [
2558
2694
  "# Generated by rulesync - AI tool configuration files",
2559
2695
  "**/.github/copilot-instructions.md",
@@ -2617,11 +2753,11 @@ ${linesToAdd.join("\n")}
2617
2753
  };
2618
2754
 
2619
2755
  // src/core/importer.ts
2620
- import { join as join20 } from "path";
2621
- import matter5 from "gray-matter";
2756
+ import { join as join19 } from "path";
2757
+ import matter6 from "gray-matter";
2622
2758
 
2623
2759
  // src/parsers/augmentcode.ts
2624
- import { basename as basename2, join as join15 } from "path";
2760
+ import { basename as basename2, join as join13 } from "path";
2625
2761
  import matter2 from "gray-matter";
2626
2762
 
2627
2763
  // src/utils/parser-helpers.ts
@@ -2670,7 +2806,7 @@ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
2670
2806
  async function parseUnifiedAugmentcode(baseDir, config) {
2671
2807
  const result = createParseResult();
2672
2808
  if (config.rulesDir) {
2673
- const rulesDir = join15(baseDir, config.rulesDir);
2809
+ const rulesDir = join13(baseDir, config.rulesDir);
2674
2810
  if (await fileExists(rulesDir)) {
2675
2811
  const rulesResult = await parseAugmentRules(rulesDir, config);
2676
2812
  addRules(result, rulesResult.rules);
@@ -2683,7 +2819,7 @@ async function parseUnifiedAugmentcode(baseDir, config) {
2683
2819
  }
2684
2820
  }
2685
2821
  if (config.legacyFilePath) {
2686
- const legacyPath = join15(baseDir, config.legacyFilePath);
2822
+ const legacyPath = join13(baseDir, config.legacyFilePath);
2687
2823
  if (await fileExists(legacyPath)) {
2688
2824
  const legacyResult = await parseAugmentGuidelines(legacyPath, config);
2689
2825
  if (legacyResult.rule) {
@@ -2707,7 +2843,7 @@ async function parseAugmentRules(rulesDir, config) {
2707
2843
  const files = await readdir2(rulesDir);
2708
2844
  for (const file of files) {
2709
2845
  if (file.endsWith(".md") || file.endsWith(".mdc")) {
2710
- const filePath = join15(rulesDir, file);
2846
+ const filePath = join13(rulesDir, file);
2711
2847
  try {
2712
2848
  const rawContent = await readFileContent(filePath);
2713
2849
  const parsed = matter2(rawContent);
@@ -2774,7 +2910,7 @@ async function parseAugmentGuidelines(guidelinesPath, config) {
2774
2910
  }
2775
2911
 
2776
2912
  // src/parsers/shared-helpers.ts
2777
- import { basename as basename3, join as join16 } from "path";
2913
+ import { basename as basename3, join as join14 } from "path";
2778
2914
  import matter3 from "gray-matter";
2779
2915
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2780
2916
  const errors = [];
@@ -2830,7 +2966,7 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2830
2966
  const files = await readdir2(dirPath);
2831
2967
  for (const file of files) {
2832
2968
  if (file.endsWith(dirConfig.filePattern)) {
2833
- const filePath = join16(dirPath, file);
2969
+ const filePath = join14(dirPath, file);
2834
2970
  const fileResult = await safeAsyncOperation(async () => {
2835
2971
  const rawContent = await readFileContent(filePath);
2836
2972
  let content;
@@ -2968,7 +3104,7 @@ async function parseMemoryFiles(memoryDir, config) {
2968
3104
  const files = await readdir2(memoryDir);
2969
3105
  for (const file of files) {
2970
3106
  if (file.endsWith(".md")) {
2971
- const filePath = join16(memoryDir, file);
3107
+ const filePath = join14(memoryDir, file);
2972
3108
  const content = await readFileContent(filePath);
2973
3109
  if (content.trim()) {
2974
3110
  const filename = basename3(file, ".md");
@@ -3063,7 +3199,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
3063
3199
  }
3064
3200
 
3065
3201
  // src/parsers/codexcli.ts
3066
- import { join as join17 } from "path";
3202
+ import { join as join15 } from "path";
3067
3203
 
3068
3204
  // src/parsers/copilot.ts
3069
3205
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
@@ -3086,7 +3222,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
3086
3222
  }
3087
3223
 
3088
3224
  // src/parsers/cursor.ts
3089
- import { basename as basename4, join as join18 } from "path";
3225
+ import { basename as basename4, join as join16 } from "path";
3090
3226
  import matter4 from "gray-matter";
3091
3227
  import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
3092
3228
  import { z as z6 } from "zod/mini";
@@ -3211,7 +3347,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3211
3347
  const rules = [];
3212
3348
  let ignorePatterns;
3213
3349
  let mcpServers;
3214
- const cursorFilePath = join18(baseDir, ".cursorrules");
3350
+ const cursorFilePath = join16(baseDir, ".cursorrules");
3215
3351
  if (await fileExists(cursorFilePath)) {
3216
3352
  try {
3217
3353
  const rawContent = await readFileContent(cursorFilePath);
@@ -3232,14 +3368,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3232
3368
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
3233
3369
  }
3234
3370
  }
3235
- const cursorRulesDir = join18(baseDir, ".cursor", "rules");
3371
+ const cursorRulesDir = join16(baseDir, ".cursor", "rules");
3236
3372
  if (await fileExists(cursorRulesDir)) {
3237
3373
  try {
3238
3374
  const { readdir: readdir2 } = await import("fs/promises");
3239
3375
  const files = await readdir2(cursorRulesDir);
3240
3376
  for (const file of files) {
3241
3377
  if (file.endsWith(".mdc")) {
3242
- const filePath = join18(cursorRulesDir, file);
3378
+ const filePath = join16(cursorRulesDir, file);
3243
3379
  try {
3244
3380
  const rawContent = await readFileContent(filePath);
3245
3381
  const parsed = matter4(rawContent, customMatterOptions);
@@ -3268,7 +3404,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3268
3404
  if (rules.length === 0) {
3269
3405
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
3270
3406
  }
3271
- const cursorIgnorePath = join18(baseDir, ".cursorignore");
3407
+ const cursorIgnorePath = join16(baseDir, ".cursorignore");
3272
3408
  if (await fileExists(cursorIgnorePath)) {
3273
3409
  try {
3274
3410
  const content = await readFileContent(cursorIgnorePath);
@@ -3281,7 +3417,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3281
3417
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
3282
3418
  }
3283
3419
  }
3284
- const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
3420
+ const cursorMcpPath = join16(baseDir, ".cursor", "mcp.json");
3285
3421
  if (await fileExists(cursorMcpPath)) {
3286
3422
  try {
3287
3423
  const content = await readFileContent(cursorMcpPath);
@@ -3330,11 +3466,11 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
3330
3466
  }
3331
3467
 
3332
3468
  // src/parsers/junie.ts
3333
- import { join as join19 } from "path";
3469
+ import { join as join17 } from "path";
3334
3470
  async function parseJunieConfiguration(baseDir = process.cwd()) {
3335
3471
  const errors = [];
3336
3472
  const rules = [];
3337
- const guidelinesPath = join19(baseDir, ".junie", "guidelines.md");
3473
+ const guidelinesPath = join17(baseDir, ".junie", "guidelines.md");
3338
3474
  if (!await fileExists(guidelinesPath)) {
3339
3475
  errors.push(".junie/guidelines.md file not found");
3340
3476
  return { rules, errors };
@@ -3385,6 +3521,11 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
3385
3521
  });
3386
3522
  }
3387
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
+
3388
3529
  // src/core/importer.ts
3389
3530
  async function importConfiguration(options) {
3390
3531
  const { tool, baseDir = process.cwd(), rulesDir = ".rulesync", verbose = false } = options;
@@ -3469,7 +3610,7 @@ async function importConfiguration(options) {
3469
3610
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
3470
3611
  return { success: false, rulesCreated: 0, errors };
3471
3612
  }
3472
- const rulesDirPath = join20(baseDir, rulesDir);
3613
+ const rulesDirPath = join19(baseDir, rulesDir);
3473
3614
  try {
3474
3615
  const { mkdir: mkdir3 } = await import("fs/promises");
3475
3616
  await mkdir3(rulesDirPath, { recursive: true });
@@ -3483,7 +3624,7 @@ async function importConfiguration(options) {
3483
3624
  try {
3484
3625
  const baseFilename = rule.filename;
3485
3626
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
3486
- const filePath = join20(rulesDirPath, `${filename}.md`);
3627
+ const filePath = join19(rulesDirPath, `${filename}.md`);
3487
3628
  const content = generateRuleFileContent(rule);
3488
3629
  await writeFileContent(filePath, content);
3489
3630
  rulesCreated++;
@@ -3498,7 +3639,7 @@ async function importConfiguration(options) {
3498
3639
  let ignoreFileCreated = false;
3499
3640
  if (ignorePatterns && ignorePatterns.length > 0) {
3500
3641
  try {
3501
- const rulesyncignorePath = join20(baseDir, ".rulesyncignore");
3642
+ const rulesyncignorePath = join19(baseDir, ".rulesyncignore");
3502
3643
  const ignoreContent = `${ignorePatterns.join("\n")}
3503
3644
  `;
3504
3645
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -3514,7 +3655,7 @@ async function importConfiguration(options) {
3514
3655
  let mcpFileCreated = false;
3515
3656
  if (mcpServers && Object.keys(mcpServers).length > 0) {
3516
3657
  try {
3517
- const mcpPath = join20(baseDir, rulesDir, ".mcp.json");
3658
+ const mcpPath = join19(baseDir, rulesDir, ".mcp.json");
3518
3659
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
3519
3660
  `;
3520
3661
  await writeFileContent(mcpPath, mcpContent);
@@ -3536,13 +3677,13 @@ async function importConfiguration(options) {
3536
3677
  };
3537
3678
  }
3538
3679
  function generateRuleFileContent(rule) {
3539
- const frontmatter = matter5.stringify("", rule.frontmatter);
3680
+ const frontmatter = matter6.stringify("", rule.frontmatter);
3540
3681
  return frontmatter + rule.content;
3541
3682
  }
3542
3683
  async function generateUniqueFilename(rulesDir, baseFilename) {
3543
3684
  let filename = baseFilename;
3544
3685
  let counter = 1;
3545
- while (await fileExists(join20(rulesDir, `${filename}.md`))) {
3686
+ while (await fileExists(join19(rulesDir, `${filename}.md`))) {
3546
3687
  filename = `${baseFilename}-${counter}`;
3547
3688
  counter++;
3548
3689
  }
@@ -3609,7 +3750,7 @@ async function importCommand(options = {}) {
3609
3750
  }
3610
3751
 
3611
3752
  // src/cli/commands/init.ts
3612
- import { join as join21 } from "path";
3753
+ import { join as join20 } from "path";
3613
3754
  async function initCommand() {
3614
3755
  const aiRulesDir = ".rulesync";
3615
3756
  console.log("Initializing rulesync...");
@@ -3656,7 +3797,7 @@ globs: ["**/*"]
3656
3797
  - Follow single responsibility principle
3657
3798
  `
3658
3799
  };
3659
- const filepath = join21(aiRulesDir, sampleFile.filename);
3800
+ const filepath = join20(aiRulesDir, sampleFile.filename);
3660
3801
  if (!await fileExists(filepath)) {
3661
3802
  await writeFileContent(filepath, sampleFile.content);
3662
3803
  console.log(`Created ${filepath}`);
@@ -3800,7 +3941,7 @@ async function watchCommand() {
3800
3941
 
3801
3942
  // src/cli/index.ts
3802
3943
  var program = new Command();
3803
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.55.0");
3944
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.56.0");
3804
3945
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
3805
3946
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
3806
3947
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);