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.cjs CHANGED
@@ -43,6 +43,7 @@ var init_tool_targets = __esm({
43
43
  "cursor",
44
44
  "cline",
45
45
  "claudecode",
46
+ "codexcli",
46
47
  "roo",
47
48
  "geminicli",
48
49
  "kiro",
@@ -241,6 +242,73 @@ var init_cline = __esm({
241
242
  }
242
243
  });
243
244
 
245
+ // src/generators/mcp/codexcli.ts
246
+ function generateCodexMcp(config) {
247
+ return generateMcpConfig(config, {
248
+ target: "codexcli",
249
+ configPaths: [".codex/mcp-config.json"],
250
+ serverTransform: (server) => {
251
+ const codexServer = {};
252
+ if (server.command) {
253
+ codexServer.command = server.command;
254
+ if (server.args) codexServer.args = server.args;
255
+ codexServer.transport = server.transport || "stdio";
256
+ } else if (server.url || server.httpUrl) {
257
+ const url = server.httpUrl || server.url;
258
+ if (url) {
259
+ codexServer.url = url;
260
+ }
261
+ if (server.httpUrl) {
262
+ codexServer.transport = "http";
263
+ } else if (server.transport === "sse") {
264
+ codexServer.transport = "sse";
265
+ } else if (server.transport === "http") {
266
+ codexServer.transport = "http";
267
+ } else {
268
+ codexServer.transport = "stdio";
269
+ }
270
+ } else {
271
+ codexServer.transport = "stdio";
272
+ }
273
+ if (server.env) {
274
+ codexServer.env = { ...server.env };
275
+ if (!codexServer.env.CODEX_DEFAULT_MODEL) {
276
+ codexServer.env.CODEX_DEFAULT_MODEL = "gpt-4o-mini";
277
+ }
278
+ }
279
+ if (server.cwd) {
280
+ codexServer.cwd = server.cwd;
281
+ codexServer.workingDirectory = server.cwd;
282
+ }
283
+ if (server.timeout) {
284
+ codexServer.timeout = server.timeout;
285
+ }
286
+ if (server.headers) {
287
+ codexServer.headers = server.headers;
288
+ }
289
+ return codexServer;
290
+ },
291
+ configWrapper: (servers) => ({
292
+ // Configuration format for MCP wrapper servers that expose Codex CLI functionality
293
+ servers,
294
+ _comment: "Configuration for MCP wrapper servers like openai-codex-mcp that integrate with Codex CLI",
295
+ _usage: "This file is intended for use with third-party MCP servers that wrap Codex CLI functionality",
296
+ _examples: {
297
+ python_server: "python -m mcp_server or uvicorn codex_server:app",
298
+ nodejs_server: "node dist/server.js or npm start",
299
+ docker_server: "docker run -i --rm custom/codex-mcp:latest"
300
+ },
301
+ _security_note: "Store API keys in environment variables, not in this configuration file"
302
+ })
303
+ });
304
+ }
305
+ var init_codexcli = __esm({
306
+ "src/generators/mcp/codexcli.ts"() {
307
+ "use strict";
308
+ init_shared_factory();
309
+ }
310
+ });
311
+
244
312
  // src/generators/mcp/copilot.ts
245
313
  function generateCopilotMcp(config, target) {
246
314
  const servers = {};
@@ -558,6 +626,7 @@ function getDefaultConfig() {
558
626
  cursor: ".cursor/rules",
559
627
  cline: ".clinerules",
560
628
  claudecode: ".",
629
+ codexcli: ".",
561
630
  roo: ".roo/rules",
562
631
  geminicli: ".gemini/memories",
563
632
  kiro: ".kiro/steering",
@@ -646,6 +715,7 @@ var OutputPathsSchema = import_mini4.z.object({
646
715
  cursor: import_mini4.z.optional(import_mini4.z.string()),
647
716
  cline: import_mini4.z.optional(import_mini4.z.string()),
648
717
  claudecode: import_mini4.z.optional(import_mini4.z.string()),
718
+ codexcli: import_mini4.z.optional(import_mini4.z.string()),
649
719
  roo: import_mini4.z.optional(import_mini4.z.string()),
650
720
  geminicli: import_mini4.z.optional(import_mini4.z.string()),
651
721
  kiro: import_mini4.z.optional(import_mini4.z.string()),
@@ -709,7 +779,8 @@ var McpServerBaseSchema = import_mini5.z.object({
709
779
  alwaysAllow: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
710
780
  tools: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
711
781
  kiroAutoApprove: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
712
- kiroAutoBlock: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string()))
782
+ kiroAutoBlock: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
783
+ headers: import_mini5.z.optional(import_mini5.z.record(import_mini5.z.string(), import_mini5.z.string()))
713
784
  });
714
785
  var RulesyncMcpServerSchema = import_mini5.z.extend(McpServerBaseSchema, {
715
786
  targets: import_mini5.z.optional(RulesyncTargetsSchema)
@@ -941,6 +1012,36 @@ function mergeWithCliOptions(config, cliOptions) {
941
1012
  return merged;
942
1013
  }
943
1014
 
1015
+ // src/utils/error.ts
1016
+ function getErrorMessage(error) {
1017
+ return error instanceof Error ? error.message : String(error);
1018
+ }
1019
+ function formatErrorWithContext(error, context) {
1020
+ const errorMessage = getErrorMessage(error);
1021
+ return `${context}: ${errorMessage}`;
1022
+ }
1023
+ function createErrorResult(error, context) {
1024
+ const errorMessage = context ? formatErrorWithContext(error, context) : getErrorMessage(error);
1025
+ return {
1026
+ success: false,
1027
+ error: errorMessage
1028
+ };
1029
+ }
1030
+ function createSuccessResult(result) {
1031
+ return {
1032
+ success: true,
1033
+ result
1034
+ };
1035
+ }
1036
+ async function safeAsyncOperation(operation, errorContext) {
1037
+ try {
1038
+ const result = await operation();
1039
+ return createSuccessResult(result);
1040
+ } catch (error) {
1041
+ return createErrorResult(error, errorContext);
1042
+ }
1043
+ }
1044
+
944
1045
  // src/utils/file.ts
945
1046
  var import_promises2 = require("fs/promises");
946
1047
  var import_node_path = require("path");
@@ -951,6 +1052,9 @@ async function ensureDir(dirPath) {
951
1052
  await (0, import_promises2.mkdir)(dirPath, { recursive: true });
952
1053
  }
953
1054
  }
1055
+ function resolvePath(relativePath, baseDir) {
1056
+ return baseDir ? (0, import_node_path.join)(baseDir, relativePath) : relativePath;
1057
+ }
954
1058
  async function readFileContent(filepath) {
955
1059
  return (0, import_promises2.readFile)(filepath, "utf-8");
956
1060
  }
@@ -1672,6 +1776,135 @@ var ignoreConfigs = {
1672
1776
  }
1673
1777
  return augmentPatterns;
1674
1778
  }
1779
+ },
1780
+ codexcli: {
1781
+ tool: "codexcli",
1782
+ filename: ".codexignore",
1783
+ header: [
1784
+ "# Generated by rulesync - OpenAI Codex CLI ignore file",
1785
+ "# This file controls which files are excluded from Codex CLI's AI context",
1786
+ "# Note: .codexignore is a community-requested feature (GitHub Issue #205)",
1787
+ "# Currently using proposed syntax based on .gitignore patterns"
1788
+ ],
1789
+ corePatterns: [
1790
+ "# \u2500\u2500\u2500\u2500\u2500 Security & Credentials (Critical) \u2500\u2500\u2500\u2500\u2500",
1791
+ "# Environment files",
1792
+ ".env",
1793
+ ".env.*",
1794
+ "!.env.example",
1795
+ "",
1796
+ "# Private keys and certificates",
1797
+ "*.key",
1798
+ "*.pem",
1799
+ "*.p12",
1800
+ "*.pfx",
1801
+ "*.der",
1802
+ "*.crt",
1803
+ "",
1804
+ "# SSH keys",
1805
+ "id_rsa*",
1806
+ "id_dsa*",
1807
+ "*.ppk",
1808
+ "",
1809
+ "# API keys and secrets",
1810
+ "api-keys.json",
1811
+ "credentials.json",
1812
+ "secrets.json",
1813
+ "**/apikeys/",
1814
+ "**/*_token*",
1815
+ "**/*_secret*",
1816
+ "**/*api_key*",
1817
+ "",
1818
+ "# Cloud provider credentials",
1819
+ "aws-credentials.json",
1820
+ "gcp-service-account*.json",
1821
+ "azure-credentials.json",
1822
+ ".aws/",
1823
+ "",
1824
+ "# \u2500\u2500\u2500\u2500\u2500 Database & Infrastructure Secrets \u2500\u2500\u2500\u2500\u2500",
1825
+ "# Database configuration",
1826
+ "*.db",
1827
+ "*.sqlite",
1828
+ "*.sqlite3",
1829
+ "database.yml",
1830
+ "**/database/config.*",
1831
+ "",
1832
+ "# Infrastructure as Code secrets",
1833
+ "*.tfstate",
1834
+ "*.tfstate.*",
1835
+ "terraform.tfvars",
1836
+ "secrets.auto.tfvars",
1837
+ ".terraform/",
1838
+ "",
1839
+ "# Kubernetes secrets",
1840
+ "**/k8s/**/secret*.yaml",
1841
+ "**/kubernetes/**/secret*.yaml",
1842
+ "",
1843
+ "# \u2500\u2500\u2500\u2500\u2500 Business Sensitive Data \u2500\u2500\u2500\u2500\u2500",
1844
+ "# Sensitive directories",
1845
+ "secrets/",
1846
+ "private/",
1847
+ "confidential/",
1848
+ "internal-docs/",
1849
+ "company-secrets/",
1850
+ "",
1851
+ "# Customer and personal data",
1852
+ "customer-data/",
1853
+ "pii/",
1854
+ "personal-data/",
1855
+ "**/*customer*.csv",
1856
+ "**/*personal*.json",
1857
+ "",
1858
+ "# \u2500\u2500\u2500\u2500\u2500 Build Artifacts & Large Files \u2500\u2500\u2500\u2500\u2500",
1859
+ "# Build outputs (may contain embedded secrets)",
1860
+ "dist/",
1861
+ "build/",
1862
+ "out/",
1863
+ ".next/",
1864
+ ".nuxt/",
1865
+ "",
1866
+ "# Large files that slow down AI processing",
1867
+ "*.zip",
1868
+ "*.tar.gz",
1869
+ "*.rar",
1870
+ "**/*.{mp4,avi,mov,mkv}",
1871
+ "**/*.{pdf,doc,docx}",
1872
+ "",
1873
+ "# Data files",
1874
+ "*.csv",
1875
+ "*.tsv",
1876
+ "*.xlsx",
1877
+ "data/",
1878
+ "datasets/",
1879
+ "",
1880
+ "# \u2500\u2500\u2500\u2500\u2500 Development Environment \u2500\u2500\u2500\u2500\u2500",
1881
+ "# Personal IDE settings",
1882
+ ".vscode/settings.json",
1883
+ ".idea/workspace.xml",
1884
+ "",
1885
+ "# Temporary files",
1886
+ "*.swp",
1887
+ "*.swo",
1888
+ "*~",
1889
+ "*.tmp",
1890
+ "",
1891
+ "# Test data with sensitive content",
1892
+ "test-data/sensitive/",
1893
+ "tests/fixtures/real-data/**",
1894
+ "",
1895
+ "# \u2500\u2500\u2500\u2500\u2500 Logs & Runtime Data \u2500\u2500\u2500\u2500\u2500",
1896
+ "*.log",
1897
+ "logs/",
1898
+ ".cache/",
1899
+ "",
1900
+ "# \u2500\u2500\u2500\u2500\u2500 Re-include Important Files \u2500\u2500\u2500\u2500\u2500",
1901
+ "# Allow configuration examples and documentation",
1902
+ "!secrets/README.md",
1903
+ "!config/*.example.*",
1904
+ "!docs/**/*.md"
1905
+ ],
1906
+ includeCommonPatterns: false,
1907
+ projectPatternsHeader: "# \u2500\u2500\u2500\u2500\u2500 Project-specific patterns from rulesync rules \u2500\u2500\u2500\u2500\u2500"
1675
1908
  }
1676
1909
  };
1677
1910
 
@@ -1749,7 +1982,7 @@ function filterIgnoredFiles(files, ignorePatterns) {
1749
1982
 
1750
1983
  // src/generators/rules/shared-helpers.ts
1751
1984
  function resolveOutputDir(config, tool, baseDir) {
1752
- return baseDir ? (0, import_node_path5.join)(baseDir, config.outputPaths[tool]) : config.outputPaths[tool];
1985
+ return resolvePath(config.outputPaths[tool], baseDir);
1753
1986
  }
1754
1987
  function createOutputsArray() {
1755
1988
  return [];
@@ -1776,7 +2009,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
1776
2009
  }
1777
2010
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1778
2011
  if (ignorePatterns.patterns.length > 0) {
1779
- const ignorePath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
2012
+ const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
1780
2013
  const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1781
2014
  outputs.push({
1782
2015
  tool: generatorConfig.tool,
@@ -1794,7 +2027,10 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1794
2027
  if (generatorConfig.generateDetailContent && generatorConfig.detailSubDir) {
1795
2028
  for (const rule of detailRules) {
1796
2029
  const content = generatorConfig.generateDetailContent(rule);
1797
- const filepath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.detailSubDir, `${rule.filename}.md`) : (0, import_node_path5.join)(generatorConfig.detailSubDir, `${rule.filename}.md`);
2030
+ const filepath = resolvePath(
2031
+ (0, import_node_path5.join)(generatorConfig.detailSubDir, `${rule.filename}.md`),
2032
+ baseDir
2033
+ );
1798
2034
  outputs.push({
1799
2035
  tool: generatorConfig.tool,
1800
2036
  filepath,
@@ -1804,7 +2040,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1804
2040
  }
1805
2041
  if (generatorConfig.generateRootContent && generatorConfig.rootFilePath) {
1806
2042
  const rootContent = generatorConfig.generateRootContent(rootRule, detailRules, baseDir);
1807
- const rootFilepath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.rootFilePath) : generatorConfig.rootFilePath;
2043
+ const rootFilepath = resolvePath(generatorConfig.rootFilePath, baseDir);
1808
2044
  outputs.push({
1809
2045
  tool: generatorConfig.tool,
1810
2046
  filepath: rootFilepath,
@@ -1813,7 +2049,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1813
2049
  }
1814
2050
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1815
2051
  if (ignorePatterns.patterns.length > 0) {
1816
- const ignorePath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
2052
+ const ignorePath = resolvePath(generatorConfig.ignoreFileName, baseDir);
1817
2053
  const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1818
2054
  outputs.push({
1819
2055
  tool: generatorConfig.tool,
@@ -1918,30 +2154,22 @@ function generateLegacyGuidelinesFile(allRules) {
1918
2154
  // src/generators/rules/claudecode.ts
1919
2155
  var import_node_path7 = require("path");
1920
2156
  async function generateClaudecodeConfig(rules, config, baseDir) {
1921
- const outputs = [];
1922
- const rootRules = rules.filter((r) => r.frontmatter.root === true);
1923
- const detailRules = rules.filter((r) => r.frontmatter.root === false);
1924
- const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
1925
- const claudeOutputDir = baseDir ? (0, import_node_path7.join)(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
1926
- outputs.push({
2157
+ const generatorConfig = {
1927
2158
  tool: "claudecode",
1928
- filepath: (0, import_node_path7.join)(claudeOutputDir, "CLAUDE.md"),
1929
- content: claudeMdContent
1930
- });
1931
- for (const rule of detailRules) {
1932
- const memoryContent = generateMemoryFile(rule);
1933
- outputs.push({
1934
- tool: "claudecode",
1935
- filepath: (0, import_node_path7.join)(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
1936
- content: memoryContent
1937
- });
1938
- }
1939
- const ignorePatterns = await loadIgnorePatterns(baseDir);
1940
- if (ignorePatterns.patterns.length > 0) {
1941
- const settingsPath = baseDir ? (0, import_node_path7.join)(baseDir, ".claude", "settings.json") : (0, import_node_path7.join)(".claude", "settings.json");
1942
- await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
1943
- }
1944
- return outputs;
2159
+ fileExtension: ".md",
2160
+ ignoreFileName: ".aiignore",
2161
+ generateContent: generateMemoryFile,
2162
+ generateRootContent: (rootRule, detailRules) => generateClaudeMarkdown(rootRule ? [rootRule] : [], detailRules),
2163
+ rootFilePath: "CLAUDE.md",
2164
+ generateDetailContent: generateMemoryFile,
2165
+ detailSubDir: ".claude/memories",
2166
+ updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
2167
+ const settingsPath = resolvePath((0, import_node_path7.join)(".claude", "settings.json"), baseDir2);
2168
+ await updateClaudeSettings(settingsPath, ignorePatterns);
2169
+ return [];
2170
+ }
2171
+ };
2172
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
1945
2173
  }
1946
2174
  function generateClaudeMarkdown(rootRules, detailRules) {
1947
2175
  const lines = [];
@@ -2001,23 +2229,222 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
2001
2229
  console.log(`\u2705 Updated Claude Code settings: ${settingsPath}`);
2002
2230
  }
2003
2231
 
2232
+ // src/generators/rules/generator-registry.ts
2233
+ var import_node_path8 = require("path");
2234
+ var GENERATOR_REGISTRY = {
2235
+ // Simple generators - generate one file per rule
2236
+ cline: {
2237
+ type: "simple",
2238
+ tool: "cline",
2239
+ fileExtension: ".md",
2240
+ ignoreFileName: ".clineignore",
2241
+ generateContent: (rule) => rule.content.trim()
2242
+ },
2243
+ roo: {
2244
+ type: "simple",
2245
+ tool: "roo",
2246
+ fileExtension: ".md",
2247
+ ignoreFileName: ".rooignore",
2248
+ generateContent: (rule) => rule.content.trim()
2249
+ },
2250
+ kiro: {
2251
+ type: "simple",
2252
+ tool: "kiro",
2253
+ fileExtension: ".md",
2254
+ ignoreFileName: ".kiroignore",
2255
+ generateContent: (rule) => rule.content.trim()
2256
+ },
2257
+ augmentcode: {
2258
+ type: "simple",
2259
+ tool: "augmentcode",
2260
+ fileExtension: ".md",
2261
+ ignoreFileName: ".aiignore",
2262
+ generateContent: (rule) => rule.content.trim()
2263
+ },
2264
+ "augmentcode-legacy": {
2265
+ type: "simple",
2266
+ tool: "augmentcode-legacy",
2267
+ fileExtension: ".md",
2268
+ ignoreFileName: ".aiignore",
2269
+ generateContent: (rule) => rule.content.trim()
2270
+ },
2271
+ // Complex generators with custom content formatting
2272
+ copilot: {
2273
+ type: "simple",
2274
+ tool: "copilot",
2275
+ fileExtension: ".instructions.md",
2276
+ ignoreFileName: ".copilotignore",
2277
+ generateContent: (rule) => {
2278
+ const lines = [];
2279
+ lines.push("---");
2280
+ lines.push(`description: "${rule.frontmatter.description}"`);
2281
+ if (rule.frontmatter.globs.length > 0) {
2282
+ lines.push(`applyTo: "${rule.frontmatter.globs.join(", ")}"`);
2283
+ } else {
2284
+ lines.push('applyTo: "**"');
2285
+ }
2286
+ lines.push("---");
2287
+ lines.push(rule.content);
2288
+ return lines.join("\n");
2289
+ },
2290
+ pathResolver: (rule, outputDir) => {
2291
+ const baseFilename = rule.filename.replace(/\.md$/, "");
2292
+ return (0, import_node_path8.join)(outputDir, `${baseFilename}.instructions.md`);
2293
+ }
2294
+ },
2295
+ cursor: {
2296
+ type: "simple",
2297
+ tool: "cursor",
2298
+ fileExtension: ".md",
2299
+ ignoreFileName: ".cursorignore",
2300
+ generateContent: (rule) => rule.content.trim()
2301
+ },
2302
+ codexcli: {
2303
+ type: "simple",
2304
+ tool: "codexcli",
2305
+ fileExtension: ".md",
2306
+ ignoreFileName: ".codexignore",
2307
+ generateContent: (rule) => rule.content.trim()
2308
+ },
2309
+ // Complex generators with root + detail pattern
2310
+ claudecode: {
2311
+ type: "complex",
2312
+ tool: "claudecode",
2313
+ fileExtension: ".md",
2314
+ ignoreFileName: ".aiignore",
2315
+ generateContent: (rule) => {
2316
+ const lines = [];
2317
+ if (rule.frontmatter.description) {
2318
+ lines.push(`# ${rule.frontmatter.description}
2319
+ `);
2320
+ }
2321
+ lines.push(rule.content.trim());
2322
+ return lines.join("\n");
2323
+ }
2324
+ // NOTE: Claude Code specific logic is handled in the actual generator file
2325
+ // due to complex settings.json manipulation requirements
2326
+ },
2327
+ geminicli: {
2328
+ type: "complex",
2329
+ tool: "geminicli",
2330
+ fileExtension: ".md",
2331
+ ignoreFileName: ".aiexclude",
2332
+ generateContent: (rule) => {
2333
+ const lines = [];
2334
+ if (rule.frontmatter.description) {
2335
+ lines.push(`# ${rule.frontmatter.description}
2336
+ `);
2337
+ }
2338
+ lines.push(rule.content.trim());
2339
+ return lines.join("\n");
2340
+ }
2341
+ // Complex generation handled by existing generator
2342
+ },
2343
+ junie: {
2344
+ type: "complex",
2345
+ tool: "junie",
2346
+ fileExtension: ".md",
2347
+ ignoreFileName: ".aiignore",
2348
+ generateContent: (rule) => {
2349
+ const lines = [];
2350
+ if (rule.frontmatter.description) {
2351
+ lines.push(`# ${rule.frontmatter.description}
2352
+ `);
2353
+ }
2354
+ lines.push(rule.content.trim());
2355
+ return lines.join("\n");
2356
+ }
2357
+ // Complex generation handled by existing generator
2358
+ }
2359
+ };
2360
+ async function generateFromRegistry(tool, rules, config, baseDir) {
2361
+ const generatorConfig = GENERATOR_REGISTRY[tool];
2362
+ if (!generatorConfig) {
2363
+ throw new Error(`No generator configuration found for tool: ${tool}`);
2364
+ }
2365
+ if (generatorConfig.type === "simple") {
2366
+ const ruleConfig = {
2367
+ tool: generatorConfig.tool,
2368
+ fileExtension: generatorConfig.fileExtension,
2369
+ ignoreFileName: generatorConfig.ignoreFileName,
2370
+ generateContent: generatorConfig.generateContent,
2371
+ ...generatorConfig.pathResolver && { pathResolver: generatorConfig.pathResolver }
2372
+ };
2373
+ return generateRulesConfig(rules, config, ruleConfig, baseDir);
2374
+ } else {
2375
+ const enhancedConfig = {
2376
+ tool: generatorConfig.tool,
2377
+ fileExtension: generatorConfig.fileExtension,
2378
+ ignoreFileName: generatorConfig.ignoreFileName,
2379
+ generateContent: generatorConfig.generateContent,
2380
+ ...generatorConfig.generateRootContent && {
2381
+ generateRootContent: generatorConfig.generateRootContent
2382
+ },
2383
+ ...generatorConfig.rootFilePath && { rootFilePath: generatorConfig.rootFilePath },
2384
+ ...generatorConfig.generateDetailContent && {
2385
+ generateDetailContent: generatorConfig.generateDetailContent
2386
+ },
2387
+ ...generatorConfig.detailSubDir && { detailSubDir: generatorConfig.detailSubDir },
2388
+ ...generatorConfig.updateAdditionalConfig && {
2389
+ updateAdditionalConfig: generatorConfig.updateAdditionalConfig
2390
+ }
2391
+ };
2392
+ return generateComplexRules(rules, config, enhancedConfig, baseDir);
2393
+ }
2394
+ }
2395
+
2004
2396
  // src/generators/rules/cline.ts
2005
2397
  async function generateClineConfig(rules, config, baseDir) {
2006
- return generateRulesConfig(
2007
- rules,
2008
- config,
2009
- {
2010
- tool: "cline",
2011
- fileExtension: ".md",
2012
- ignoreFileName: ".clineignore",
2013
- generateContent: (rule) => rule.content.trim()
2014
- },
2015
- baseDir
2016
- );
2398
+ return generateFromRegistry("cline", rules, config, baseDir);
2399
+ }
2400
+
2401
+ // src/generators/rules/codexcli.ts
2402
+ async function generateCodexConfig(rules, config, baseDir) {
2403
+ const outputs = [];
2404
+ if (rules.length === 0) {
2405
+ return outputs;
2406
+ }
2407
+ const sortedRules = [...rules].sort((a, b) => {
2408
+ if (a.frontmatter.root === true && b.frontmatter.root !== true) return -1;
2409
+ if (a.frontmatter.root !== true && b.frontmatter.root === true) return 1;
2410
+ return 0;
2411
+ });
2412
+ const concatenatedContent = generateConcatenatedCodexContent(sortedRules);
2413
+ if (concatenatedContent.trim()) {
2414
+ const outputDir = resolveOutputDir(config, "codexcli", baseDir);
2415
+ const filepath = `${outputDir}/codex.md`;
2416
+ outputs.push({
2417
+ tool: "codexcli",
2418
+ filepath,
2419
+ content: concatenatedContent
2420
+ });
2421
+ }
2422
+ const ignorePatterns = await loadIgnorePatterns(baseDir);
2423
+ if (ignorePatterns.patterns.length > 0) {
2424
+ const ignorePath = resolvePath(".codexignore", baseDir);
2425
+ const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
2426
+ outputs.push({
2427
+ tool: "codexcli",
2428
+ filepath: ignorePath,
2429
+ content: ignoreContent
2430
+ });
2431
+ }
2432
+ return outputs;
2433
+ }
2434
+ function generateConcatenatedCodexContent(rules) {
2435
+ const sections = [];
2436
+ for (const rule of rules) {
2437
+ const content = rule.content.trim();
2438
+ if (!content) {
2439
+ continue;
2440
+ }
2441
+ sections.push(content);
2442
+ }
2443
+ return sections.join("\n\n---\n\n");
2017
2444
  }
2018
2445
 
2019
2446
  // src/generators/rules/copilot.ts
2020
- var import_node_path8 = require("path");
2447
+ var import_node_path9 = require("path");
2021
2448
  async function generateCopilotConfig(rules, config, baseDir) {
2022
2449
  return generateComplexRulesConfig(
2023
2450
  rules,
@@ -2029,7 +2456,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
2029
2456
  generateContent: generateCopilotMarkdown,
2030
2457
  getOutputPath: (rule, outputDir) => {
2031
2458
  const baseFilename = rule.filename.replace(/\.md$/, "");
2032
- return (0, import_node_path8.join)(outputDir, `${baseFilename}.instructions.md`);
2459
+ return (0, import_node_path9.join)(outputDir, `${baseFilename}.instructions.md`);
2033
2460
  }
2034
2461
  },
2035
2462
  baseDir
@@ -2050,7 +2477,7 @@ function generateCopilotMarkdown(rule) {
2050
2477
  }
2051
2478
 
2052
2479
  // src/generators/rules/cursor.ts
2053
- var import_node_path9 = require("path");
2480
+ var import_node_path10 = require("path");
2054
2481
  async function generateCursorConfig(rules, config, baseDir) {
2055
2482
  return generateComplexRulesConfig(
2056
2483
  rules,
@@ -2061,7 +2488,7 @@ async function generateCursorConfig(rules, config, baseDir) {
2061
2488
  ignoreFileName: ".cursorignore",
2062
2489
  generateContent: generateCursorMarkdown,
2063
2490
  getOutputPath: (rule, outputDir) => {
2064
- return (0, import_node_path9.join)(outputDir, `${rule.filename}.mdc`);
2491
+ return (0, import_node_path10.join)(outputDir, `${rule.filename}.mdc`);
2065
2492
  }
2066
2493
  },
2067
2494
  baseDir
@@ -2190,38 +2617,13 @@ function generateGuidelinesMarkdown(rootRule, detailRules) {
2190
2617
  }
2191
2618
 
2192
2619
  // src/generators/rules/kiro.ts
2193
- var import_node_path10 = require("path");
2194
2620
  async function generateKiroConfig(rules, config, baseDir) {
2195
- const outputs = [];
2196
- for (const rule of rules) {
2197
- const content = generateKiroMarkdown(rule);
2198
- const outputDir = baseDir ? (0, import_node_path10.join)(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
2199
- const filepath = (0, import_node_path10.join)(outputDir, `${rule.filename}.md`);
2200
- outputs.push({
2201
- tool: "kiro",
2202
- filepath,
2203
- content
2204
- });
2205
- }
2206
- return outputs;
2207
- }
2208
- function generateKiroMarkdown(rule) {
2209
- return rule.content.trim();
2621
+ return generateFromRegistry("kiro", rules, config, baseDir);
2210
2622
  }
2211
2623
 
2212
2624
  // src/generators/rules/roo.ts
2213
2625
  async function generateRooConfig(rules, config, baseDir) {
2214
- return generateRulesConfig(
2215
- rules,
2216
- config,
2217
- {
2218
- tool: "roo",
2219
- fileExtension: ".md",
2220
- ignoreFileName: ".rooignore",
2221
- generateContent: (rule) => rule.content.trim()
2222
- },
2223
- baseDir
2224
- );
2626
+ return generateFromRegistry("roo", rules, config, baseDir);
2225
2627
  }
2226
2628
 
2227
2629
  // src/core/generator.ts
@@ -2280,6 +2682,8 @@ async function generateForTool(tool, rules, config, baseDir) {
2280
2682
  return generateClineConfig(rules, config, baseDir);
2281
2683
  case "claudecode":
2282
2684
  return await generateClaudecodeConfig(rules, config, baseDir);
2685
+ case "codexcli":
2686
+ return generateCodexConfig(rules, config, baseDir);
2283
2687
  case "roo":
2284
2688
  return generateRooConfig(rules, config, baseDir);
2285
2689
  case "geminicli":
@@ -2410,6 +2814,7 @@ var path4 = __toESM(require("path"), 1);
2410
2814
  init_augmentcode();
2411
2815
  init_claudecode();
2412
2816
  init_cline();
2817
+ init_codexcli();
2413
2818
  init_copilot();
2414
2819
  init_cursor();
2415
2820
  init_geminicli();
@@ -2483,6 +2888,11 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
2483
2888
  path: path4.join(targetRoot, ".cline", "mcp.json"),
2484
2889
  generate: () => generateClineMcp(config)
2485
2890
  },
2891
+ {
2892
+ tool: "codexcli-project",
2893
+ path: path4.join(targetRoot, ".codex", "mcp-config.json"),
2894
+ generate: () => generateCodexMcp(config)
2895
+ },
2486
2896
  {
2487
2897
  tool: "gemini-project",
2488
2898
  path: path4.join(targetRoot, ".gemini", "settings.json"),
@@ -2518,7 +2928,7 @@ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
2518
2928
  try {
2519
2929
  const content = generator.generate();
2520
2930
  const parsed = JSON.parse(content);
2521
- 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")) {
2931
+ 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")) {
2522
2932
  if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
2523
2933
  results.push({
2524
2934
  tool: generator.tool,
@@ -2743,6 +3153,8 @@ var gitignoreCommand = async () => {
2743
3153
  "**/.clineignore",
2744
3154
  "**/CLAUDE.md",
2745
3155
  "**/.claude/memories/",
3156
+ "**/codex.md",
3157
+ "**/.codexignore",
2746
3158
  "**/.roo/rules/",
2747
3159
  "**/.rooignore",
2748
3160
  "**/.copilotignore",
@@ -2761,6 +3173,7 @@ var gitignoreCommand = async () => {
2761
3173
  "**/.cursor/mcp.json",
2762
3174
  "**/.cline/mcp.json",
2763
3175
  "**/.vscode/mcp.json",
3176
+ "**/.codex/mcp-config.json",
2764
3177
  "**/.gemini/settings.json",
2765
3178
  "**/.roo/mcp.json"
2766
3179
  ];
@@ -2819,36 +3232,63 @@ function addRules(result, rules) {
2819
3232
  }
2820
3233
  result.rules.push(...rules);
2821
3234
  }
2822
- function handleParseError(error, context) {
2823
- const errorMessage = error instanceof Error ? error.message : String(error);
2824
- return `${context}: ${errorMessage}`;
2825
- }
2826
3235
  async function safeReadFile(operation, errorContext) {
2827
3236
  try {
2828
3237
  const result = await operation();
2829
- return { success: true, result };
3238
+ return createSuccessResult(result);
2830
3239
  } catch (error) {
2831
- return {
2832
- success: false,
2833
- error: handleParseError(error, errorContext)
2834
- };
3240
+ return createErrorResult(error, errorContext);
2835
3241
  }
2836
3242
  }
2837
3243
 
2838
3244
  // src/parsers/augmentcode.ts
2839
3245
  async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
3246
+ return parseUnifiedAugmentcode(baseDir, {
3247
+ rulesDir: ".augment/rules",
3248
+ targetName: "augmentcode",
3249
+ filenamePrefix: "augmentcode"
3250
+ });
3251
+ }
3252
+ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
3253
+ return parseUnifiedAugmentcode(baseDir, {
3254
+ legacyFilePath: ".augment-guidelines",
3255
+ targetName: "augmentcode-legacy",
3256
+ filenamePrefix: "augmentcode-legacy"
3257
+ });
3258
+ }
3259
+ async function parseUnifiedAugmentcode(baseDir, config) {
2840
3260
  const result = createParseResult();
2841
- const rulesDir = (0, import_node_path14.join)(baseDir, ".augment", "rules");
2842
- if (await fileExists(rulesDir)) {
2843
- const rulesResult = await parseAugmentRules(rulesDir);
2844
- addRules(result, rulesResult.rules);
2845
- result.errors.push(...rulesResult.errors);
2846
- } else {
2847
- addError(result, "No AugmentCode configuration found. Expected .augment/rules/ directory.");
3261
+ if (config.rulesDir) {
3262
+ const rulesDir = (0, import_node_path14.join)(baseDir, config.rulesDir);
3263
+ if (await fileExists(rulesDir)) {
3264
+ const rulesResult = await parseAugmentRules(rulesDir, config);
3265
+ addRules(result, rulesResult.rules);
3266
+ result.errors.push(...rulesResult.errors);
3267
+ } else {
3268
+ addError(
3269
+ result,
3270
+ `No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
3271
+ );
3272
+ }
3273
+ }
3274
+ if (config.legacyFilePath) {
3275
+ const legacyPath = (0, import_node_path14.join)(baseDir, config.legacyFilePath);
3276
+ if (await fileExists(legacyPath)) {
3277
+ const legacyResult = await parseAugmentGuidelines(legacyPath, config);
3278
+ if (legacyResult.rule) {
3279
+ addRule(result, legacyResult.rule);
3280
+ }
3281
+ result.errors.push(...legacyResult.errors);
3282
+ } else {
3283
+ addError(
3284
+ result,
3285
+ `No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
3286
+ );
3287
+ }
2848
3288
  }
2849
3289
  return { rules: result.rules || [], errors: result.errors };
2850
3290
  }
2851
- async function parseAugmentRules(rulesDir) {
3291
+ async function parseAugmentRules(rulesDir, config) {
2852
3292
  const rules = [];
2853
3293
  const errors = [];
2854
3294
  try {
@@ -2868,7 +3308,7 @@ async function parseAugmentRules(rulesDir) {
2868
3308
  const filename = (0, import_node_path14.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
2869
3309
  const frontmatter = {
2870
3310
  root: isRoot,
2871
- targets: ["augmentcode"],
3311
+ targets: [config.targetName],
2872
3312
  description,
2873
3313
  globs: ["**/*"],
2874
3314
  // AugmentCode doesn't use specific globs in the same way
@@ -2877,7 +3317,7 @@ async function parseAugmentRules(rulesDir) {
2877
3317
  rules.push({
2878
3318
  frontmatter,
2879
3319
  content: parsed.content.trim(),
2880
- filename: `augmentcode-${ruleType}-${filename}`,
3320
+ filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
2881
3321
  filepath: filePath
2882
3322
  });
2883
3323
  } catch (error) {
@@ -2888,50 +3328,33 @@ async function parseAugmentRules(rulesDir) {
2888
3328
  }
2889
3329
  } catch (error) {
2890
3330
  const errorMessage = error instanceof Error ? error.message : String(error);
2891
- errors.push(`Failed to read .augment/rules/ directory: ${errorMessage}`);
3331
+ errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
2892
3332
  }
2893
3333
  return { rules, errors };
2894
3334
  }
2895
-
2896
- // src/parsers/augmentcode-legacy.ts
2897
- var import_node_path15 = require("path");
2898
- async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
2899
- const result = createParseResult();
2900
- const guidelinesPath = (0, import_node_path15.join)(baseDir, ".augment-guidelines");
2901
- if (await fileExists(guidelinesPath)) {
2902
- const guidelinesResult = await parseAugmentGuidelines(guidelinesPath);
2903
- if (guidelinesResult.rule) {
2904
- addRule(result, guidelinesResult.rule);
2905
- }
2906
- result.errors.push(...guidelinesResult.errors);
2907
- } else {
2908
- addError(
2909
- result,
2910
- "No AugmentCode legacy configuration found. Expected .augment-guidelines file."
2911
- );
2912
- }
2913
- return { rules: result.rules || [], errors: result.errors };
2914
- }
2915
- async function parseAugmentGuidelines(guidelinesPath) {
2916
- const parseResult = await safeReadFile(async () => {
2917
- const content = await readFileContent(guidelinesPath);
2918
- if (content.trim()) {
2919
- const frontmatter = {
2920
- root: true,
2921
- // Legacy guidelines become root rules
2922
- targets: ["augmentcode-legacy"],
2923
- description: "Legacy AugmentCode guidelines",
2924
- globs: ["**/*"]
2925
- };
2926
- return {
2927
- frontmatter,
2928
- content: content.trim(),
2929
- filename: "augmentcode-legacy-guidelines",
2930
- filepath: guidelinesPath
2931
- };
2932
- }
2933
- return null;
2934
- }, "Failed to parse .augment-guidelines");
3335
+ async function parseAugmentGuidelines(guidelinesPath, config) {
3336
+ const parseResult = await safeReadFile(
3337
+ async () => {
3338
+ const content = await readFileContent(guidelinesPath);
3339
+ if (content.trim()) {
3340
+ const frontmatter = {
3341
+ root: true,
3342
+ // Legacy guidelines become root rules
3343
+ targets: [config.targetName],
3344
+ description: "Legacy AugmentCode guidelines",
3345
+ globs: ["**/*"]
3346
+ };
3347
+ return {
3348
+ frontmatter,
3349
+ content: content.trim(),
3350
+ filename: `${config.filenamePrefix}-guidelines`,
3351
+ filepath: guidelinesPath
3352
+ };
3353
+ }
3354
+ return null;
3355
+ },
3356
+ `Failed to parse ${config.legacyFilePath || guidelinesPath}`
3357
+ );
2935
3358
  if (parseResult.success) {
2936
3359
  return { rule: parseResult.result || null, errors: [] };
2937
3360
  } else {
@@ -2940,33 +3363,36 @@ async function parseAugmentGuidelines(guidelinesPath) {
2940
3363
  }
2941
3364
 
2942
3365
  // src/parsers/shared-helpers.ts
2943
- var import_node_path16 = require("path");
3366
+ var import_node_path15 = require("path");
2944
3367
  var import_gray_matter3 = __toESM(require("gray-matter"), 1);
2945
3368
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2946
3369
  const errors = [];
2947
3370
  const rules = [];
2948
3371
  if (config.mainFile) {
2949
- const mainFilePath = (0, import_node_path16.join)(baseDir, config.mainFile.path);
3372
+ const mainFile = config.mainFile;
3373
+ const mainFilePath = resolvePath(mainFile.path, baseDir);
2950
3374
  if (await fileExists(mainFilePath)) {
2951
- try {
3375
+ const result = await safeAsyncOperation(async () => {
2952
3376
  const rawContent = await readFileContent(mainFilePath);
2953
3377
  let content;
2954
3378
  let frontmatter;
2955
- if (config.mainFile.useFrontmatter) {
3379
+ if (mainFile.useFrontmatter) {
2956
3380
  const parsed = (0, import_gray_matter3.default)(rawContent);
2957
3381
  content = parsed.content.trim();
3382
+ const parsedFrontmatter = parsed.data;
2958
3383
  frontmatter = {
2959
- root: false,
3384
+ root: mainFile.isRoot ?? false,
2960
3385
  targets: [config.tool],
2961
- description: config.mainFile.description,
2962
- globs: ["**/*"]
3386
+ description: parsedFrontmatter.description || mainFile.description,
3387
+ globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
3388
+ ...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
2963
3389
  };
2964
3390
  } else {
2965
3391
  content = rawContent.trim();
2966
3392
  frontmatter = {
2967
- root: false,
3393
+ root: mainFile.isRoot ?? false,
2968
3394
  targets: [config.tool],
2969
- description: config.mainFile.description,
3395
+ description: mainFile.description,
2970
3396
  globs: ["**/*"]
2971
3397
  };
2972
3398
  }
@@ -2974,43 +3400,52 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2974
3400
  rules.push({
2975
3401
  frontmatter,
2976
3402
  content,
2977
- filename: "instructions",
3403
+ filename: mainFile.filenameOverride || "instructions",
2978
3404
  filepath: mainFilePath
2979
3405
  });
2980
3406
  }
2981
- } catch (error) {
2982
- const errorMessage = error instanceof Error ? error.message : String(error);
2983
- errors.push(`Failed to parse ${config.mainFile.path}: ${errorMessage}`);
3407
+ }, `Failed to parse ${mainFile.path}`);
3408
+ if (!result.success) {
3409
+ errors.push(result.error);
2984
3410
  }
2985
3411
  }
2986
3412
  }
2987
3413
  if (config.directories) {
2988
3414
  for (const dirConfig of config.directories) {
2989
- const dirPath = (0, import_node_path16.join)(baseDir, dirConfig.directory);
3415
+ const dirPath = resolvePath(dirConfig.directory, baseDir);
2990
3416
  if (await fileExists(dirPath)) {
2991
- try {
3417
+ const result = await safeAsyncOperation(async () => {
2992
3418
  const { readdir: readdir2 } = await import("fs/promises");
2993
3419
  const files = await readdir2(dirPath);
2994
3420
  for (const file of files) {
2995
3421
  if (file.endsWith(dirConfig.filePattern)) {
2996
- const filePath = (0, import_node_path16.join)(dirPath, file);
2997
- try {
3422
+ const filePath = (0, import_node_path15.join)(dirPath, file);
3423
+ const fileResult = await safeAsyncOperation(async () => {
2998
3424
  const rawContent = await readFileContent(filePath);
2999
3425
  let content;
3426
+ let frontmatter;
3427
+ const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
3000
3428
  if (dirConfig.filePattern === ".instructions.md") {
3001
3429
  const parsed = (0, import_gray_matter3.default)(rawContent);
3002
3430
  content = parsed.content.trim();
3431
+ const parsedFrontmatter = parsed.data;
3432
+ frontmatter = {
3433
+ root: false,
3434
+ targets: [config.tool],
3435
+ description: parsedFrontmatter.description || `${dirConfig.description}: ${filename}`,
3436
+ globs: Array.isArray(parsedFrontmatter.globs) ? parsedFrontmatter.globs : ["**/*"],
3437
+ ...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
3438
+ };
3003
3439
  } else {
3004
3440
  content = rawContent.trim();
3005
- }
3006
- if (content) {
3007
- const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
3008
- const frontmatter = {
3441
+ frontmatter = {
3009
3442
  root: false,
3010
3443
  targets: [config.tool],
3011
3444
  description: `${dirConfig.description}: ${filename}`,
3012
3445
  globs: ["**/*"]
3013
3446
  };
3447
+ }
3448
+ if (content) {
3014
3449
  rules.push({
3015
3450
  frontmatter,
3016
3451
  content,
@@ -3018,15 +3453,15 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3018
3453
  filepath: filePath
3019
3454
  });
3020
3455
  }
3021
- } catch (error) {
3022
- const errorMessage = error instanceof Error ? error.message : String(error);
3023
- errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
3456
+ }, `Failed to parse ${filePath}`);
3457
+ if (!fileResult.success) {
3458
+ errors.push(fileResult.error);
3024
3459
  }
3025
3460
  }
3026
3461
  }
3027
- } catch (error) {
3028
- const errorMessage = error instanceof Error ? error.message : String(error);
3029
- errors.push(`Failed to parse ${dirConfig.directory} files: ${errorMessage}`);
3462
+ }, `Failed to parse ${dirConfig.directory} files`);
3463
+ if (!result.success) {
3464
+ errors.push(result.error);
3030
3465
  }
3031
3466
  }
3032
3467
  }
@@ -3041,7 +3476,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
3041
3476
  const rules = [];
3042
3477
  let ignorePatterns;
3043
3478
  let mcpServers;
3044
- const mainFilePath = (0, import_node_path16.join)(baseDir, config.mainFileName);
3479
+ const mainFilePath = resolvePath(config.mainFileName, baseDir);
3045
3480
  if (!await fileExists(mainFilePath)) {
3046
3481
  errors.push(`${config.mainFileName} file not found`);
3047
3482
  return { rules, errors };
@@ -3052,12 +3487,12 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
3052
3487
  if (mainRule) {
3053
3488
  rules.push(mainRule);
3054
3489
  }
3055
- const memoryDir = (0, import_node_path16.join)(baseDir, config.memoryDirPath);
3490
+ const memoryDir = resolvePath(config.memoryDirPath, baseDir);
3056
3491
  if (await fileExists(memoryDir)) {
3057
3492
  const memoryRules = await parseMemoryFiles(memoryDir, config);
3058
3493
  rules.push(...memoryRules);
3059
3494
  }
3060
- const settingsPath = (0, import_node_path16.join)(baseDir, config.settingsPath);
3495
+ const settingsPath = resolvePath(config.settingsPath, baseDir);
3061
3496
  if (await fileExists(settingsPath)) {
3062
3497
  const settingsResult = await parseSettingsFile(settingsPath, config.tool);
3063
3498
  if (settingsResult.ignorePatterns) {
@@ -3069,7 +3504,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
3069
3504
  errors.push(...settingsResult.errors);
3070
3505
  }
3071
3506
  if (config.additionalIgnoreFile) {
3072
- const additionalIgnorePath = (0, import_node_path16.join)(baseDir, config.additionalIgnoreFile.path);
3507
+ const additionalIgnorePath = resolvePath(config.additionalIgnoreFile.path, baseDir);
3073
3508
  if (await fileExists(additionalIgnorePath)) {
3074
3509
  const additionalPatterns = await config.additionalIgnoreFile.parser(additionalIgnorePath);
3075
3510
  if (additionalPatterns.length > 0) {
@@ -3078,8 +3513,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
3078
3513
  }
3079
3514
  }
3080
3515
  } catch (error) {
3081
- const errorMessage = error instanceof Error ? error.message : String(error);
3082
- errors.push(`Failed to parse ${config.tool} configuration: ${errorMessage}`);
3516
+ errors.push(`Failed to parse ${config.tool} configuration: ${getErrorMessage(error)}`);
3083
3517
  }
3084
3518
  return {
3085
3519
  rules,
@@ -3123,10 +3557,10 @@ async function parseMemoryFiles(memoryDir, config) {
3123
3557
  const files = await readdir2(memoryDir);
3124
3558
  for (const file of files) {
3125
3559
  if (file.endsWith(".md")) {
3126
- const filePath = (0, import_node_path16.join)(memoryDir, file);
3560
+ const filePath = (0, import_node_path15.join)(memoryDir, file);
3127
3561
  const content = await readFileContent(filePath);
3128
3562
  if (content.trim()) {
3129
- const filename = (0, import_node_path16.basename)(file, ".md");
3563
+ const filename = (0, import_node_path15.basename)(file, ".md");
3130
3564
  const frontmatter = {
3131
3565
  root: false,
3132
3566
  targets: [config.tool],
@@ -3175,8 +3609,7 @@ async function parseSettingsFile(settingsPath, tool) {
3175
3609
  mcpServers = parseResult.data.mcpServers;
3176
3610
  }
3177
3611
  } catch (error) {
3178
- const errorMessage = error instanceof Error ? error.message : String(error);
3179
- errors.push(`Failed to parse settings.json: ${errorMessage}`);
3612
+ errors.push(`Failed to parse settings.json: ${getErrorMessage(error)}`);
3180
3613
  }
3181
3614
  return {
3182
3615
  errors,
@@ -3218,6 +3651,9 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
3218
3651
  });
3219
3652
  }
3220
3653
 
3654
+ // src/parsers/codexcli.ts
3655
+ var import_node_path16 = require("path");
3656
+
3221
3657
  // src/parsers/copilot.ts
3222
3658
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
3223
3659
  return parseConfigurationFiles(baseDir, {
@@ -3953,12 +4389,12 @@ async function watchCommand() {
3953
4389
 
3954
4390
  // src/cli/index.ts
3955
4391
  var program = new import_commander.Command();
3956
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.53.0");
4392
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.55.0");
3957
4393
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
3958
4394
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
3959
4395
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
3960
4396
  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);
3961
- 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(
4397
+ 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(
3962
4398
  "-b, --base-dir <paths>",
3963
4399
  "Base directories to generate files (comma-separated for multiple paths)"
3964
4400
  ).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
@@ -3968,6 +4404,7 @@ program.command("generate").description("Generate configuration files for AI too
3968
4404
  if (options.copilot) tools.push("copilot");
3969
4405
  if (options.cursor) tools.push("cursor");
3970
4406
  if (options.cline) tools.push("cline");
4407
+ if (options.codexcli) tools.push("codexcli");
3971
4408
  if (options.claudecode) tools.push("claudecode");
3972
4409
  if (options.roo) tools.push("roo");
3973
4410
  if (options.geminicli) tools.push("geminicli");