rulesync 0.54.0 → 0.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +401 -210
- package/dist/index.js +399 -208
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -435,6 +435,36 @@ function mergeWithCliOptions(config, cliOptions) {
|
|
|
435
435
|
return merged;
|
|
436
436
|
}
|
|
437
437
|
|
|
438
|
+
// src/utils/error.ts
|
|
439
|
+
function getErrorMessage(error) {
|
|
440
|
+
return error instanceof Error ? error.message : String(error);
|
|
441
|
+
}
|
|
442
|
+
function formatErrorWithContext(error, context) {
|
|
443
|
+
const errorMessage = getErrorMessage(error);
|
|
444
|
+
return `${context}: ${errorMessage}`;
|
|
445
|
+
}
|
|
446
|
+
function createErrorResult(error, context) {
|
|
447
|
+
const errorMessage = context ? formatErrorWithContext(error, context) : getErrorMessage(error);
|
|
448
|
+
return {
|
|
449
|
+
success: false,
|
|
450
|
+
error: errorMessage
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function createSuccessResult(result) {
|
|
454
|
+
return {
|
|
455
|
+
success: true,
|
|
456
|
+
result
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
async function safeAsyncOperation(operation, errorContext) {
|
|
460
|
+
try {
|
|
461
|
+
const result = await operation();
|
|
462
|
+
return createSuccessResult(result);
|
|
463
|
+
} catch (error) {
|
|
464
|
+
return createErrorResult(error, errorContext);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
438
468
|
// src/utils/file.ts
|
|
439
469
|
import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
|
|
440
470
|
import { dirname, join as join2 } from "path";
|
|
@@ -445,6 +475,9 @@ async function ensureDir(dirPath) {
|
|
|
445
475
|
await mkdir2(dirPath, { recursive: true });
|
|
446
476
|
}
|
|
447
477
|
}
|
|
478
|
+
function resolvePath(relativePath, baseDir) {
|
|
479
|
+
return baseDir ? join2(baseDir, relativePath) : relativePath;
|
|
480
|
+
}
|
|
448
481
|
async function readFileContent(filepath) {
|
|
449
482
|
return readFile(filepath, "utf-8");
|
|
450
483
|
}
|
|
@@ -1372,7 +1405,7 @@ function filterIgnoredFiles(files, ignorePatterns) {
|
|
|
1372
1405
|
|
|
1373
1406
|
// src/generators/rules/shared-helpers.ts
|
|
1374
1407
|
function resolveOutputDir(config, tool, baseDir) {
|
|
1375
|
-
return
|
|
1408
|
+
return resolvePath(config.outputPaths[tool], baseDir);
|
|
1376
1409
|
}
|
|
1377
1410
|
function createOutputsArray() {
|
|
1378
1411
|
return [];
|
|
@@ -1399,7 +1432,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
|
1399
1432
|
}
|
|
1400
1433
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
1401
1434
|
if (ignorePatterns.patterns.length > 0) {
|
|
1402
|
-
const ignorePath =
|
|
1435
|
+
const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
|
|
1403
1436
|
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
|
|
1404
1437
|
outputs.push({
|
|
1405
1438
|
tool: generatorConfig.tool,
|
|
@@ -1417,7 +1450,10 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
|
1417
1450
|
if (generatorConfig.generateDetailContent && generatorConfig.detailSubDir) {
|
|
1418
1451
|
for (const rule of detailRules) {
|
|
1419
1452
|
const content = generatorConfig.generateDetailContent(rule);
|
|
1420
|
-
const filepath =
|
|
1453
|
+
const filepath = resolvePath(
|
|
1454
|
+
join5(generatorConfig.detailSubDir, `${rule.filename}.md`),
|
|
1455
|
+
baseDir
|
|
1456
|
+
);
|
|
1421
1457
|
outputs.push({
|
|
1422
1458
|
tool: generatorConfig.tool,
|
|
1423
1459
|
filepath,
|
|
@@ -1427,7 +1463,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
|
1427
1463
|
}
|
|
1428
1464
|
if (generatorConfig.generateRootContent && generatorConfig.rootFilePath) {
|
|
1429
1465
|
const rootContent = generatorConfig.generateRootContent(rootRule, detailRules, baseDir);
|
|
1430
|
-
const rootFilepath =
|
|
1466
|
+
const rootFilepath = resolvePath(generatorConfig.rootFilePath, baseDir);
|
|
1431
1467
|
outputs.push({
|
|
1432
1468
|
tool: generatorConfig.tool,
|
|
1433
1469
|
filepath: rootFilepath,
|
|
@@ -1436,7 +1472,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
|
1436
1472
|
}
|
|
1437
1473
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
1438
1474
|
if (ignorePatterns.patterns.length > 0) {
|
|
1439
|
-
const ignorePath =
|
|
1475
|
+
const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
|
|
1440
1476
|
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
|
|
1441
1477
|
outputs.push({
|
|
1442
1478
|
tool: generatorConfig.tool,
|
|
@@ -1541,30 +1577,22 @@ function generateLegacyGuidelinesFile(allRules) {
|
|
|
1541
1577
|
// src/generators/rules/claudecode.ts
|
|
1542
1578
|
import { join as join7 } from "path";
|
|
1543
1579
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
1544
|
-
const
|
|
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({
|
|
1580
|
+
const generatorConfig = {
|
|
1550
1581
|
tool: "claudecode",
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
|
|
1566
|
-
}
|
|
1567
|
-
return outputs;
|
|
1582
|
+
fileExtension: ".md",
|
|
1583
|
+
ignoreFileName: ".aiignore",
|
|
1584
|
+
generateContent: generateMemoryFile,
|
|
1585
|
+
generateRootContent: (rootRule, detailRules) => generateClaudeMarkdown(rootRule ? [rootRule] : [], detailRules),
|
|
1586
|
+
rootFilePath: "CLAUDE.md",
|
|
1587
|
+
generateDetailContent: generateMemoryFile,
|
|
1588
|
+
detailSubDir: ".claude/memories",
|
|
1589
|
+
updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
|
|
1590
|
+
const settingsPath = resolvePath(join7(".claude", "settings.json"), baseDir2);
|
|
1591
|
+
await updateClaudeSettings(settingsPath, ignorePatterns);
|
|
1592
|
+
return [];
|
|
1593
|
+
}
|
|
1594
|
+
};
|
|
1595
|
+
return generateComplexRules(rules, config, generatorConfig, baseDir);
|
|
1568
1596
|
}
|
|
1569
1597
|
function generateClaudeMarkdown(rootRules, detailRules) {
|
|
1570
1598
|
const lines = [];
|
|
@@ -1624,54 +1652,222 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
1624
1652
|
console.log(`\u2705 Updated Claude Code settings: ${settingsPath}`);
|
|
1625
1653
|
}
|
|
1626
1654
|
|
|
1627
|
-
// src/generators/rules/
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1655
|
+
// src/generators/rules/generator-registry.ts
|
|
1656
|
+
import { join as join8 } from "path";
|
|
1657
|
+
var GENERATOR_REGISTRY = {
|
|
1658
|
+
// Simple generators - generate one file per rule
|
|
1659
|
+
cline: {
|
|
1660
|
+
type: "simple",
|
|
1661
|
+
tool: "cline",
|
|
1662
|
+
fileExtension: ".md",
|
|
1663
|
+
ignoreFileName: ".clineignore",
|
|
1664
|
+
generateContent: (rule) => rule.content.trim()
|
|
1665
|
+
},
|
|
1666
|
+
roo: {
|
|
1667
|
+
type: "simple",
|
|
1668
|
+
tool: "roo",
|
|
1669
|
+
fileExtension: ".md",
|
|
1670
|
+
ignoreFileName: ".rooignore",
|
|
1671
|
+
generateContent: (rule) => rule.content.trim()
|
|
1672
|
+
},
|
|
1673
|
+
kiro: {
|
|
1674
|
+
type: "simple",
|
|
1675
|
+
tool: "kiro",
|
|
1676
|
+
fileExtension: ".md",
|
|
1677
|
+
ignoreFileName: ".kiroignore",
|
|
1678
|
+
generateContent: (rule) => rule.content.trim()
|
|
1679
|
+
},
|
|
1680
|
+
augmentcode: {
|
|
1681
|
+
type: "simple",
|
|
1682
|
+
tool: "augmentcode",
|
|
1683
|
+
fileExtension: ".md",
|
|
1684
|
+
ignoreFileName: ".aiignore",
|
|
1685
|
+
generateContent: (rule) => rule.content.trim()
|
|
1686
|
+
},
|
|
1687
|
+
"augmentcode-legacy": {
|
|
1688
|
+
type: "simple",
|
|
1689
|
+
tool: "augmentcode-legacy",
|
|
1690
|
+
fileExtension: ".md",
|
|
1691
|
+
ignoreFileName: ".aiignore",
|
|
1692
|
+
generateContent: (rule) => rule.content.trim()
|
|
1693
|
+
},
|
|
1694
|
+
// Complex generators with custom content formatting
|
|
1695
|
+
copilot: {
|
|
1696
|
+
type: "simple",
|
|
1697
|
+
tool: "copilot",
|
|
1698
|
+
fileExtension: ".instructions.md",
|
|
1699
|
+
ignoreFileName: ".copilotignore",
|
|
1700
|
+
generateContent: (rule) => {
|
|
1701
|
+
const lines = [];
|
|
1702
|
+
lines.push("---");
|
|
1703
|
+
lines.push(`description: "${rule.frontmatter.description}"`);
|
|
1704
|
+
if (rule.frontmatter.globs.length > 0) {
|
|
1705
|
+
lines.push(`applyTo: "${rule.frontmatter.globs.join(", ")}"`);
|
|
1706
|
+
} else {
|
|
1707
|
+
lines.push('applyTo: "**"');
|
|
1708
|
+
}
|
|
1709
|
+
lines.push("---");
|
|
1710
|
+
lines.push(rule.content);
|
|
1711
|
+
return lines.join("\n");
|
|
1637
1712
|
},
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1713
|
+
pathResolver: (rule, outputDir) => {
|
|
1714
|
+
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
1715
|
+
return join8(outputDir, `${baseFilename}.instructions.md`);
|
|
1716
|
+
}
|
|
1717
|
+
},
|
|
1718
|
+
cursor: {
|
|
1719
|
+
type: "simple",
|
|
1720
|
+
tool: "cursor",
|
|
1721
|
+
fileExtension: ".md",
|
|
1722
|
+
ignoreFileName: ".cursorignore",
|
|
1723
|
+
generateContent: (rule) => rule.content.trim()
|
|
1724
|
+
},
|
|
1725
|
+
codexcli: {
|
|
1726
|
+
type: "simple",
|
|
1645
1727
|
tool: "codexcli",
|
|
1646
1728
|
fileExtension: ".md",
|
|
1647
1729
|
ignoreFileName: ".codexignore",
|
|
1648
|
-
generateContent:
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1730
|
+
generateContent: (rule) => rule.content.trim()
|
|
1731
|
+
},
|
|
1732
|
+
// Complex generators with root + detail pattern
|
|
1733
|
+
claudecode: {
|
|
1734
|
+
type: "complex",
|
|
1735
|
+
tool: "claudecode",
|
|
1736
|
+
fileExtension: ".md",
|
|
1737
|
+
ignoreFileName: ".aiignore",
|
|
1738
|
+
generateContent: (rule) => {
|
|
1739
|
+
const lines = [];
|
|
1740
|
+
if (rule.frontmatter.description) {
|
|
1741
|
+
lines.push(`# ${rule.frontmatter.description}
|
|
1742
|
+
`);
|
|
1652
1743
|
}
|
|
1653
|
-
|
|
1744
|
+
lines.push(rule.content.trim());
|
|
1745
|
+
return lines.join("\n");
|
|
1654
1746
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1747
|
+
// NOTE: Claude Code specific logic is handled in the actual generator file
|
|
1748
|
+
// due to complex settings.json manipulation requirements
|
|
1749
|
+
},
|
|
1750
|
+
geminicli: {
|
|
1751
|
+
type: "complex",
|
|
1752
|
+
tool: "geminicli",
|
|
1753
|
+
fileExtension: ".md",
|
|
1754
|
+
ignoreFileName: ".aiexclude",
|
|
1755
|
+
generateContent: (rule) => {
|
|
1756
|
+
const lines = [];
|
|
1757
|
+
if (rule.frontmatter.description) {
|
|
1758
|
+
lines.push(`# ${rule.frontmatter.description}
|
|
1759
|
+
`);
|
|
1760
|
+
}
|
|
1761
|
+
lines.push(rule.content.trim());
|
|
1762
|
+
return lines.join("\n");
|
|
1763
|
+
}
|
|
1764
|
+
// Complex generation handled by existing generator
|
|
1765
|
+
},
|
|
1766
|
+
junie: {
|
|
1767
|
+
type: "complex",
|
|
1768
|
+
tool: "junie",
|
|
1769
|
+
fileExtension: ".md",
|
|
1770
|
+
ignoreFileName: ".aiignore",
|
|
1771
|
+
generateContent: (rule) => {
|
|
1772
|
+
const lines = [];
|
|
1773
|
+
if (rule.frontmatter.description) {
|
|
1774
|
+
lines.push(`# ${rule.frontmatter.description}
|
|
1775
|
+
`);
|
|
1776
|
+
}
|
|
1777
|
+
lines.push(rule.content.trim());
|
|
1778
|
+
return lines.join("\n");
|
|
1664
1779
|
}
|
|
1780
|
+
// Complex generation handled by existing generator
|
|
1665
1781
|
}
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1782
|
+
};
|
|
1783
|
+
async function generateFromRegistry(tool, rules, config, baseDir) {
|
|
1784
|
+
const generatorConfig = GENERATOR_REGISTRY[tool];
|
|
1785
|
+
if (!generatorConfig) {
|
|
1786
|
+
throw new Error(`No generator configuration found for tool: ${tool}`);
|
|
1787
|
+
}
|
|
1788
|
+
if (generatorConfig.type === "simple") {
|
|
1789
|
+
const ruleConfig = {
|
|
1790
|
+
tool: generatorConfig.tool,
|
|
1791
|
+
fileExtension: generatorConfig.fileExtension,
|
|
1792
|
+
ignoreFileName: generatorConfig.ignoreFileName,
|
|
1793
|
+
generateContent: generatorConfig.generateContent,
|
|
1794
|
+
...generatorConfig.pathResolver && { pathResolver: generatorConfig.pathResolver }
|
|
1795
|
+
};
|
|
1796
|
+
return generateRulesConfig(rules, config, ruleConfig, baseDir);
|
|
1797
|
+
} else {
|
|
1798
|
+
const enhancedConfig = {
|
|
1799
|
+
tool: generatorConfig.tool,
|
|
1800
|
+
fileExtension: generatorConfig.fileExtension,
|
|
1801
|
+
ignoreFileName: generatorConfig.ignoreFileName,
|
|
1802
|
+
generateContent: generatorConfig.generateContent,
|
|
1803
|
+
...generatorConfig.generateRootContent && {
|
|
1804
|
+
generateRootContent: generatorConfig.generateRootContent
|
|
1805
|
+
},
|
|
1806
|
+
...generatorConfig.rootFilePath && { rootFilePath: generatorConfig.rootFilePath },
|
|
1807
|
+
...generatorConfig.generateDetailContent && {
|
|
1808
|
+
generateDetailContent: generatorConfig.generateDetailContent
|
|
1809
|
+
},
|
|
1810
|
+
...generatorConfig.detailSubDir && { detailSubDir: generatorConfig.detailSubDir },
|
|
1811
|
+
...generatorConfig.updateAdditionalConfig && {
|
|
1812
|
+
updateAdditionalConfig: generatorConfig.updateAdditionalConfig
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
return generateComplexRules(rules, config, enhancedConfig, baseDir);
|
|
1669
1816
|
}
|
|
1670
|
-
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
// src/generators/rules/cline.ts
|
|
1820
|
+
async function generateClineConfig(rules, config, baseDir) {
|
|
1821
|
+
return generateFromRegistry("cline", rules, config, baseDir);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// src/generators/rules/codexcli.ts
|
|
1825
|
+
async function generateCodexConfig(rules, config, baseDir) {
|
|
1826
|
+
const outputs = [];
|
|
1827
|
+
if (rules.length === 0) {
|
|
1828
|
+
return outputs;
|
|
1829
|
+
}
|
|
1830
|
+
const sortedRules = [...rules].sort((a, b) => {
|
|
1831
|
+
if (a.frontmatter.root === true && b.frontmatter.root !== true) return -1;
|
|
1832
|
+
if (a.frontmatter.root !== true && b.frontmatter.root === true) return 1;
|
|
1833
|
+
return 0;
|
|
1834
|
+
});
|
|
1835
|
+
const concatenatedContent = generateConcatenatedCodexContent(sortedRules);
|
|
1836
|
+
if (concatenatedContent.trim()) {
|
|
1837
|
+
const outputDir = resolveOutputDir(config, "codexcli", baseDir);
|
|
1838
|
+
const filepath = `${outputDir}/codex.md`;
|
|
1839
|
+
outputs.push({
|
|
1840
|
+
tool: "codexcli",
|
|
1841
|
+
filepath,
|
|
1842
|
+
content: concatenatedContent
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
1846
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
1847
|
+
const ignorePath = resolvePath(".codexignore", baseDir);
|
|
1848
|
+
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
|
|
1849
|
+
outputs.push({
|
|
1850
|
+
tool: "codexcli",
|
|
1851
|
+
filepath: ignorePath,
|
|
1852
|
+
content: ignoreContent
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
return outputs;
|
|
1856
|
+
}
|
|
1857
|
+
function generateConcatenatedCodexContent(rules) {
|
|
1858
|
+
const sections = [];
|
|
1859
|
+
for (const rule of rules) {
|
|
1860
|
+
const content = rule.content.trim();
|
|
1861
|
+
if (!content) {
|
|
1862
|
+
continue;
|
|
1863
|
+
}
|
|
1864
|
+
sections.push(content);
|
|
1865
|
+
}
|
|
1866
|
+
return sections.join("\n\n---\n\n");
|
|
1671
1867
|
}
|
|
1672
1868
|
|
|
1673
1869
|
// src/generators/rules/copilot.ts
|
|
1674
|
-
import { join as
|
|
1870
|
+
import { join as join9 } from "path";
|
|
1675
1871
|
async function generateCopilotConfig(rules, config, baseDir) {
|
|
1676
1872
|
return generateComplexRulesConfig(
|
|
1677
1873
|
rules,
|
|
@@ -1683,7 +1879,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
|
|
|
1683
1879
|
generateContent: generateCopilotMarkdown,
|
|
1684
1880
|
getOutputPath: (rule, outputDir) => {
|
|
1685
1881
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
1686
|
-
return
|
|
1882
|
+
return join9(outputDir, `${baseFilename}.instructions.md`);
|
|
1687
1883
|
}
|
|
1688
1884
|
},
|
|
1689
1885
|
baseDir
|
|
@@ -1704,7 +1900,7 @@ function generateCopilotMarkdown(rule) {
|
|
|
1704
1900
|
}
|
|
1705
1901
|
|
|
1706
1902
|
// src/generators/rules/cursor.ts
|
|
1707
|
-
import { join as
|
|
1903
|
+
import { join as join10 } from "path";
|
|
1708
1904
|
async function generateCursorConfig(rules, config, baseDir) {
|
|
1709
1905
|
return generateComplexRulesConfig(
|
|
1710
1906
|
rules,
|
|
@@ -1715,7 +1911,7 @@ async function generateCursorConfig(rules, config, baseDir) {
|
|
|
1715
1911
|
ignoreFileName: ".cursorignore",
|
|
1716
1912
|
generateContent: generateCursorMarkdown,
|
|
1717
1913
|
getOutputPath: (rule, outputDir) => {
|
|
1718
|
-
return
|
|
1914
|
+
return join10(outputDir, `${rule.filename}.mdc`);
|
|
1719
1915
|
}
|
|
1720
1916
|
},
|
|
1721
1917
|
baseDir
|
|
@@ -1844,38 +2040,13 @@ function generateGuidelinesMarkdown(rootRule, detailRules) {
|
|
|
1844
2040
|
}
|
|
1845
2041
|
|
|
1846
2042
|
// src/generators/rules/kiro.ts
|
|
1847
|
-
import { join as join10 } from "path";
|
|
1848
2043
|
async function generateKiroConfig(rules, config, baseDir) {
|
|
1849
|
-
|
|
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();
|
|
2044
|
+
return generateFromRegistry("kiro", rules, config, baseDir);
|
|
1864
2045
|
}
|
|
1865
2046
|
|
|
1866
2047
|
// src/generators/rules/roo.ts
|
|
1867
2048
|
async function generateRooConfig(rules, config, baseDir) {
|
|
1868
|
-
return
|
|
1869
|
-
rules,
|
|
1870
|
-
config,
|
|
1871
|
-
{
|
|
1872
|
-
tool: "roo",
|
|
1873
|
-
fileExtension: ".md",
|
|
1874
|
-
ignoreFileName: ".rooignore",
|
|
1875
|
-
generateContent: (rule) => rule.content.trim()
|
|
1876
|
-
},
|
|
1877
|
-
baseDir
|
|
1878
|
-
);
|
|
2049
|
+
return generateFromRegistry("roo", rules, config, baseDir);
|
|
1879
2050
|
}
|
|
1880
2051
|
|
|
1881
2052
|
// src/core/generator.ts
|
|
@@ -2446,7 +2617,7 @@ ${linesToAdd.join("\n")}
|
|
|
2446
2617
|
};
|
|
2447
2618
|
|
|
2448
2619
|
// src/core/importer.ts
|
|
2449
|
-
import { join as
|
|
2620
|
+
import { join as join20 } from "path";
|
|
2450
2621
|
import matter5 from "gray-matter";
|
|
2451
2622
|
|
|
2452
2623
|
// src/parsers/augmentcode.ts
|
|
@@ -2472,36 +2643,63 @@ function addRules(result, rules) {
|
|
|
2472
2643
|
}
|
|
2473
2644
|
result.rules.push(...rules);
|
|
2474
2645
|
}
|
|
2475
|
-
function handleParseError(error, context) {
|
|
2476
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2477
|
-
return `${context}: ${errorMessage}`;
|
|
2478
|
-
}
|
|
2479
2646
|
async function safeReadFile(operation, errorContext) {
|
|
2480
2647
|
try {
|
|
2481
2648
|
const result = await operation();
|
|
2482
|
-
return
|
|
2649
|
+
return createSuccessResult(result);
|
|
2483
2650
|
} catch (error) {
|
|
2484
|
-
return
|
|
2485
|
-
success: false,
|
|
2486
|
-
error: handleParseError(error, errorContext)
|
|
2487
|
-
};
|
|
2651
|
+
return createErrorResult(error, errorContext);
|
|
2488
2652
|
}
|
|
2489
2653
|
}
|
|
2490
2654
|
|
|
2491
2655
|
// src/parsers/augmentcode.ts
|
|
2492
2656
|
async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
|
|
2657
|
+
return parseUnifiedAugmentcode(baseDir, {
|
|
2658
|
+
rulesDir: ".augment/rules",
|
|
2659
|
+
targetName: "augmentcode",
|
|
2660
|
+
filenamePrefix: "augmentcode"
|
|
2661
|
+
});
|
|
2662
|
+
}
|
|
2663
|
+
async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
|
|
2664
|
+
return parseUnifiedAugmentcode(baseDir, {
|
|
2665
|
+
legacyFilePath: ".augment-guidelines",
|
|
2666
|
+
targetName: "augmentcode-legacy",
|
|
2667
|
+
filenamePrefix: "augmentcode-legacy"
|
|
2668
|
+
});
|
|
2669
|
+
}
|
|
2670
|
+
async function parseUnifiedAugmentcode(baseDir, config) {
|
|
2493
2671
|
const result = createParseResult();
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2672
|
+
if (config.rulesDir) {
|
|
2673
|
+
const rulesDir = join15(baseDir, config.rulesDir);
|
|
2674
|
+
if (await fileExists(rulesDir)) {
|
|
2675
|
+
const rulesResult = await parseAugmentRules(rulesDir, config);
|
|
2676
|
+
addRules(result, rulesResult.rules);
|
|
2677
|
+
result.errors.push(...rulesResult.errors);
|
|
2678
|
+
} else {
|
|
2679
|
+
addError(
|
|
2680
|
+
result,
|
|
2681
|
+
`No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
|
|
2682
|
+
);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
if (config.legacyFilePath) {
|
|
2686
|
+
const legacyPath = join15(baseDir, config.legacyFilePath);
|
|
2687
|
+
if (await fileExists(legacyPath)) {
|
|
2688
|
+
const legacyResult = await parseAugmentGuidelines(legacyPath, config);
|
|
2689
|
+
if (legacyResult.rule) {
|
|
2690
|
+
addRule(result, legacyResult.rule);
|
|
2691
|
+
}
|
|
2692
|
+
result.errors.push(...legacyResult.errors);
|
|
2693
|
+
} else {
|
|
2694
|
+
addError(
|
|
2695
|
+
result,
|
|
2696
|
+
`No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
|
|
2697
|
+
);
|
|
2698
|
+
}
|
|
2501
2699
|
}
|
|
2502
2700
|
return { rules: result.rules || [], errors: result.errors };
|
|
2503
2701
|
}
|
|
2504
|
-
async function parseAugmentRules(rulesDir) {
|
|
2702
|
+
async function parseAugmentRules(rulesDir, config) {
|
|
2505
2703
|
const rules = [];
|
|
2506
2704
|
const errors = [];
|
|
2507
2705
|
try {
|
|
@@ -2521,7 +2719,7 @@ async function parseAugmentRules(rulesDir) {
|
|
|
2521
2719
|
const filename = basename2(file, file.endsWith(".mdc") ? ".mdc" : ".md");
|
|
2522
2720
|
const frontmatter = {
|
|
2523
2721
|
root: isRoot,
|
|
2524
|
-
targets: [
|
|
2722
|
+
targets: [config.targetName],
|
|
2525
2723
|
description,
|
|
2526
2724
|
globs: ["**/*"],
|
|
2527
2725
|
// AugmentCode doesn't use specific globs in the same way
|
|
@@ -2530,7 +2728,7 @@ async function parseAugmentRules(rulesDir) {
|
|
|
2530
2728
|
rules.push({
|
|
2531
2729
|
frontmatter,
|
|
2532
2730
|
content: parsed.content.trim(),
|
|
2533
|
-
filename:
|
|
2731
|
+
filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
|
|
2534
2732
|
filepath: filePath
|
|
2535
2733
|
});
|
|
2536
2734
|
} catch (error) {
|
|
@@ -2541,50 +2739,33 @@ async function parseAugmentRules(rulesDir) {
|
|
|
2541
2739
|
}
|
|
2542
2740
|
} catch (error) {
|
|
2543
2741
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2544
|
-
errors.push(`Failed to read .
|
|
2742
|
+
errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
|
|
2545
2743
|
}
|
|
2546
2744
|
return { rules, errors };
|
|
2547
2745
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
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");
|
|
2746
|
+
async function parseAugmentGuidelines(guidelinesPath, config) {
|
|
2747
|
+
const parseResult = await safeReadFile(
|
|
2748
|
+
async () => {
|
|
2749
|
+
const content = await readFileContent(guidelinesPath);
|
|
2750
|
+
if (content.trim()) {
|
|
2751
|
+
const frontmatter = {
|
|
2752
|
+
root: true,
|
|
2753
|
+
// Legacy guidelines become root rules
|
|
2754
|
+
targets: [config.targetName],
|
|
2755
|
+
description: "Legacy AugmentCode guidelines",
|
|
2756
|
+
globs: ["**/*"]
|
|
2757
|
+
};
|
|
2758
|
+
return {
|
|
2759
|
+
frontmatter,
|
|
2760
|
+
content: content.trim(),
|
|
2761
|
+
filename: `${config.filenamePrefix}-guidelines`,
|
|
2762
|
+
filepath: guidelinesPath
|
|
2763
|
+
};
|
|
2764
|
+
}
|
|
2765
|
+
return null;
|
|
2766
|
+
},
|
|
2767
|
+
`Failed to parse ${config.legacyFilePath || guidelinesPath}`
|
|
2768
|
+
);
|
|
2588
2769
|
if (parseResult.success) {
|
|
2589
2770
|
return { rule: parseResult.result || null, errors: [] };
|
|
2590
2771
|
} else {
|
|
@@ -2593,33 +2774,36 @@ async function parseAugmentGuidelines(guidelinesPath) {
|
|
|
2593
2774
|
}
|
|
2594
2775
|
|
|
2595
2776
|
// src/parsers/shared-helpers.ts
|
|
2596
|
-
import { basename as basename3, join as
|
|
2777
|
+
import { basename as basename3, join as join16 } from "path";
|
|
2597
2778
|
import matter3 from "gray-matter";
|
|
2598
2779
|
async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
2599
2780
|
const errors = [];
|
|
2600
2781
|
const rules = [];
|
|
2601
2782
|
if (config.mainFile) {
|
|
2602
|
-
const
|
|
2783
|
+
const mainFile = config.mainFile;
|
|
2784
|
+
const mainFilePath = resolvePath(mainFile.path, baseDir);
|
|
2603
2785
|
if (await fileExists(mainFilePath)) {
|
|
2604
|
-
|
|
2786
|
+
const result = await safeAsyncOperation(async () => {
|
|
2605
2787
|
const rawContent = await readFileContent(mainFilePath);
|
|
2606
2788
|
let content;
|
|
2607
2789
|
let frontmatter;
|
|
2608
|
-
if (
|
|
2790
|
+
if (mainFile.useFrontmatter) {
|
|
2609
2791
|
const parsed = matter3(rawContent);
|
|
2610
2792
|
content = parsed.content.trim();
|
|
2793
|
+
const parsedFrontmatter = parsed.data;
|
|
2611
2794
|
frontmatter = {
|
|
2612
|
-
root: false,
|
|
2795
|
+
root: mainFile.isRoot ?? false,
|
|
2613
2796
|
targets: [config.tool],
|
|
2614
|
-
description:
|
|
2615
|
-
globs: ["**/*"]
|
|
2797
|
+
description: parsedFrontmatter.description || mainFile.description,
|
|
2798
|
+
globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
|
|
2799
|
+
...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
|
|
2616
2800
|
};
|
|
2617
2801
|
} else {
|
|
2618
2802
|
content = rawContent.trim();
|
|
2619
2803
|
frontmatter = {
|
|
2620
|
-
root: false,
|
|
2804
|
+
root: mainFile.isRoot ?? false,
|
|
2621
2805
|
targets: [config.tool],
|
|
2622
|
-
description:
|
|
2806
|
+
description: mainFile.description,
|
|
2623
2807
|
globs: ["**/*"]
|
|
2624
2808
|
};
|
|
2625
2809
|
}
|
|
@@ -2627,43 +2811,52 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
2627
2811
|
rules.push({
|
|
2628
2812
|
frontmatter,
|
|
2629
2813
|
content,
|
|
2630
|
-
filename: "instructions",
|
|
2814
|
+
filename: mainFile.filenameOverride || "instructions",
|
|
2631
2815
|
filepath: mainFilePath
|
|
2632
2816
|
});
|
|
2633
2817
|
}
|
|
2634
|
-
}
|
|
2635
|
-
|
|
2636
|
-
errors.push(
|
|
2818
|
+
}, `Failed to parse ${mainFile.path}`);
|
|
2819
|
+
if (!result.success) {
|
|
2820
|
+
errors.push(result.error);
|
|
2637
2821
|
}
|
|
2638
2822
|
}
|
|
2639
2823
|
}
|
|
2640
2824
|
if (config.directories) {
|
|
2641
2825
|
for (const dirConfig of config.directories) {
|
|
2642
|
-
const dirPath =
|
|
2826
|
+
const dirPath = resolvePath(dirConfig.directory, baseDir);
|
|
2643
2827
|
if (await fileExists(dirPath)) {
|
|
2644
|
-
|
|
2828
|
+
const result = await safeAsyncOperation(async () => {
|
|
2645
2829
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
2646
2830
|
const files = await readdir2(dirPath);
|
|
2647
2831
|
for (const file of files) {
|
|
2648
2832
|
if (file.endsWith(dirConfig.filePattern)) {
|
|
2649
|
-
const filePath =
|
|
2650
|
-
|
|
2833
|
+
const filePath = join16(dirPath, file);
|
|
2834
|
+
const fileResult = await safeAsyncOperation(async () => {
|
|
2651
2835
|
const rawContent = await readFileContent(filePath);
|
|
2652
2836
|
let content;
|
|
2837
|
+
let frontmatter;
|
|
2838
|
+
const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
|
|
2653
2839
|
if (dirConfig.filePattern === ".instructions.md") {
|
|
2654
2840
|
const parsed = matter3(rawContent);
|
|
2655
2841
|
content = parsed.content.trim();
|
|
2842
|
+
const parsedFrontmatter = parsed.data;
|
|
2843
|
+
frontmatter = {
|
|
2844
|
+
root: false,
|
|
2845
|
+
targets: [config.tool],
|
|
2846
|
+
description: parsedFrontmatter.description || `${dirConfig.description}: ${filename}`,
|
|
2847
|
+
globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
|
|
2848
|
+
...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
|
|
2849
|
+
};
|
|
2656
2850
|
} else {
|
|
2657
2851
|
content = rawContent.trim();
|
|
2658
|
-
|
|
2659
|
-
if (content) {
|
|
2660
|
-
const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
|
|
2661
|
-
const frontmatter = {
|
|
2852
|
+
frontmatter = {
|
|
2662
2853
|
root: false,
|
|
2663
2854
|
targets: [config.tool],
|
|
2664
2855
|
description: `${dirConfig.description}: ${filename}`,
|
|
2665
2856
|
globs: ["**/*"]
|
|
2666
2857
|
};
|
|
2858
|
+
}
|
|
2859
|
+
if (content) {
|
|
2667
2860
|
rules.push({
|
|
2668
2861
|
frontmatter,
|
|
2669
2862
|
content,
|
|
@@ -2671,15 +2864,15 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
2671
2864
|
filepath: filePath
|
|
2672
2865
|
});
|
|
2673
2866
|
}
|
|
2674
|
-
}
|
|
2675
|
-
|
|
2676
|
-
errors.push(
|
|
2867
|
+
}, `Failed to parse ${filePath}`);
|
|
2868
|
+
if (!fileResult.success) {
|
|
2869
|
+
errors.push(fileResult.error);
|
|
2677
2870
|
}
|
|
2678
2871
|
}
|
|
2679
2872
|
}
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
errors.push(
|
|
2873
|
+
}, `Failed to parse ${dirConfig.directory} files`);
|
|
2874
|
+
if (!result.success) {
|
|
2875
|
+
errors.push(result.error);
|
|
2683
2876
|
}
|
|
2684
2877
|
}
|
|
2685
2878
|
}
|
|
@@ -2694,7 +2887,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
2694
2887
|
const rules = [];
|
|
2695
2888
|
let ignorePatterns;
|
|
2696
2889
|
let mcpServers;
|
|
2697
|
-
const mainFilePath =
|
|
2890
|
+
const mainFilePath = resolvePath(config.mainFileName, baseDir);
|
|
2698
2891
|
if (!await fileExists(mainFilePath)) {
|
|
2699
2892
|
errors.push(`${config.mainFileName} file not found`);
|
|
2700
2893
|
return { rules, errors };
|
|
@@ -2705,12 +2898,12 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
2705
2898
|
if (mainRule) {
|
|
2706
2899
|
rules.push(mainRule);
|
|
2707
2900
|
}
|
|
2708
|
-
const memoryDir =
|
|
2901
|
+
const memoryDir = resolvePath(config.memoryDirPath, baseDir);
|
|
2709
2902
|
if (await fileExists(memoryDir)) {
|
|
2710
2903
|
const memoryRules = await parseMemoryFiles(memoryDir, config);
|
|
2711
2904
|
rules.push(...memoryRules);
|
|
2712
2905
|
}
|
|
2713
|
-
const settingsPath =
|
|
2906
|
+
const settingsPath = resolvePath(config.settingsPath, baseDir);
|
|
2714
2907
|
if (await fileExists(settingsPath)) {
|
|
2715
2908
|
const settingsResult = await parseSettingsFile(settingsPath, config.tool);
|
|
2716
2909
|
if (settingsResult.ignorePatterns) {
|
|
@@ -2722,7 +2915,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
2722
2915
|
errors.push(...settingsResult.errors);
|
|
2723
2916
|
}
|
|
2724
2917
|
if (config.additionalIgnoreFile) {
|
|
2725
|
-
const additionalIgnorePath =
|
|
2918
|
+
const additionalIgnorePath = resolvePath(config.additionalIgnoreFile.path, baseDir);
|
|
2726
2919
|
if (await fileExists(additionalIgnorePath)) {
|
|
2727
2920
|
const additionalPatterns = await config.additionalIgnoreFile.parser(additionalIgnorePath);
|
|
2728
2921
|
if (additionalPatterns.length > 0) {
|
|
@@ -2731,8 +2924,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
2731
2924
|
}
|
|
2732
2925
|
}
|
|
2733
2926
|
} catch (error) {
|
|
2734
|
-
|
|
2735
|
-
errors.push(`Failed to parse ${config.tool} configuration: ${errorMessage}`);
|
|
2927
|
+
errors.push(`Failed to parse ${config.tool} configuration: ${getErrorMessage(error)}`);
|
|
2736
2928
|
}
|
|
2737
2929
|
return {
|
|
2738
2930
|
rules,
|
|
@@ -2776,7 +2968,7 @@ async function parseMemoryFiles(memoryDir, config) {
|
|
|
2776
2968
|
const files = await readdir2(memoryDir);
|
|
2777
2969
|
for (const file of files) {
|
|
2778
2970
|
if (file.endsWith(".md")) {
|
|
2779
|
-
const filePath =
|
|
2971
|
+
const filePath = join16(memoryDir, file);
|
|
2780
2972
|
const content = await readFileContent(filePath);
|
|
2781
2973
|
if (content.trim()) {
|
|
2782
2974
|
const filename = basename3(file, ".md");
|
|
@@ -2828,8 +3020,7 @@ async function parseSettingsFile(settingsPath, tool) {
|
|
|
2828
3020
|
mcpServers = parseResult.data.mcpServers;
|
|
2829
3021
|
}
|
|
2830
3022
|
} catch (error) {
|
|
2831
|
-
|
|
2832
|
-
errors.push(`Failed to parse settings.json: ${errorMessage}`);
|
|
3023
|
+
errors.push(`Failed to parse settings.json: ${getErrorMessage(error)}`);
|
|
2833
3024
|
}
|
|
2834
3025
|
return {
|
|
2835
3026
|
errors,
|
|
@@ -2872,7 +3063,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
2872
3063
|
}
|
|
2873
3064
|
|
|
2874
3065
|
// src/parsers/codexcli.ts
|
|
2875
|
-
import { join as
|
|
3066
|
+
import { join as join17 } from "path";
|
|
2876
3067
|
|
|
2877
3068
|
// src/parsers/copilot.ts
|
|
2878
3069
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
@@ -2895,7 +3086,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
2895
3086
|
}
|
|
2896
3087
|
|
|
2897
3088
|
// src/parsers/cursor.ts
|
|
2898
|
-
import { basename as basename4, join as
|
|
3089
|
+
import { basename as basename4, join as join18 } from "path";
|
|
2899
3090
|
import matter4 from "gray-matter";
|
|
2900
3091
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
2901
3092
|
import { z as z6 } from "zod/mini";
|
|
@@ -3020,7 +3211,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3020
3211
|
const rules = [];
|
|
3021
3212
|
let ignorePatterns;
|
|
3022
3213
|
let mcpServers;
|
|
3023
|
-
const cursorFilePath =
|
|
3214
|
+
const cursorFilePath = join18(baseDir, ".cursorrules");
|
|
3024
3215
|
if (await fileExists(cursorFilePath)) {
|
|
3025
3216
|
try {
|
|
3026
3217
|
const rawContent = await readFileContent(cursorFilePath);
|
|
@@ -3041,14 +3232,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3041
3232
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
3042
3233
|
}
|
|
3043
3234
|
}
|
|
3044
|
-
const cursorRulesDir =
|
|
3235
|
+
const cursorRulesDir = join18(baseDir, ".cursor", "rules");
|
|
3045
3236
|
if (await fileExists(cursorRulesDir)) {
|
|
3046
3237
|
try {
|
|
3047
3238
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
3048
3239
|
const files = await readdir2(cursorRulesDir);
|
|
3049
3240
|
for (const file of files) {
|
|
3050
3241
|
if (file.endsWith(".mdc")) {
|
|
3051
|
-
const filePath =
|
|
3242
|
+
const filePath = join18(cursorRulesDir, file);
|
|
3052
3243
|
try {
|
|
3053
3244
|
const rawContent = await readFileContent(filePath);
|
|
3054
3245
|
const parsed = matter4(rawContent, customMatterOptions);
|
|
@@ -3077,7 +3268,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3077
3268
|
if (rules.length === 0) {
|
|
3078
3269
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
3079
3270
|
}
|
|
3080
|
-
const cursorIgnorePath =
|
|
3271
|
+
const cursorIgnorePath = join18(baseDir, ".cursorignore");
|
|
3081
3272
|
if (await fileExists(cursorIgnorePath)) {
|
|
3082
3273
|
try {
|
|
3083
3274
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -3090,7 +3281,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
3090
3281
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
3091
3282
|
}
|
|
3092
3283
|
}
|
|
3093
|
-
const cursorMcpPath =
|
|
3284
|
+
const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
|
|
3094
3285
|
if (await fileExists(cursorMcpPath)) {
|
|
3095
3286
|
try {
|
|
3096
3287
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -3139,11 +3330,11 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
3139
3330
|
}
|
|
3140
3331
|
|
|
3141
3332
|
// src/parsers/junie.ts
|
|
3142
|
-
import { join as
|
|
3333
|
+
import { join as join19 } from "path";
|
|
3143
3334
|
async function parseJunieConfiguration(baseDir = process.cwd()) {
|
|
3144
3335
|
const errors = [];
|
|
3145
3336
|
const rules = [];
|
|
3146
|
-
const guidelinesPath =
|
|
3337
|
+
const guidelinesPath = join19(baseDir, ".junie", "guidelines.md");
|
|
3147
3338
|
if (!await fileExists(guidelinesPath)) {
|
|
3148
3339
|
errors.push(".junie/guidelines.md file not found");
|
|
3149
3340
|
return { rules, errors };
|
|
@@ -3278,7 +3469,7 @@ async function importConfiguration(options) {
|
|
|
3278
3469
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
3279
3470
|
return { success: false, rulesCreated: 0, errors };
|
|
3280
3471
|
}
|
|
3281
|
-
const rulesDirPath =
|
|
3472
|
+
const rulesDirPath = join20(baseDir, rulesDir);
|
|
3282
3473
|
try {
|
|
3283
3474
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
3284
3475
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -3292,7 +3483,7 @@ async function importConfiguration(options) {
|
|
|
3292
3483
|
try {
|
|
3293
3484
|
const baseFilename = rule.filename;
|
|
3294
3485
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
3295
|
-
const filePath =
|
|
3486
|
+
const filePath = join20(rulesDirPath, `${filename}.md`);
|
|
3296
3487
|
const content = generateRuleFileContent(rule);
|
|
3297
3488
|
await writeFileContent(filePath, content);
|
|
3298
3489
|
rulesCreated++;
|
|
@@ -3307,7 +3498,7 @@ async function importConfiguration(options) {
|
|
|
3307
3498
|
let ignoreFileCreated = false;
|
|
3308
3499
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
3309
3500
|
try {
|
|
3310
|
-
const rulesyncignorePath =
|
|
3501
|
+
const rulesyncignorePath = join20(baseDir, ".rulesyncignore");
|
|
3311
3502
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
3312
3503
|
`;
|
|
3313
3504
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -3323,7 +3514,7 @@ async function importConfiguration(options) {
|
|
|
3323
3514
|
let mcpFileCreated = false;
|
|
3324
3515
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
3325
3516
|
try {
|
|
3326
|
-
const mcpPath =
|
|
3517
|
+
const mcpPath = join20(baseDir, rulesDir, ".mcp.json");
|
|
3327
3518
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
3328
3519
|
`;
|
|
3329
3520
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -3351,7 +3542,7 @@ function generateRuleFileContent(rule) {
|
|
|
3351
3542
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
3352
3543
|
let filename = baseFilename;
|
|
3353
3544
|
let counter = 1;
|
|
3354
|
-
while (await fileExists(
|
|
3545
|
+
while (await fileExists(join20(rulesDir, `${filename}.md`))) {
|
|
3355
3546
|
filename = `${baseFilename}-${counter}`;
|
|
3356
3547
|
counter++;
|
|
3357
3548
|
}
|
|
@@ -3418,7 +3609,7 @@ async function importCommand(options = {}) {
|
|
|
3418
3609
|
}
|
|
3419
3610
|
|
|
3420
3611
|
// src/cli/commands/init.ts
|
|
3421
|
-
import { join as
|
|
3612
|
+
import { join as join21 } from "path";
|
|
3422
3613
|
async function initCommand() {
|
|
3423
3614
|
const aiRulesDir = ".rulesync";
|
|
3424
3615
|
console.log("Initializing rulesync...");
|
|
@@ -3465,7 +3656,7 @@ globs: ["**/*"]
|
|
|
3465
3656
|
- Follow single responsibility principle
|
|
3466
3657
|
`
|
|
3467
3658
|
};
|
|
3468
|
-
const filepath =
|
|
3659
|
+
const filepath = join21(aiRulesDir, sampleFile.filename);
|
|
3469
3660
|
if (!await fileExists(filepath)) {
|
|
3470
3661
|
await writeFileContent(filepath, sampleFile.content);
|
|
3471
3662
|
console.log(`Created ${filepath}`);
|
|
@@ -3609,7 +3800,7 @@ async function watchCommand() {
|
|
|
3609
3800
|
|
|
3610
3801
|
// src/cli/index.ts
|
|
3611
3802
|
var program = new Command();
|
|
3612
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
3803
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.55.0");
|
|
3613
3804
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
3614
3805
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
3615
3806
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|