rulesync 0.53.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.
Files changed (27) hide show
  1. package/README.ja.md +186 -23
  2. package/README.md +101 -25
  3. package/dist/{augmentcode-ICLQ2NEZ.js → augmentcode-MJYD2Y4S.js} +2 -2
  4. package/dist/{chunk-TTHBLXOB.js → chunk-3PHMFVXP.js} +1 -1
  5. package/dist/{chunk-FFW6TGCM.js → chunk-BEPSWIZC.js} +1 -1
  6. package/dist/chunk-D7XQ4OHK.js +145 -0
  7. package/dist/{chunk-OPZOVKIL.js → chunk-ORNO5MOO.js} +1 -1
  8. package/dist/{chunk-Y2XH4E5R.js → chunk-OXKDEZJK.js} +1 -1
  9. package/dist/{chunk-FNL2KPM3.js → chunk-OY6BYYIX.js} +1 -1
  10. package/dist/{chunk-YPIFCGAP.js → chunk-PPAQWVXX.js} +1 -1
  11. package/dist/{chunk-QUJMXHNR.js → chunk-QVPD6ENS.js} +1 -1
  12. package/dist/{chunk-Y26DXTAT.js → chunk-TJKD6LEW.js} +1 -1
  13. package/dist/{chunk-HMMTSS5E.js → chunk-UHANRG2O.js} +1 -1
  14. package/dist/{chunk-IXCMY24P.js → chunk-UZCJNUXO.js} +1 -1
  15. package/dist/{chunk-USKQYIZ2.js → chunk-VI6SBYFB.js} +1 -0
  16. package/dist/{claudecode-4XWK2WAY.js → claudecode-CKGUHLRR.js} +3 -3
  17. package/dist/{cline-MNXOHP77.js → cline-Z5C656VR.js} +3 -3
  18. package/dist/codexcli-VFUJKSIJ.js +10 -0
  19. package/dist/{copilot-ARYIWVJ7.js → copilot-4WQS5TA7.js} +2 -2
  20. package/dist/{cursor-FCS74IAH.js → cursor-HOB2F2V2.js} +2 -2
  21. package/dist/{geminicli-VOPV6DXZ.js → geminicli-XTMQTIU2.js} +2 -2
  22. package/dist/index.cjs +608 -171
  23. package/dist/index.js +554 -183
  24. package/dist/{junie-A2Y2WZI4.js → junie-AN6CR7DD.js} +2 -2
  25. package/dist/{kiro-MHIK4UBV.js → kiro-PTUZOHQ2.js} +2 -2
  26. package/dist/{roo-VG4IUNTE.js → roo-WOMS36KU.js} +2 -2
  27. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,39 +1,42 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ generateJunieMcp
4
+ } from "./chunk-TJKD6LEW.js";
2
5
  import {
3
6
  generateKiroMcp
4
- } from "./chunk-QUJMXHNR.js";
7
+ } from "./chunk-QVPD6ENS.js";
5
8
  import {
6
9
  generateRooMcp
7
- } from "./chunk-FFW6TGCM.js";
10
+ } from "./chunk-BEPSWIZC.js";
8
11
  import {
9
12
  generateAugmentcodeMcp
10
- } from "./chunk-OPZOVKIL.js";
13
+ } from "./chunk-ORNO5MOO.js";
11
14
  import {
12
15
  generateClaudeMcp
13
- } from "./chunk-FNL2KPM3.js";
16
+ } from "./chunk-OY6BYYIX.js";
14
17
  import {
15
18
  generateClineMcp
16
- } from "./chunk-HMMTSS5E.js";
17
- import "./chunk-YPIFCGAP.js";
19
+ } from "./chunk-UHANRG2O.js";
20
+ import {
21
+ generateCodexMcp
22
+ } from "./chunk-D7XQ4OHK.js";
23
+ import "./chunk-PPAQWVXX.js";
18
24
  import {
19
25
  generateCopilotMcp
20
- } from "./chunk-Y2XH4E5R.js";
26
+ } from "./chunk-OXKDEZJK.js";
21
27
  import {
22
28
  generateCursorMcp
23
- } from "./chunk-TTHBLXOB.js";
29
+ } from "./chunk-3PHMFVXP.js";
24
30
  import {
25
31
  generateGeminiCliMcp
26
- } from "./chunk-IXCMY24P.js";
27
- import {
28
- generateJunieMcp
29
- } from "./chunk-Y26DXTAT.js";
32
+ } from "./chunk-UZCJNUXO.js";
30
33
  import {
31
34
  ALL_TOOL_TARGETS,
32
35
  RulesyncTargetsSchema,
33
36
  ToolTargetSchema,
34
37
  ToolTargetsSchema,
35
38
  isToolTarget
36
- } from "./chunk-USKQYIZ2.js";
39
+ } from "./chunk-VI6SBYFB.js";
37
40
 
38
41
  // src/cli/index.ts
39
42
  import { Command } from "commander";
@@ -53,6 +56,7 @@ function getDefaultConfig() {
53
56
  cursor: ".cursor/rules",
54
57
  cline: ".clinerules",
55
58
  claudecode: ".",
59
+ codexcli: ".",
56
60
  roo: ".roo/rules",
57
61
  geminicli: ".gemini/memories",
58
62
  kiro: ".kiro/steering",
@@ -139,6 +143,7 @@ var OutputPathsSchema = z3.object({
139
143
  cursor: z3.optional(z3.string()),
140
144
  cline: z3.optional(z3.string()),
141
145
  claudecode: z3.optional(z3.string()),
146
+ codexcli: z3.optional(z3.string()),
142
147
  roo: z3.optional(z3.string()),
143
148
  geminicli: z3.optional(z3.string()),
144
149
  kiro: z3.optional(z3.string()),
@@ -201,7 +206,8 @@ var McpServerBaseSchema = z4.object({
201
206
  alwaysAllow: z4.optional(z4.array(z4.string())),
202
207
  tools: z4.optional(z4.array(z4.string())),
203
208
  kiroAutoApprove: z4.optional(z4.array(z4.string())),
204
- kiroAutoBlock: z4.optional(z4.array(z4.string()))
209
+ kiroAutoBlock: z4.optional(z4.array(z4.string())),
210
+ headers: z4.optional(z4.record(z4.string(), z4.string()))
205
211
  });
206
212
  var RulesyncMcpServerSchema = z4.extend(McpServerBaseSchema, {
207
213
  targets: z4.optional(RulesyncTargetsSchema)
@@ -429,6 +435,36 @@ function mergeWithCliOptions(config, cliOptions) {
429
435
  return merged;
430
436
  }
431
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
+
432
468
  // src/utils/file.ts
433
469
  import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
434
470
  import { dirname, join as join2 } from "path";
@@ -439,6 +475,9 @@ async function ensureDir(dirPath) {
439
475
  await mkdir2(dirPath, { recursive: true });
440
476
  }
441
477
  }
478
+ function resolvePath(relativePath, baseDir) {
479
+ return baseDir ? join2(baseDir, relativePath) : relativePath;
480
+ }
442
481
  async function readFileContent(filepath) {
443
482
  return readFile(filepath, "utf-8");
444
483
  }
@@ -1160,6 +1199,135 @@ var ignoreConfigs = {
1160
1199
  }
1161
1200
  return augmentPatterns;
1162
1201
  }
1202
+ },
1203
+ codexcli: {
1204
+ tool: "codexcli",
1205
+ filename: ".codexignore",
1206
+ header: [
1207
+ "# Generated by rulesync - OpenAI Codex CLI ignore file",
1208
+ "# This file controls which files are excluded from Codex CLI's AI context",
1209
+ "# Note: .codexignore is a community-requested feature (GitHub Issue #205)",
1210
+ "# Currently using proposed syntax based on .gitignore patterns"
1211
+ ],
1212
+ corePatterns: [
1213
+ "# \u2500\u2500\u2500\u2500\u2500 Security & Credentials (Critical) \u2500\u2500\u2500\u2500\u2500",
1214
+ "# Environment files",
1215
+ ".env",
1216
+ ".env.*",
1217
+ "!.env.example",
1218
+ "",
1219
+ "# Private keys and certificates",
1220
+ "*.key",
1221
+ "*.pem",
1222
+ "*.p12",
1223
+ "*.pfx",
1224
+ "*.der",
1225
+ "*.crt",
1226
+ "",
1227
+ "# SSH keys",
1228
+ "id_rsa*",
1229
+ "id_dsa*",
1230
+ "*.ppk",
1231
+ "",
1232
+ "# API keys and secrets",
1233
+ "api-keys.json",
1234
+ "credentials.json",
1235
+ "secrets.json",
1236
+ "**/apikeys/",
1237
+ "**/*_token*",
1238
+ "**/*_secret*",
1239
+ "**/*api_key*",
1240
+ "",
1241
+ "# Cloud provider credentials",
1242
+ "aws-credentials.json",
1243
+ "gcp-service-account*.json",
1244
+ "azure-credentials.json",
1245
+ ".aws/",
1246
+ "",
1247
+ "# \u2500\u2500\u2500\u2500\u2500 Database & Infrastructure Secrets \u2500\u2500\u2500\u2500\u2500",
1248
+ "# Database configuration",
1249
+ "*.db",
1250
+ "*.sqlite",
1251
+ "*.sqlite3",
1252
+ "database.yml",
1253
+ "**/database/config.*",
1254
+ "",
1255
+ "# Infrastructure as Code secrets",
1256
+ "*.tfstate",
1257
+ "*.tfstate.*",
1258
+ "terraform.tfvars",
1259
+ "secrets.auto.tfvars",
1260
+ ".terraform/",
1261
+ "",
1262
+ "# Kubernetes secrets",
1263
+ "**/k8s/**/secret*.yaml",
1264
+ "**/kubernetes/**/secret*.yaml",
1265
+ "",
1266
+ "# \u2500\u2500\u2500\u2500\u2500 Business Sensitive Data \u2500\u2500\u2500\u2500\u2500",
1267
+ "# Sensitive directories",
1268
+ "secrets/",
1269
+ "private/",
1270
+ "confidential/",
1271
+ "internal-docs/",
1272
+ "company-secrets/",
1273
+ "",
1274
+ "# Customer and personal data",
1275
+ "customer-data/",
1276
+ "pii/",
1277
+ "personal-data/",
1278
+ "**/*customer*.csv",
1279
+ "**/*personal*.json",
1280
+ "",
1281
+ "# \u2500\u2500\u2500\u2500\u2500 Build Artifacts & Large Files \u2500\u2500\u2500\u2500\u2500",
1282
+ "# Build outputs (may contain embedded secrets)",
1283
+ "dist/",
1284
+ "build/",
1285
+ "out/",
1286
+ ".next/",
1287
+ ".nuxt/",
1288
+ "",
1289
+ "# Large files that slow down AI processing",
1290
+ "*.zip",
1291
+ "*.tar.gz",
1292
+ "*.rar",
1293
+ "**/*.{mp4,avi,mov,mkv}",
1294
+ "**/*.{pdf,doc,docx}",
1295
+ "",
1296
+ "# Data files",
1297
+ "*.csv",
1298
+ "*.tsv",
1299
+ "*.xlsx",
1300
+ "data/",
1301
+ "datasets/",
1302
+ "",
1303
+ "# \u2500\u2500\u2500\u2500\u2500 Development Environment \u2500\u2500\u2500\u2500\u2500",
1304
+ "# Personal IDE settings",
1305
+ ".vscode/settings.json",
1306
+ ".idea/workspace.xml",
1307
+ "",
1308
+ "# Temporary files",
1309
+ "*.swp",
1310
+ "*.swo",
1311
+ "*~",
1312
+ "*.tmp",
1313
+ "",
1314
+ "# Test data with sensitive content",
1315
+ "test-data/sensitive/",
1316
+ "tests/fixtures/real-data/**",
1317
+ "",
1318
+ "# \u2500\u2500\u2500\u2500\u2500 Logs & Runtime Data \u2500\u2500\u2500\u2500\u2500",
1319
+ "*.log",
1320
+ "logs/",
1321
+ ".cache/",
1322
+ "",
1323
+ "# \u2500\u2500\u2500\u2500\u2500 Re-include Important Files \u2500\u2500\u2500\u2500\u2500",
1324
+ "# Allow configuration examples and documentation",
1325
+ "!secrets/README.md",
1326
+ "!config/*.example.*",
1327
+ "!docs/**/*.md"
1328
+ ],
1329
+ includeCommonPatterns: false,
1330
+ projectPatternsHeader: "# \u2500\u2500\u2500\u2500\u2500 Project-specific patterns from rulesync rules \u2500\u2500\u2500\u2500\u2500"
1163
1331
  }
1164
1332
  };
1165
1333
 
@@ -1237,7 +1405,7 @@ function filterIgnoredFiles(files, ignorePatterns) {
1237
1405
 
1238
1406
  // src/generators/rules/shared-helpers.ts
1239
1407
  function resolveOutputDir(config, tool, baseDir) {
1240
- return baseDir ? join5(baseDir, config.outputPaths[tool]) : config.outputPaths[tool];
1408
+ return resolvePath(config.outputPaths[tool], baseDir);
1241
1409
  }
1242
1410
  function createOutputsArray() {
1243
1411
  return [];
@@ -1264,7 +1432,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
1264
1432
  }
1265
1433
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1266
1434
  if (ignorePatterns.patterns.length > 0) {
1267
- const ignorePath = baseDir ? join5(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
1435
+ const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
1268
1436
  const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1269
1437
  outputs.push({
1270
1438
  tool: generatorConfig.tool,
@@ -1282,7 +1450,10 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1282
1450
  if (generatorConfig.generateDetailContent && generatorConfig.detailSubDir) {
1283
1451
  for (const rule of detailRules) {
1284
1452
  const content = generatorConfig.generateDetailContent(rule);
1285
- const filepath = baseDir ? join5(baseDir, generatorConfig.detailSubDir, `${rule.filename}.md`) : join5(generatorConfig.detailSubDir, `${rule.filename}.md`);
1453
+ const filepath = resolvePath(
1454
+ join5(generatorConfig.detailSubDir, `${rule.filename}.md`),
1455
+ baseDir
1456
+ );
1286
1457
  outputs.push({
1287
1458
  tool: generatorConfig.tool,
1288
1459
  filepath,
@@ -1292,7 +1463,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1292
1463
  }
1293
1464
  if (generatorConfig.generateRootContent && generatorConfig.rootFilePath) {
1294
1465
  const rootContent = generatorConfig.generateRootContent(rootRule, detailRules, baseDir);
1295
- const rootFilepath = baseDir ? join5(baseDir, generatorConfig.rootFilePath) : generatorConfig.rootFilePath;
1466
+ const rootFilepath = resolvePath(generatorConfig.rootFilePath, baseDir);
1296
1467
  outputs.push({
1297
1468
  tool: generatorConfig.tool,
1298
1469
  filepath: rootFilepath,
@@ -1301,7 +1472,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1301
1472
  }
1302
1473
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1303
1474
  if (ignorePatterns.patterns.length > 0) {
1304
- const ignorePath = baseDir ? join5(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
1475
+ const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
1305
1476
  const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1306
1477
  outputs.push({
1307
1478
  tool: generatorConfig.tool,
@@ -1406,30 +1577,22 @@ function generateLegacyGuidelinesFile(allRules) {
1406
1577
  // src/generators/rules/claudecode.ts
1407
1578
  import { join as join7 } from "path";
1408
1579
  async function generateClaudecodeConfig(rules, config, baseDir) {
1409
- const outputs = [];
1410
- const rootRules = rules.filter((r) => r.frontmatter.root === true);
1411
- const detailRules = rules.filter((r) => r.frontmatter.root === false);
1412
- const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
1413
- const claudeOutputDir = baseDir ? join7(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
1414
- outputs.push({
1580
+ const generatorConfig = {
1415
1581
  tool: "claudecode",
1416
- filepath: join7(claudeOutputDir, "CLAUDE.md"),
1417
- content: claudeMdContent
1418
- });
1419
- for (const rule of detailRules) {
1420
- const memoryContent = generateMemoryFile(rule);
1421
- outputs.push({
1422
- tool: "claudecode",
1423
- filepath: join7(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
1424
- content: memoryContent
1425
- });
1426
- }
1427
- const ignorePatterns = await loadIgnorePatterns(baseDir);
1428
- if (ignorePatterns.patterns.length > 0) {
1429
- const settingsPath = baseDir ? join7(baseDir, ".claude", "settings.json") : join7(".claude", "settings.json");
1430
- await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
1431
- }
1432
- 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);
1433
1596
  }
1434
1597
  function generateClaudeMarkdown(rootRules, detailRules) {
1435
1598
  const lines = [];
@@ -1489,23 +1652,222 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
1489
1652
  console.log(`\u2705 Updated Claude Code settings: ${settingsPath}`);
1490
1653
  }
1491
1654
 
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");
1712
+ },
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",
1727
+ tool: "codexcli",
1728
+ fileExtension: ".md",
1729
+ ignoreFileName: ".codexignore",
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
+ `);
1743
+ }
1744
+ lines.push(rule.content.trim());
1745
+ return lines.join("\n");
1746
+ }
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");
1779
+ }
1780
+ // Complex generation handled by existing generator
1781
+ }
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);
1816
+ }
1817
+ }
1818
+
1492
1819
  // src/generators/rules/cline.ts
1493
1820
  async function generateClineConfig(rules, config, baseDir) {
1494
- return generateRulesConfig(
1495
- rules,
1496
- config,
1497
- {
1498
- tool: "cline",
1499
- fileExtension: ".md",
1500
- ignoreFileName: ".clineignore",
1501
- generateContent: (rule) => rule.content.trim()
1502
- },
1503
- baseDir
1504
- );
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");
1505
1867
  }
1506
1868
 
1507
1869
  // src/generators/rules/copilot.ts
1508
- import { join as join8 } from "path";
1870
+ import { join as join9 } from "path";
1509
1871
  async function generateCopilotConfig(rules, config, baseDir) {
1510
1872
  return generateComplexRulesConfig(
1511
1873
  rules,
@@ -1517,7 +1879,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
1517
1879
  generateContent: generateCopilotMarkdown,
1518
1880
  getOutputPath: (rule, outputDir) => {
1519
1881
  const baseFilename = rule.filename.replace(/\.md$/, "");
1520
- return join8(outputDir, `${baseFilename}.instructions.md`);
1882
+ return join9(outputDir, `${baseFilename}.instructions.md`);
1521
1883
  }
1522
1884
  },
1523
1885
  baseDir
@@ -1538,7 +1900,7 @@ function generateCopilotMarkdown(rule) {
1538
1900
  }
1539
1901
 
1540
1902
  // src/generators/rules/cursor.ts
1541
- import { join as join9 } from "path";
1903
+ import { join as join10 } from "path";
1542
1904
  async function generateCursorConfig(rules, config, baseDir) {
1543
1905
  return generateComplexRulesConfig(
1544
1906
  rules,
@@ -1549,7 +1911,7 @@ async function generateCursorConfig(rules, config, baseDir) {
1549
1911
  ignoreFileName: ".cursorignore",
1550
1912
  generateContent: generateCursorMarkdown,
1551
1913
  getOutputPath: (rule, outputDir) => {
1552
- return join9(outputDir, `${rule.filename}.mdc`);
1914
+ return join10(outputDir, `${rule.filename}.mdc`);
1553
1915
  }
1554
1916
  },
1555
1917
  baseDir
@@ -1678,38 +2040,13 @@ function generateGuidelinesMarkdown(rootRule, detailRules) {
1678
2040
  }
1679
2041
 
1680
2042
  // src/generators/rules/kiro.ts
1681
- import { join as join10 } from "path";
1682
2043
  async function generateKiroConfig(rules, config, baseDir) {
1683
- const outputs = [];
1684
- for (const rule of rules) {
1685
- const content = generateKiroMarkdown(rule);
1686
- const outputDir = baseDir ? join10(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
1687
- const filepath = join10(outputDir, `${rule.filename}.md`);
1688
- outputs.push({
1689
- tool: "kiro",
1690
- filepath,
1691
- content
1692
- });
1693
- }
1694
- return outputs;
1695
- }
1696
- function generateKiroMarkdown(rule) {
1697
- return rule.content.trim();
2044
+ return generateFromRegistry("kiro", rules, config, baseDir);
1698
2045
  }
1699
2046
 
1700
2047
  // src/generators/rules/roo.ts
1701
2048
  async function generateRooConfig(rules, config, baseDir) {
1702
- return generateRulesConfig(
1703
- rules,
1704
- config,
1705
- {
1706
- tool: "roo",
1707
- fileExtension: ".md",
1708
- ignoreFileName: ".rooignore",
1709
- generateContent: (rule) => rule.content.trim()
1710
- },
1711
- baseDir
1712
- );
2049
+ return generateFromRegistry("roo", rules, config, baseDir);
1713
2050
  }
1714
2051
 
1715
2052
  // src/core/generator.ts
@@ -1768,6 +2105,8 @@ async function generateForTool(tool, rules, config, baseDir) {
1768
2105
  return generateClineConfig(rules, config, baseDir);
1769
2106
  case "claudecode":
1770
2107
  return await generateClaudecodeConfig(rules, config, baseDir);
2108
+ case "codexcli":
2109
+ return generateCodexConfig(rules, config, baseDir);
1771
2110
  case "roo":
1772
2111
  return generateRooConfig(rules, config, baseDir);
1773
2112
  case "geminicli":
@@ -1960,6 +2299,11 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
1960
2299
  path: path4.join(targetRoot, ".cline", "mcp.json"),
1961
2300
  generate: () => generateClineMcp(config)
1962
2301
  },
2302
+ {
2303
+ tool: "codexcli-project",
2304
+ path: path4.join(targetRoot, ".codex", "mcp-config.json"),
2305
+ generate: () => generateCodexMcp(config)
2306
+ },
1963
2307
  {
1964
2308
  tool: "gemini-project",
1965
2309
  path: path4.join(targetRoot, ".gemini", "settings.json"),
@@ -1995,7 +2339,7 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
1995
2339
  try {
1996
2340
  const content = generator.generate();
1997
2341
  const parsed = JSON.parse(content);
1998
- if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("junie") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
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")) {
1999
2343
  if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
2000
2344
  results.push({
2001
2345
  tool: generator.tool,
@@ -2220,6 +2564,8 @@ var gitignoreCommand = async () => {
2220
2564
  "**/.clineignore",
2221
2565
  "**/CLAUDE.md",
2222
2566
  "**/.claude/memories/",
2567
+ "**/codex.md",
2568
+ "**/.codexignore",
2223
2569
  "**/.roo/rules/",
2224
2570
  "**/.rooignore",
2225
2571
  "**/.copilotignore",
@@ -2238,6 +2584,7 @@ var gitignoreCommand = async () => {
2238
2584
  "**/.cursor/mcp.json",
2239
2585
  "**/.cline/mcp.json",
2240
2586
  "**/.vscode/mcp.json",
2587
+ "**/.codex/mcp-config.json",
2241
2588
  "**/.gemini/settings.json",
2242
2589
  "**/.roo/mcp.json"
2243
2590
  ];
@@ -2296,36 +2643,63 @@ function addRules(result, rules) {
2296
2643
  }
2297
2644
  result.rules.push(...rules);
2298
2645
  }
2299
- function handleParseError(error, context) {
2300
- const errorMessage = error instanceof Error ? error.message : String(error);
2301
- return `${context}: ${errorMessage}`;
2302
- }
2303
2646
  async function safeReadFile(operation, errorContext) {
2304
2647
  try {
2305
2648
  const result = await operation();
2306
- return { success: true, result };
2649
+ return createSuccessResult(result);
2307
2650
  } catch (error) {
2308
- return {
2309
- success: false,
2310
- error: handleParseError(error, errorContext)
2311
- };
2651
+ return createErrorResult(error, errorContext);
2312
2652
  }
2313
2653
  }
2314
2654
 
2315
2655
  // src/parsers/augmentcode.ts
2316
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) {
2317
2671
  const result = createParseResult();
2318
- const rulesDir = join15(baseDir, ".augment", "rules");
2319
- if (await fileExists(rulesDir)) {
2320
- const rulesResult = await parseAugmentRules(rulesDir);
2321
- addRules(result, rulesResult.rules);
2322
- result.errors.push(...rulesResult.errors);
2323
- } else {
2324
- addError(result, "No AugmentCode configuration found. Expected .augment/rules/ directory.");
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
+ }
2325
2699
  }
2326
2700
  return { rules: result.rules || [], errors: result.errors };
2327
2701
  }
2328
- async function parseAugmentRules(rulesDir) {
2702
+ async function parseAugmentRules(rulesDir, config) {
2329
2703
  const rules = [];
2330
2704
  const errors = [];
2331
2705
  try {
@@ -2345,7 +2719,7 @@ async function parseAugmentRules(rulesDir) {
2345
2719
  const filename = basename2(file, file.endsWith(".mdc") ? ".mdc" : ".md");
2346
2720
  const frontmatter = {
2347
2721
  root: isRoot,
2348
- targets: ["augmentcode"],
2722
+ targets: [config.targetName],
2349
2723
  description,
2350
2724
  globs: ["**/*"],
2351
2725
  // AugmentCode doesn't use specific globs in the same way
@@ -2354,7 +2728,7 @@ async function parseAugmentRules(rulesDir) {
2354
2728
  rules.push({
2355
2729
  frontmatter,
2356
2730
  content: parsed.content.trim(),
2357
- filename: `augmentcode-${ruleType}-${filename}`,
2731
+ filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
2358
2732
  filepath: filePath
2359
2733
  });
2360
2734
  } catch (error) {
@@ -2365,50 +2739,33 @@ async function parseAugmentRules(rulesDir) {
2365
2739
  }
2366
2740
  } catch (error) {
2367
2741
  const errorMessage = error instanceof Error ? error.message : String(error);
2368
- errors.push(`Failed to read .augment/rules/ directory: ${errorMessage}`);
2742
+ errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
2369
2743
  }
2370
2744
  return { rules, errors };
2371
2745
  }
2372
-
2373
- // src/parsers/augmentcode-legacy.ts
2374
- import { join as join16 } from "path";
2375
- async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
2376
- const result = createParseResult();
2377
- const guidelinesPath = join16(baseDir, ".augment-guidelines");
2378
- if (await fileExists(guidelinesPath)) {
2379
- const guidelinesResult = await parseAugmentGuidelines(guidelinesPath);
2380
- if (guidelinesResult.rule) {
2381
- addRule(result, guidelinesResult.rule);
2382
- }
2383
- result.errors.push(...guidelinesResult.errors);
2384
- } else {
2385
- addError(
2386
- result,
2387
- "No AugmentCode legacy configuration found. Expected .augment-guidelines file."
2388
- );
2389
- }
2390
- return { rules: result.rules || [], errors: result.errors };
2391
- }
2392
- async function parseAugmentGuidelines(guidelinesPath) {
2393
- const parseResult = await safeReadFile(async () => {
2394
- const content = await readFileContent(guidelinesPath);
2395
- if (content.trim()) {
2396
- const frontmatter = {
2397
- root: true,
2398
- // Legacy guidelines become root rules
2399
- targets: ["augmentcode-legacy"],
2400
- description: "Legacy AugmentCode guidelines",
2401
- globs: ["**/*"]
2402
- };
2403
- return {
2404
- frontmatter,
2405
- content: content.trim(),
2406
- filename: "augmentcode-legacy-guidelines",
2407
- filepath: guidelinesPath
2408
- };
2409
- }
2410
- return null;
2411
- }, "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
+ );
2412
2769
  if (parseResult.success) {
2413
2770
  return { rule: parseResult.result || null, errors: [] };
2414
2771
  } else {
@@ -2417,33 +2774,36 @@ async function parseAugmentGuidelines(guidelinesPath) {
2417
2774
  }
2418
2775
 
2419
2776
  // src/parsers/shared-helpers.ts
2420
- import { basename as basename3, join as join17 } from "path";
2777
+ import { basename as basename3, join as join16 } from "path";
2421
2778
  import matter3 from "gray-matter";
2422
2779
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2423
2780
  const errors = [];
2424
2781
  const rules = [];
2425
2782
  if (config.mainFile) {
2426
- const mainFilePath = join17(baseDir, config.mainFile.path);
2783
+ const mainFile = config.mainFile;
2784
+ const mainFilePath = resolvePath(mainFile.path, baseDir);
2427
2785
  if (await fileExists(mainFilePath)) {
2428
- try {
2786
+ const result = await safeAsyncOperation(async () => {
2429
2787
  const rawContent = await readFileContent(mainFilePath);
2430
2788
  let content;
2431
2789
  let frontmatter;
2432
- if (config.mainFile.useFrontmatter) {
2790
+ if (mainFile.useFrontmatter) {
2433
2791
  const parsed = matter3(rawContent);
2434
2792
  content = parsed.content.trim();
2793
+ const parsedFrontmatter = parsed.data;
2435
2794
  frontmatter = {
2436
- root: false,
2795
+ root: mainFile.isRoot ?? false,
2437
2796
  targets: [config.tool],
2438
- description: config.mainFile.description,
2439
- globs: ["**/*"]
2797
+ description: parsedFrontmatter.description || mainFile.description,
2798
+ globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
2799
+ ...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
2440
2800
  };
2441
2801
  } else {
2442
2802
  content = rawContent.trim();
2443
2803
  frontmatter = {
2444
- root: false,
2804
+ root: mainFile.isRoot ?? false,
2445
2805
  targets: [config.tool],
2446
- description: config.mainFile.description,
2806
+ description: mainFile.description,
2447
2807
  globs: ["**/*"]
2448
2808
  };
2449
2809
  }
@@ -2451,43 +2811,52 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2451
2811
  rules.push({
2452
2812
  frontmatter,
2453
2813
  content,
2454
- filename: "instructions",
2814
+ filename: mainFile.filenameOverride || "instructions",
2455
2815
  filepath: mainFilePath
2456
2816
  });
2457
2817
  }
2458
- } catch (error) {
2459
- const errorMessage = error instanceof Error ? error.message : String(error);
2460
- errors.push(`Failed to parse ${config.mainFile.path}: ${errorMessage}`);
2818
+ }, `Failed to parse ${mainFile.path}`);
2819
+ if (!result.success) {
2820
+ errors.push(result.error);
2461
2821
  }
2462
2822
  }
2463
2823
  }
2464
2824
  if (config.directories) {
2465
2825
  for (const dirConfig of config.directories) {
2466
- const dirPath = join17(baseDir, dirConfig.directory);
2826
+ const dirPath = resolvePath(dirConfig.directory, baseDir);
2467
2827
  if (await fileExists(dirPath)) {
2468
- try {
2828
+ const result = await safeAsyncOperation(async () => {
2469
2829
  const { readdir: readdir2 } = await import("fs/promises");
2470
2830
  const files = await readdir2(dirPath);
2471
2831
  for (const file of files) {
2472
2832
  if (file.endsWith(dirConfig.filePattern)) {
2473
- const filePath = join17(dirPath, file);
2474
- try {
2833
+ const filePath = join16(dirPath, file);
2834
+ const fileResult = await safeAsyncOperation(async () => {
2475
2835
  const rawContent = await readFileContent(filePath);
2476
2836
  let content;
2837
+ let frontmatter;
2838
+ const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
2477
2839
  if (dirConfig.filePattern === ".instructions.md") {
2478
2840
  const parsed = matter3(rawContent);
2479
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
+ };
2480
2850
  } else {
2481
2851
  content = rawContent.trim();
2482
- }
2483
- if (content) {
2484
- const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
2485
- const frontmatter = {
2852
+ frontmatter = {
2486
2853
  root: false,
2487
2854
  targets: [config.tool],
2488
2855
  description: `${dirConfig.description}: ${filename}`,
2489
2856
  globs: ["**/*"]
2490
2857
  };
2858
+ }
2859
+ if (content) {
2491
2860
  rules.push({
2492
2861
  frontmatter,
2493
2862
  content,
@@ -2495,15 +2864,15 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2495
2864
  filepath: filePath
2496
2865
  });
2497
2866
  }
2498
- } catch (error) {
2499
- const errorMessage = error instanceof Error ? error.message : String(error);
2500
- errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
2867
+ }, `Failed to parse ${filePath}`);
2868
+ if (!fileResult.success) {
2869
+ errors.push(fileResult.error);
2501
2870
  }
2502
2871
  }
2503
2872
  }
2504
- } catch (error) {
2505
- const errorMessage = error instanceof Error ? error.message : String(error);
2506
- errors.push(`Failed to parse ${dirConfig.directory} files: ${errorMessage}`);
2873
+ }, `Failed to parse ${dirConfig.directory} files`);
2874
+ if (!result.success) {
2875
+ errors.push(result.error);
2507
2876
  }
2508
2877
  }
2509
2878
  }
@@ -2518,7 +2887,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
2518
2887
  const rules = [];
2519
2888
  let ignorePatterns;
2520
2889
  let mcpServers;
2521
- const mainFilePath = join17(baseDir, config.mainFileName);
2890
+ const mainFilePath = resolvePath(config.mainFileName, baseDir);
2522
2891
  if (!await fileExists(mainFilePath)) {
2523
2892
  errors.push(`${config.mainFileName} file not found`);
2524
2893
  return { rules, errors };
@@ -2529,12 +2898,12 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
2529
2898
  if (mainRule) {
2530
2899
  rules.push(mainRule);
2531
2900
  }
2532
- const memoryDir = join17(baseDir, config.memoryDirPath);
2901
+ const memoryDir = resolvePath(config.memoryDirPath, baseDir);
2533
2902
  if (await fileExists(memoryDir)) {
2534
2903
  const memoryRules = await parseMemoryFiles(memoryDir, config);
2535
2904
  rules.push(...memoryRules);
2536
2905
  }
2537
- const settingsPath = join17(baseDir, config.settingsPath);
2906
+ const settingsPath = resolvePath(config.settingsPath, baseDir);
2538
2907
  if (await fileExists(settingsPath)) {
2539
2908
  const settingsResult = await parseSettingsFile(settingsPath, config.tool);
2540
2909
  if (settingsResult.ignorePatterns) {
@@ -2546,7 +2915,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
2546
2915
  errors.push(...settingsResult.errors);
2547
2916
  }
2548
2917
  if (config.additionalIgnoreFile) {
2549
- const additionalIgnorePath = join17(baseDir, config.additionalIgnoreFile.path);
2918
+ const additionalIgnorePath = resolvePath(config.additionalIgnoreFile.path, baseDir);
2550
2919
  if (await fileExists(additionalIgnorePath)) {
2551
2920
  const additionalPatterns = await config.additionalIgnoreFile.parser(additionalIgnorePath);
2552
2921
  if (additionalPatterns.length > 0) {
@@ -2555,8 +2924,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
2555
2924
  }
2556
2925
  }
2557
2926
  } catch (error) {
2558
- const errorMessage = error instanceof Error ? error.message : String(error);
2559
- errors.push(`Failed to parse ${config.tool} configuration: ${errorMessage}`);
2927
+ errors.push(`Failed to parse ${config.tool} configuration: ${getErrorMessage(error)}`);
2560
2928
  }
2561
2929
  return {
2562
2930
  rules,
@@ -2600,7 +2968,7 @@ async function parseMemoryFiles(memoryDir, config) {
2600
2968
  const files = await readdir2(memoryDir);
2601
2969
  for (const file of files) {
2602
2970
  if (file.endsWith(".md")) {
2603
- const filePath = join17(memoryDir, file);
2971
+ const filePath = join16(memoryDir, file);
2604
2972
  const content = await readFileContent(filePath);
2605
2973
  if (content.trim()) {
2606
2974
  const filename = basename3(file, ".md");
@@ -2652,8 +3020,7 @@ async function parseSettingsFile(settingsPath, tool) {
2652
3020
  mcpServers = parseResult.data.mcpServers;
2653
3021
  }
2654
3022
  } catch (error) {
2655
- const errorMessage = error instanceof Error ? error.message : String(error);
2656
- errors.push(`Failed to parse settings.json: ${errorMessage}`);
3023
+ errors.push(`Failed to parse settings.json: ${getErrorMessage(error)}`);
2657
3024
  }
2658
3025
  return {
2659
3026
  errors,
@@ -2695,6 +3062,9 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
2695
3062
  });
2696
3063
  }
2697
3064
 
3065
+ // src/parsers/codexcli.ts
3066
+ import { join as join17 } from "path";
3067
+
2698
3068
  // src/parsers/copilot.ts
2699
3069
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
2700
3070
  return parseConfigurationFiles(baseDir, {
@@ -3430,12 +3800,12 @@ async function watchCommand() {
3430
3800
 
3431
3801
  // src/cli/index.ts
3432
3802
  var program = new Command();
3433
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.53.0");
3803
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.55.0");
3434
3804
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
3435
3805
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
3436
3806
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
3437
3807
  program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "Import from AugmentCode legacy format (.augment-guidelines)").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("-v, --verbose", "Verbose output").action(importCommand);
3438
- program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "Generate only for AugmentCode legacy format").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--junie", "Generate only for JetBrains Junie").option("--kiro", "Generate only for Kiro IDE").option("--delete", "Delete all existing files in output directories before generating").option(
3808
+ program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "Generate only for AugmentCode legacy format").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--codexcli", "Generate only for OpenAI Codex CLI").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--junie", "Generate only for JetBrains Junie").option("--kiro", "Generate only for Kiro IDE").option("--delete", "Delete all existing files in output directories before generating").option(
3439
3809
  "-b, --base-dir <paths>",
3440
3810
  "Base directories to generate files (comma-separated for multiple paths)"
3441
3811
  ).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
@@ -3445,6 +3815,7 @@ program.command("generate").description("Generate configuration files for AI too
3445
3815
  if (options.copilot) tools.push("copilot");
3446
3816
  if (options.cursor) tools.push("cursor");
3447
3817
  if (options.cline) tools.push("cline");
3818
+ if (options.codexcli) tools.push("codexcli");
3448
3819
  if (options.claudecode) tools.push("claudecode");
3449
3820
  if (options.roo) tools.push("roo");
3450
3821
  if (options.geminicli) tools.push("geminicli");