rulesync 0.55.0 → 0.57.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 +117 -18
  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 +553 -323
  24. package/dist/index.js +325 -168
  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()),
@@ -222,11 +227,13 @@ var RulesyncMcpConfigSchema = z4.object({
222
227
  // src/types/rules.ts
223
228
  import { z as z5 } from "zod/mini";
224
229
  var RuleFrontmatterSchema = z5.object({
225
- root: z5.boolean(),
226
- targets: RulesyncTargetsSchema,
227
- description: z5.string(),
228
- globs: z5.array(z5.string()),
230
+ root: z5.optional(z5.boolean()),
231
+ targets: z5.optional(RulesyncTargetsSchema),
232
+ description: z5.optional(z5.string()),
233
+ globs: z5.optional(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;
@@ -2165,7 +2293,23 @@ async function parseRuleFile(filepath) {
2165
2293
  const content = await readFileContent(filepath);
2166
2294
  const parsed = matter(content);
2167
2295
  try {
2168
- const frontmatter = RuleFrontmatterSchema.parse(parsed.data);
2296
+ const validatedData = RuleFrontmatterSchema.parse(parsed.data);
2297
+ const frontmatter = {
2298
+ root: validatedData.root ?? false,
2299
+ targets: validatedData.targets ?? ["*"],
2300
+ description: validatedData.description ?? "",
2301
+ globs: validatedData.globs ?? [],
2302
+ ...validatedData.cursorRuleType !== void 0 && {
2303
+ cursorRuleType: validatedData.cursorRuleType
2304
+ },
2305
+ ...validatedData.windsurfActivationMode !== void 0 && {
2306
+ windsurfActivationMode: validatedData.windsurfActivationMode
2307
+ },
2308
+ ...validatedData.windsurfOutputFormat !== void 0 && {
2309
+ windsurfOutputFormat: validatedData.windsurfOutputFormat
2310
+ },
2311
+ ...validatedData.tags !== void 0 && { tags: validatedData.tags }
2312
+ };
2169
2313
  const filename = basename(filepath, ".md");
2170
2314
  return {
2171
2315
  frontmatter,
@@ -2323,6 +2467,11 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
2323
2467
  tool: "roo-project",
2324
2468
  path: path4.join(targetRoot, ".roo", "mcp.json"),
2325
2469
  generate: () => generateRooMcp(config)
2470
+ },
2471
+ {
2472
+ tool: "windsurf-project",
2473
+ path: path4.join(targetRoot, "mcp_config.json"),
2474
+ generate: () => generateWindsurfMcp(config)
2326
2475
  }
2327
2476
  ];
2328
2477
  const filteredGenerators = targetTools ? generators.filter((g) => {
@@ -2339,7 +2488,7 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
2339
2488
  try {
2340
2489
  const content = generator.generate();
2341
2490
  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")) {
2491
+ 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
2492
  if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
2344
2493
  results.push({
2345
2494
  tool: generator.tool,
@@ -2451,12 +2600,12 @@ async function generateCommand(options = {}) {
2451
2600
  for (const tool of targetTools) {
2452
2601
  switch (tool) {
2453
2602
  case "augmentcode":
2454
- deleteTasks.push(removeDirectory(join13(".augment", "rules")));
2455
- deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
2603
+ deleteTasks.push(removeDirectory(join11(".augment", "rules")));
2604
+ deleteTasks.push(removeDirectory(join11(".augment", "ignore")));
2456
2605
  break;
2457
2606
  case "augmentcode-legacy":
2458
2607
  deleteTasks.push(removeClaudeGeneratedFiles());
2459
- deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
2608
+ deleteTasks.push(removeDirectory(join11(".augment", "ignore")));
2460
2609
  break;
2461
2610
  case "copilot":
2462
2611
  deleteTasks.push(removeDirectory(config.outputPaths.copilot));
@@ -2479,6 +2628,9 @@ async function generateCommand(options = {}) {
2479
2628
  case "kiro":
2480
2629
  deleteTasks.push(removeDirectory(config.outputPaths.kiro));
2481
2630
  break;
2631
+ case "windsurf":
2632
+ deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
2633
+ break;
2482
2634
  }
2483
2635
  }
2484
2636
  await Promise.all(deleteTasks);
@@ -2551,9 +2703,9 @@ Generating configurations for base directory: ${baseDir}`);
2551
2703
 
2552
2704
  // src/cli/commands/gitignore.ts
2553
2705
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
2554
- import { join as join14 } from "path";
2706
+ import { join as join12 } from "path";
2555
2707
  var gitignoreCommand = async () => {
2556
- const gitignorePath = join14(process.cwd(), ".gitignore");
2708
+ const gitignorePath = join12(process.cwd(), ".gitignore");
2557
2709
  const rulesFilesToIgnore = [
2558
2710
  "# Generated by rulesync - AI tool configuration files",
2559
2711
  "**/.github/copilot-instructions.md",
@@ -2617,11 +2769,11 @@ ${linesToAdd.join("\n")}
2617
2769
  };
2618
2770
 
2619
2771
  // src/core/importer.ts
2620
- import { join as join20 } from "path";
2621
- import matter5 from "gray-matter";
2772
+ import { join as join19 } from "path";
2773
+ import matter6 from "gray-matter";
2622
2774
 
2623
2775
  // src/parsers/augmentcode.ts
2624
- import { basename as basename2, join as join15 } from "path";
2776
+ import { basename as basename2, join as join13 } from "path";
2625
2777
  import matter2 from "gray-matter";
2626
2778
 
2627
2779
  // src/utils/parser-helpers.ts
@@ -2670,7 +2822,7 @@ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
2670
2822
  async function parseUnifiedAugmentcode(baseDir, config) {
2671
2823
  const result = createParseResult();
2672
2824
  if (config.rulesDir) {
2673
- const rulesDir = join15(baseDir, config.rulesDir);
2825
+ const rulesDir = join13(baseDir, config.rulesDir);
2674
2826
  if (await fileExists(rulesDir)) {
2675
2827
  const rulesResult = await parseAugmentRules(rulesDir, config);
2676
2828
  addRules(result, rulesResult.rules);
@@ -2683,7 +2835,7 @@ async function parseUnifiedAugmentcode(baseDir, config) {
2683
2835
  }
2684
2836
  }
2685
2837
  if (config.legacyFilePath) {
2686
- const legacyPath = join15(baseDir, config.legacyFilePath);
2838
+ const legacyPath = join13(baseDir, config.legacyFilePath);
2687
2839
  if (await fileExists(legacyPath)) {
2688
2840
  const legacyResult = await parseAugmentGuidelines(legacyPath, config);
2689
2841
  if (legacyResult.rule) {
@@ -2707,7 +2859,7 @@ async function parseAugmentRules(rulesDir, config) {
2707
2859
  const files = await readdir2(rulesDir);
2708
2860
  for (const file of files) {
2709
2861
  if (file.endsWith(".md") || file.endsWith(".mdc")) {
2710
- const filePath = join15(rulesDir, file);
2862
+ const filePath = join13(rulesDir, file);
2711
2863
  try {
2712
2864
  const rawContent = await readFileContent(filePath);
2713
2865
  const parsed = matter2(rawContent);
@@ -2774,7 +2926,7 @@ async function parseAugmentGuidelines(guidelinesPath, config) {
2774
2926
  }
2775
2927
 
2776
2928
  // src/parsers/shared-helpers.ts
2777
- import { basename as basename3, join as join16 } from "path";
2929
+ import { basename as basename3, join as join14 } from "path";
2778
2930
  import matter3 from "gray-matter";
2779
2931
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2780
2932
  const errors = [];
@@ -2830,7 +2982,7 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2830
2982
  const files = await readdir2(dirPath);
2831
2983
  for (const file of files) {
2832
2984
  if (file.endsWith(dirConfig.filePattern)) {
2833
- const filePath = join16(dirPath, file);
2985
+ const filePath = join14(dirPath, file);
2834
2986
  const fileResult = await safeAsyncOperation(async () => {
2835
2987
  const rawContent = await readFileContent(filePath);
2836
2988
  let content;
@@ -2968,7 +3120,7 @@ async function parseMemoryFiles(memoryDir, config) {
2968
3120
  const files = await readdir2(memoryDir);
2969
3121
  for (const file of files) {
2970
3122
  if (file.endsWith(".md")) {
2971
- const filePath = join16(memoryDir, file);
3123
+ const filePath = join14(memoryDir, file);
2972
3124
  const content = await readFileContent(filePath);
2973
3125
  if (content.trim()) {
2974
3126
  const filename = basename3(file, ".md");
@@ -3063,7 +3215,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
3063
3215
  }
3064
3216
 
3065
3217
  // src/parsers/codexcli.ts
3066
- import { join as join17 } from "path";
3218
+ import { join as join15 } from "path";
3067
3219
 
3068
3220
  // src/parsers/copilot.ts
3069
3221
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
@@ -3086,7 +3238,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
3086
3238
  }
3087
3239
 
3088
3240
  // src/parsers/cursor.ts
3089
- import { basename as basename4, join as join18 } from "path";
3241
+ import { basename as basename4, join as join16 } from "path";
3090
3242
  import matter4 from "gray-matter";
3091
3243
  import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
3092
3244
  import { z as z6 } from "zod/mini";
@@ -3211,7 +3363,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3211
3363
  const rules = [];
3212
3364
  let ignorePatterns;
3213
3365
  let mcpServers;
3214
- const cursorFilePath = join18(baseDir, ".cursorrules");
3366
+ const cursorFilePath = join16(baseDir, ".cursorrules");
3215
3367
  if (await fileExists(cursorFilePath)) {
3216
3368
  try {
3217
3369
  const rawContent = await readFileContent(cursorFilePath);
@@ -3232,14 +3384,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3232
3384
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
3233
3385
  }
3234
3386
  }
3235
- const cursorRulesDir = join18(baseDir, ".cursor", "rules");
3387
+ const cursorRulesDir = join16(baseDir, ".cursor", "rules");
3236
3388
  if (await fileExists(cursorRulesDir)) {
3237
3389
  try {
3238
3390
  const { readdir: readdir2 } = await import("fs/promises");
3239
3391
  const files = await readdir2(cursorRulesDir);
3240
3392
  for (const file of files) {
3241
3393
  if (file.endsWith(".mdc")) {
3242
- const filePath = join18(cursorRulesDir, file);
3394
+ const filePath = join16(cursorRulesDir, file);
3243
3395
  try {
3244
3396
  const rawContent = await readFileContent(filePath);
3245
3397
  const parsed = matter4(rawContent, customMatterOptions);
@@ -3268,7 +3420,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3268
3420
  if (rules.length === 0) {
3269
3421
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
3270
3422
  }
3271
- const cursorIgnorePath = join18(baseDir, ".cursorignore");
3423
+ const cursorIgnorePath = join16(baseDir, ".cursorignore");
3272
3424
  if (await fileExists(cursorIgnorePath)) {
3273
3425
  try {
3274
3426
  const content = await readFileContent(cursorIgnorePath);
@@ -3281,7 +3433,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3281
3433
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
3282
3434
  }
3283
3435
  }
3284
- const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
3436
+ const cursorMcpPath = join16(baseDir, ".cursor", "mcp.json");
3285
3437
  if (await fileExists(cursorMcpPath)) {
3286
3438
  try {
3287
3439
  const content = await readFileContent(cursorMcpPath);
@@ -3330,11 +3482,11 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
3330
3482
  }
3331
3483
 
3332
3484
  // src/parsers/junie.ts
3333
- import { join as join19 } from "path";
3485
+ import { join as join17 } from "path";
3334
3486
  async function parseJunieConfiguration(baseDir = process.cwd()) {
3335
3487
  const errors = [];
3336
3488
  const rules = [];
3337
- const guidelinesPath = join19(baseDir, ".junie", "guidelines.md");
3489
+ const guidelinesPath = join17(baseDir, ".junie", "guidelines.md");
3338
3490
  if (!await fileExists(guidelinesPath)) {
3339
3491
  errors.push(".junie/guidelines.md file not found");
3340
3492
  return { rules, errors };
@@ -3385,6 +3537,11 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
3385
3537
  });
3386
3538
  }
3387
3539
 
3540
+ // src/parsers/windsurf.ts
3541
+ import { readFile as readFile2 } from "fs/promises";
3542
+ import { join as join18 } from "path";
3543
+ import matter5 from "gray-matter";
3544
+
3388
3545
  // src/core/importer.ts
3389
3546
  async function importConfiguration(options) {
3390
3547
  const { tool, baseDir = process.cwd(), rulesDir = ".rulesync", verbose = false } = options;
@@ -3469,7 +3626,7 @@ async function importConfiguration(options) {
3469
3626
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
3470
3627
  return { success: false, rulesCreated: 0, errors };
3471
3628
  }
3472
- const rulesDirPath = join20(baseDir, rulesDir);
3629
+ const rulesDirPath = join19(baseDir, rulesDir);
3473
3630
  try {
3474
3631
  const { mkdir: mkdir3 } = await import("fs/promises");
3475
3632
  await mkdir3(rulesDirPath, { recursive: true });
@@ -3483,7 +3640,7 @@ async function importConfiguration(options) {
3483
3640
  try {
3484
3641
  const baseFilename = rule.filename;
3485
3642
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
3486
- const filePath = join20(rulesDirPath, `${filename}.md`);
3643
+ const filePath = join19(rulesDirPath, `${filename}.md`);
3487
3644
  const content = generateRuleFileContent(rule);
3488
3645
  await writeFileContent(filePath, content);
3489
3646
  rulesCreated++;
@@ -3498,7 +3655,7 @@ async function importConfiguration(options) {
3498
3655
  let ignoreFileCreated = false;
3499
3656
  if (ignorePatterns && ignorePatterns.length > 0) {
3500
3657
  try {
3501
- const rulesyncignorePath = join20(baseDir, ".rulesyncignore");
3658
+ const rulesyncignorePath = join19(baseDir, ".rulesyncignore");
3502
3659
  const ignoreContent = `${ignorePatterns.join("\n")}
3503
3660
  `;
3504
3661
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -3514,7 +3671,7 @@ async function importConfiguration(options) {
3514
3671
  let mcpFileCreated = false;
3515
3672
  if (mcpServers && Object.keys(mcpServers).length > 0) {
3516
3673
  try {
3517
- const mcpPath = join20(baseDir, rulesDir, ".mcp.json");
3674
+ const mcpPath = join19(baseDir, rulesDir, ".mcp.json");
3518
3675
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
3519
3676
  `;
3520
3677
  await writeFileContent(mcpPath, mcpContent);
@@ -3536,13 +3693,13 @@ async function importConfiguration(options) {
3536
3693
  };
3537
3694
  }
3538
3695
  function generateRuleFileContent(rule) {
3539
- const frontmatter = matter5.stringify("", rule.frontmatter);
3696
+ const frontmatter = matter6.stringify("", rule.frontmatter);
3540
3697
  return frontmatter + rule.content;
3541
3698
  }
3542
3699
  async function generateUniqueFilename(rulesDir, baseFilename) {
3543
3700
  let filename = baseFilename;
3544
3701
  let counter = 1;
3545
- while (await fileExists(join20(rulesDir, `${filename}.md`))) {
3702
+ while (await fileExists(join19(rulesDir, `${filename}.md`))) {
3546
3703
  filename = `${baseFilename}-${counter}`;
3547
3704
  counter++;
3548
3705
  }
@@ -3609,7 +3766,7 @@ async function importCommand(options = {}) {
3609
3766
  }
3610
3767
 
3611
3768
  // src/cli/commands/init.ts
3612
- import { join as join21 } from "path";
3769
+ import { join as join20 } from "path";
3613
3770
  async function initCommand() {
3614
3771
  const aiRulesDir = ".rulesync";
3615
3772
  console.log("Initializing rulesync...");
@@ -3656,7 +3813,7 @@ globs: ["**/*"]
3656
3813
  - Follow single responsibility principle
3657
3814
  `
3658
3815
  };
3659
- const filepath = join21(aiRulesDir, sampleFile.filename);
3816
+ const filepath = join20(aiRulesDir, sampleFile.filename);
3660
3817
  if (!await fileExists(filepath)) {
3661
3818
  await writeFileContent(filepath, sampleFile.content);
3662
3819
  console.log(`Created ${filepath}`);
@@ -3800,7 +3957,7 @@ async function watchCommand() {
3800
3957
 
3801
3958
  // src/cli/index.ts
3802
3959
  var program = new Command();
3803
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.55.0");
3960
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.57.0");
3804
3961
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
3805
3962
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
3806
3963
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);