rulesync 1.2.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -16,7 +16,18 @@ var RulesyncFeaturesSchema = z.array(z.enum(ALL_FEATURES_WITH_WILDCARD));
16
16
 
17
17
  // src/utils/logger.ts
18
18
  import { consola } from "consola";
19
+
20
+ // src/utils/vitest.ts
19
21
  var isEnvTest = process.env.NODE_ENV === "test";
22
+ function getVitestWorkerId() {
23
+ const vitestWorkerId = process.env.VITEST_WORKER_ID;
24
+ if (!vitestWorkerId) {
25
+ throw new Error("VITEST_WORKER_ID is not set");
26
+ }
27
+ return vitestWorkerId;
28
+ }
29
+
30
+ // src/utils/logger.ts
20
31
  var Logger = class {
21
32
  _verbose = false;
22
33
  console = consola.withDefaults({
@@ -57,9 +68,17 @@ var Logger = class {
57
68
  };
58
69
  var logger = new Logger();
59
70
 
71
+ // src/cli/commands/generate.ts
72
+ import { intersection } from "es-toolkit";
73
+
74
+ // src/commands/commands-processor.ts
75
+ import { basename as basename10, join as join10 } from "path";
76
+ import { z as z8 } from "zod/mini";
77
+
60
78
  // src/utils/file.ts
61
79
  import { globSync } from "fs";
62
- import { mkdir, mkdtemp, readdir, readFile, rm, stat, writeFile } from "fs/promises";
80
+ import { mkdir, readdir, readFile, rm, stat, writeFile } from "fs/promises";
81
+ import os from "os";
63
82
  import { basename, dirname, join, relative, resolve } from "path";
64
83
  async function ensureDir(dirPath) {
65
84
  try {
@@ -112,47 +131,32 @@ async function removeFile(filepath) {
112
131
  logger.warn(`Failed to remove file ${filepath}:`, error);
113
132
  }
114
133
  }
115
-
116
- // src/cli/commands/config.ts
117
- async function configCommand(options) {
118
- if (options.init) {
119
- await initConfig();
120
- return;
134
+ function getHomeDirectory() {
135
+ if (isEnvTest) {
136
+ return join("./tmp", "tests", "home", getVitestWorkerId());
121
137
  }
122
- logger.info(`Please run \`rulesync config --init\` to create a new configuration file`);
138
+ return os.homedir();
123
139
  }
124
- async function initConfig() {
125
- logger.info("Initializing configuration...");
126
- if (await fileExists("rulesync.jsonc")) {
127
- logger.error("rulesync.jsonc already exists");
128
- process.exit(1);
140
+ function validateBaseDir(baseDir) {
141
+ if (baseDir.trim() === "") {
142
+ throw new Error("baseDir cannot be an empty string");
143
+ }
144
+ if (baseDir.includes("..")) {
145
+ throw new Error(`baseDir cannot contain directory traversal (..): ${baseDir}`);
146
+ }
147
+ if (baseDir.startsWith("/")) {
148
+ throw new Error(`baseDir must be a relative path. Absolute path not allowed: ${baseDir}`);
149
+ }
150
+ if (/^[a-zA-Z]:[/\\]/.test(baseDir)) {
151
+ throw new Error(`baseDir must be a relative path. Absolute path not allowed: ${baseDir}`);
152
+ }
153
+ const normalized = resolve(".", baseDir);
154
+ const rel = relative(".", normalized);
155
+ if (rel.startsWith("..")) {
156
+ throw new Error(`baseDir cannot contain directory traversal (..): ${baseDir}`);
129
157
  }
130
- await writeFileContent(
131
- "rulesync.jsonc",
132
- JSON.stringify(
133
- {
134
- targets: ["copilot", "cursor", "claudecode", "codexcli"],
135
- features: ["rules", "ignore", "mcp", "commands", "subagents"],
136
- baseDirs: ["."],
137
- delete: true,
138
- verbose: false,
139
- experimentalSimulateCommands: false,
140
- experimentalSimulateSubagents: false
141
- },
142
- null,
143
- 2
144
- )
145
- );
146
- logger.success("Configuration file created successfully!");
147
158
  }
148
159
 
149
- // src/cli/commands/generate.ts
150
- import { intersection } from "es-toolkit";
151
-
152
- // src/commands/commands-processor.ts
153
- import { basename as basename10, join as join10 } from "path";
154
- import { z as z8 } from "zod/mini";
155
-
156
160
  // src/types/feature-processor.ts
157
161
  var FeatureProcessor = class {
158
162
  baseDir;
@@ -240,7 +244,7 @@ import { basename as basename2, join as join2 } from "path";
240
244
  import { z as z3 } from "zod/mini";
241
245
 
242
246
  // src/types/ai-file.ts
243
- import path from "path";
247
+ import path, { relative as relative2, resolve as resolve2 } from "path";
244
248
  var AiFile = class {
245
249
  /**
246
250
  * @example "."
@@ -258,17 +262,23 @@ var AiFile = class {
258
262
  * Whole raw file content
259
263
  */
260
264
  fileContent;
265
+ /**
266
+ * @example true
267
+ */
268
+ global;
261
269
  constructor({
262
270
  baseDir = ".",
263
271
  relativeDirPath,
264
272
  relativeFilePath,
265
273
  fileContent,
266
- validate = true
274
+ validate = true,
275
+ global = false
267
276
  }) {
268
277
  this.baseDir = baseDir;
269
278
  this.relativeDirPath = relativeDirPath;
270
279
  this.relativeFilePath = relativeFilePath;
271
280
  this.fileContent = fileContent;
281
+ this.global = global;
272
282
  if (validate) {
273
283
  const result = this.validate();
274
284
  if (!result.success) {
@@ -289,7 +299,16 @@ var AiFile = class {
289
299
  return this.relativeFilePath;
290
300
  }
291
301
  getFilePath() {
292
- return path.join(this.baseDir, this.relativeDirPath, this.relativeFilePath);
302
+ const fullPath = path.join(this.baseDir, this.relativeDirPath, this.relativeFilePath);
303
+ const resolvedFull = resolve2(fullPath);
304
+ const resolvedBase = resolve2(this.baseDir);
305
+ const rel = relative2(resolvedBase, resolvedFull);
306
+ if (rel.startsWith("..") || path.isAbsolute(rel)) {
307
+ throw new Error(
308
+ `Path traversal detected: Final path escapes baseDir. baseDir="${this.baseDir}", relativeDirPath="${this.relativeDirPath}", relativeFilePath="${this.relativeFilePath}"`
309
+ );
310
+ }
311
+ return fullPath;
293
312
  }
294
313
  getFileContent() {
295
314
  return this.fileContent;
@@ -1319,6 +1338,7 @@ var CommandsProcessor = class extends FeatureProcessor {
1319
1338
  };
1320
1339
 
1321
1340
  // src/config/config-resolver.ts
1341
+ import { join as join11 } from "path";
1322
1342
  import { loadConfig } from "c12";
1323
1343
 
1324
1344
  // src/config/config.ts
@@ -1328,6 +1348,7 @@ var Config = class {
1328
1348
  features;
1329
1349
  verbose;
1330
1350
  delete;
1351
+ experimentalGlobal;
1331
1352
  experimentalSimulateCommands;
1332
1353
  experimentalSimulateSubagents;
1333
1354
  constructor({
@@ -1336,6 +1357,7 @@ var Config = class {
1336
1357
  features,
1337
1358
  verbose,
1338
1359
  delete: isDelete,
1360
+ experimentalGlobal,
1339
1361
  experimentalSimulateCommands,
1340
1362
  experimentalSimulateSubagents
1341
1363
  }) {
@@ -1344,6 +1366,7 @@ var Config = class {
1344
1366
  this.features = features;
1345
1367
  this.verbose = verbose;
1346
1368
  this.delete = isDelete;
1369
+ this.experimentalGlobal = experimentalGlobal;
1347
1370
  this.experimentalSimulateCommands = experimentalSimulateCommands;
1348
1371
  this.experimentalSimulateSubagents = experimentalSimulateSubagents;
1349
1372
  }
@@ -1368,6 +1391,9 @@ var Config = class {
1368
1391
  getDelete() {
1369
1392
  return this.delete;
1370
1393
  }
1394
+ getExperimentalGlobal() {
1395
+ return this.experimentalGlobal;
1396
+ }
1371
1397
  getExperimentalSimulateCommands() {
1372
1398
  return this.experimentalSimulateCommands;
1373
1399
  }
@@ -1384,6 +1410,7 @@ var defaults = {
1384
1410
  delete: false,
1385
1411
  baseDirs: ["."],
1386
1412
  configPath: "rulesync.jsonc",
1413
+ experimentalGlobal: false,
1387
1414
  experimentalSimulateCommands: false,
1388
1415
  experimentalSimulateSubagents: false
1389
1416
  };
@@ -1395,16 +1422,21 @@ var ConfigResolver = class {
1395
1422
  delete: isDelete,
1396
1423
  baseDirs,
1397
1424
  configPath = defaults.configPath,
1425
+ experimentalGlobal,
1398
1426
  experimentalSimulateCommands,
1399
1427
  experimentalSimulateSubagents
1400
1428
  }) {
1401
- if (!fileExists(configPath)) {
1429
+ if (!await fileExists(configPath)) {
1402
1430
  return new Config({
1403
1431
  targets: targets ?? defaults.targets,
1404
1432
  features: features ?? defaults.features,
1405
1433
  verbose: verbose ?? defaults.verbose,
1406
1434
  delete: isDelete ?? defaults.delete,
1407
- baseDirs: baseDirs ?? defaults.baseDirs,
1435
+ baseDirs: getBaseDirsInLightOfGlobal({
1436
+ baseDirs: baseDirs ?? defaults.baseDirs,
1437
+ global: experimentalGlobal ?? false
1438
+ }),
1439
+ experimentalGlobal: experimentalGlobal ?? defaults.experimentalGlobal,
1408
1440
  experimentalSimulateCommands: experimentalSimulateCommands ?? defaults.experimentalSimulateCommands,
1409
1441
  experimentalSimulateSubagents: experimentalSimulateSubagents ?? defaults.experimentalSimulateSubagents
1410
1442
  });
@@ -1426,19 +1458,38 @@ var ConfigResolver = class {
1426
1458
  features: features ?? configByFile.features ?? defaults.features,
1427
1459
  verbose: verbose ?? configByFile.verbose ?? defaults.verbose,
1428
1460
  delete: isDelete ?? configByFile.delete ?? defaults.delete,
1429
- baseDirs: baseDirs ?? configByFile.baseDirs ?? defaults.baseDirs,
1461
+ baseDirs: getBaseDirsInLightOfGlobal({
1462
+ baseDirs: baseDirs ?? configByFile.baseDirs ?? defaults.baseDirs,
1463
+ global: experimentalGlobal ?? false
1464
+ }),
1465
+ experimentalGlobal: experimentalGlobal ?? configByFile.experimentalGlobal ?? defaults.experimentalGlobal,
1430
1466
  experimentalSimulateCommands: experimentalSimulateCommands ?? configByFile.experimentalSimulateCommands ?? defaults.experimentalSimulateCommands,
1431
1467
  experimentalSimulateSubagents: experimentalSimulateSubagents ?? configByFile.experimentalSimulateSubagents ?? defaults.experimentalSimulateSubagents
1432
1468
  };
1433
1469
  return new Config(configParams);
1434
1470
  }
1435
1471
  };
1472
+ function getBaseDirsInLightOfGlobal({
1473
+ baseDirs,
1474
+ global
1475
+ }) {
1476
+ if (isEnvTest) {
1477
+ return baseDirs.map((baseDir) => join11(".", baseDir));
1478
+ }
1479
+ if (global) {
1480
+ return [getHomeDirectory()];
1481
+ }
1482
+ baseDirs.forEach((baseDir) => {
1483
+ validateBaseDir(baseDir);
1484
+ });
1485
+ return baseDirs;
1486
+ }
1436
1487
 
1437
1488
  // src/ignore/ignore-processor.ts
1438
1489
  import { z as z9 } from "zod/mini";
1439
1490
 
1440
1491
  // src/ignore/amazonqcli-ignore.ts
1441
- import { join as join11 } from "path";
1492
+ import { join as join12 } from "path";
1442
1493
 
1443
1494
  // src/types/tool-file.ts
1444
1495
  var ToolFile = class extends AiFile {
@@ -1546,7 +1597,7 @@ var AmazonqcliIgnore = class _AmazonqcliIgnore extends ToolIgnore {
1546
1597
  validate = true
1547
1598
  }) {
1548
1599
  const fileContent = await readFileContent(
1549
- join11(
1600
+ join12(
1550
1601
  baseDir,
1551
1602
  this.getSettablePaths().relativeDirPath,
1552
1603
  this.getSettablePaths().relativeFilePath
@@ -1563,7 +1614,7 @@ var AmazonqcliIgnore = class _AmazonqcliIgnore extends ToolIgnore {
1563
1614
  };
1564
1615
 
1565
1616
  // src/ignore/augmentcode-ignore.ts
1566
- import { join as join12 } from "path";
1617
+ import { join as join13 } from "path";
1567
1618
  var AugmentcodeIgnore = class _AugmentcodeIgnore extends ToolIgnore {
1568
1619
  static getSettablePaths() {
1569
1620
  return {
@@ -1601,7 +1652,7 @@ var AugmentcodeIgnore = class _AugmentcodeIgnore extends ToolIgnore {
1601
1652
  validate = true
1602
1653
  }) {
1603
1654
  const fileContent = await readFileContent(
1604
- join12(
1655
+ join13(
1605
1656
  baseDir,
1606
1657
  this.getSettablePaths().relativeDirPath,
1607
1658
  this.getSettablePaths().relativeFilePath
@@ -1618,7 +1669,7 @@ var AugmentcodeIgnore = class _AugmentcodeIgnore extends ToolIgnore {
1618
1669
  };
1619
1670
 
1620
1671
  // src/ignore/claudecode-ignore.ts
1621
- import { join as join13 } from "path";
1672
+ import { join as join14 } from "path";
1622
1673
  import { uniq } from "es-toolkit";
1623
1674
  var ClaudecodeIgnore = class _ClaudecodeIgnore extends ToolIgnore {
1624
1675
  constructor(params) {
@@ -1654,7 +1705,7 @@ var ClaudecodeIgnore = class _ClaudecodeIgnore extends ToolIgnore {
1654
1705
  const fileContent = rulesyncIgnore.getFileContent();
1655
1706
  const patterns = fileContent.split(/\r?\n|\r/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
1656
1707
  const deniedValues = patterns.map((pattern) => `Read(${pattern})`);
1657
- const filePath = join13(
1708
+ const filePath = join14(
1658
1709
  baseDir,
1659
1710
  this.getSettablePaths().relativeDirPath,
1660
1711
  this.getSettablePaths().relativeFilePath
@@ -1682,7 +1733,7 @@ var ClaudecodeIgnore = class _ClaudecodeIgnore extends ToolIgnore {
1682
1733
  validate = true
1683
1734
  }) {
1684
1735
  const fileContent = await readFileContent(
1685
- join13(
1736
+ join14(
1686
1737
  baseDir,
1687
1738
  this.getSettablePaths().relativeDirPath,
1688
1739
  this.getSettablePaths().relativeFilePath
@@ -1699,7 +1750,7 @@ var ClaudecodeIgnore = class _ClaudecodeIgnore extends ToolIgnore {
1699
1750
  };
1700
1751
 
1701
1752
  // src/ignore/cline-ignore.ts
1702
- import { join as join14 } from "path";
1753
+ import { join as join15 } from "path";
1703
1754
  var ClineIgnore = class _ClineIgnore extends ToolIgnore {
1704
1755
  static getSettablePaths() {
1705
1756
  return {
@@ -1736,7 +1787,7 @@ var ClineIgnore = class _ClineIgnore extends ToolIgnore {
1736
1787
  validate = true
1737
1788
  }) {
1738
1789
  const fileContent = await readFileContent(
1739
- join14(
1790
+ join15(
1740
1791
  baseDir,
1741
1792
  this.getSettablePaths().relativeDirPath,
1742
1793
  this.getSettablePaths().relativeFilePath
@@ -1753,7 +1804,7 @@ var ClineIgnore = class _ClineIgnore extends ToolIgnore {
1753
1804
  };
1754
1805
 
1755
1806
  // src/ignore/codexcli-ignore.ts
1756
- import { join as join15 } from "path";
1807
+ import { join as join16 } from "path";
1757
1808
  var CodexcliIgnore = class _CodexcliIgnore extends ToolIgnore {
1758
1809
  static getSettablePaths() {
1759
1810
  return {
@@ -1783,7 +1834,7 @@ var CodexcliIgnore = class _CodexcliIgnore extends ToolIgnore {
1783
1834
  validate = true
1784
1835
  }) {
1785
1836
  const fileContent = await readFileContent(
1786
- join15(
1837
+ join16(
1787
1838
  baseDir,
1788
1839
  this.getSettablePaths().relativeDirPath,
1789
1840
  this.getSettablePaths().relativeFilePath
@@ -1800,7 +1851,7 @@ var CodexcliIgnore = class _CodexcliIgnore extends ToolIgnore {
1800
1851
  };
1801
1852
 
1802
1853
  // src/ignore/cursor-ignore.ts
1803
- import { join as join16 } from "path";
1854
+ import { join as join17 } from "path";
1804
1855
  var CursorIgnore = class _CursorIgnore extends ToolIgnore {
1805
1856
  static getSettablePaths() {
1806
1857
  return {
@@ -1833,7 +1884,7 @@ var CursorIgnore = class _CursorIgnore extends ToolIgnore {
1833
1884
  validate = true
1834
1885
  }) {
1835
1886
  const fileContent = await readFileContent(
1836
- join16(
1887
+ join17(
1837
1888
  baseDir,
1838
1889
  this.getSettablePaths().relativeDirPath,
1839
1890
  this.getSettablePaths().relativeFilePath
@@ -1850,7 +1901,7 @@ var CursorIgnore = class _CursorIgnore extends ToolIgnore {
1850
1901
  };
1851
1902
 
1852
1903
  // src/ignore/geminicli-ignore.ts
1853
- import { join as join17 } from "path";
1904
+ import { join as join18 } from "path";
1854
1905
  var GeminiCliIgnore = class _GeminiCliIgnore extends ToolIgnore {
1855
1906
  static getSettablePaths() {
1856
1907
  return {
@@ -1877,7 +1928,7 @@ var GeminiCliIgnore = class _GeminiCliIgnore extends ToolIgnore {
1877
1928
  validate = true
1878
1929
  }) {
1879
1930
  const fileContent = await readFileContent(
1880
- join17(
1931
+ join18(
1881
1932
  baseDir,
1882
1933
  this.getSettablePaths().relativeDirPath,
1883
1934
  this.getSettablePaths().relativeFilePath
@@ -1894,7 +1945,7 @@ var GeminiCliIgnore = class _GeminiCliIgnore extends ToolIgnore {
1894
1945
  };
1895
1946
 
1896
1947
  // src/ignore/junie-ignore.ts
1897
- import { join as join18 } from "path";
1948
+ import { join as join19 } from "path";
1898
1949
  var JunieIgnore = class _JunieIgnore extends ToolIgnore {
1899
1950
  static getSettablePaths() {
1900
1951
  return {
@@ -1921,7 +1972,7 @@ var JunieIgnore = class _JunieIgnore extends ToolIgnore {
1921
1972
  validate = true
1922
1973
  }) {
1923
1974
  const fileContent = await readFileContent(
1924
- join18(
1975
+ join19(
1925
1976
  baseDir,
1926
1977
  this.getSettablePaths().relativeDirPath,
1927
1978
  this.getSettablePaths().relativeFilePath
@@ -1938,7 +1989,7 @@ var JunieIgnore = class _JunieIgnore extends ToolIgnore {
1938
1989
  };
1939
1990
 
1940
1991
  // src/ignore/kiro-ignore.ts
1941
- import { join as join19 } from "path";
1992
+ import { join as join20 } from "path";
1942
1993
  var KiroIgnore = class _KiroIgnore extends ToolIgnore {
1943
1994
  static getSettablePaths() {
1944
1995
  return {
@@ -1965,7 +2016,7 @@ var KiroIgnore = class _KiroIgnore extends ToolIgnore {
1965
2016
  validate = true
1966
2017
  }) {
1967
2018
  const fileContent = await readFileContent(
1968
- join19(
2019
+ join20(
1969
2020
  baseDir,
1970
2021
  this.getSettablePaths().relativeDirPath,
1971
2022
  this.getSettablePaths().relativeFilePath
@@ -1982,7 +2033,7 @@ var KiroIgnore = class _KiroIgnore extends ToolIgnore {
1982
2033
  };
1983
2034
 
1984
2035
  // src/ignore/qwencode-ignore.ts
1985
- import { join as join20 } from "path";
2036
+ import { join as join21 } from "path";
1986
2037
  var QwencodeIgnore = class _QwencodeIgnore extends ToolIgnore {
1987
2038
  static getSettablePaths() {
1988
2039
  return {
@@ -2009,7 +2060,7 @@ var QwencodeIgnore = class _QwencodeIgnore extends ToolIgnore {
2009
2060
  validate = true
2010
2061
  }) {
2011
2062
  const fileContent = await readFileContent(
2012
- join20(
2063
+ join21(
2013
2064
  baseDir,
2014
2065
  this.getSettablePaths().relativeDirPath,
2015
2066
  this.getSettablePaths().relativeFilePath
@@ -2026,7 +2077,7 @@ var QwencodeIgnore = class _QwencodeIgnore extends ToolIgnore {
2026
2077
  };
2027
2078
 
2028
2079
  // src/ignore/roo-ignore.ts
2029
- import { join as join21 } from "path";
2080
+ import { join as join22 } from "path";
2030
2081
  var RooIgnore = class _RooIgnore extends ToolIgnore {
2031
2082
  static getSettablePaths() {
2032
2083
  return {
@@ -2053,7 +2104,7 @@ var RooIgnore = class _RooIgnore extends ToolIgnore {
2053
2104
  validate = true
2054
2105
  }) {
2055
2106
  const fileContent = await readFileContent(
2056
- join21(
2107
+ join22(
2057
2108
  baseDir,
2058
2109
  this.getSettablePaths().relativeDirPath,
2059
2110
  this.getSettablePaths().relativeFilePath
@@ -2070,7 +2121,7 @@ var RooIgnore = class _RooIgnore extends ToolIgnore {
2070
2121
  };
2071
2122
 
2072
2123
  // src/ignore/windsurf-ignore.ts
2073
- import { join as join22 } from "path";
2124
+ import { join as join23 } from "path";
2074
2125
  var WindsurfIgnore = class _WindsurfIgnore extends ToolIgnore {
2075
2126
  static getSettablePaths() {
2076
2127
  return {
@@ -2097,7 +2148,7 @@ var WindsurfIgnore = class _WindsurfIgnore extends ToolIgnore {
2097
2148
  validate = true
2098
2149
  }) {
2099
2150
  const fileContent = await readFileContent(
2100
- join22(
2151
+ join23(
2101
2152
  baseDir,
2102
2153
  this.getSettablePaths().relativeDirPath,
2103
2154
  this.getSettablePaths().relativeFilePath
@@ -2307,10 +2358,10 @@ var IgnoreProcessor = class extends FeatureProcessor {
2307
2358
  import { z as z11 } from "zod/mini";
2308
2359
 
2309
2360
  // src/mcp/amazonqcli-mcp.ts
2310
- import { join as join24 } from "path";
2361
+ import { join as join25 } from "path";
2311
2362
 
2312
2363
  // src/mcp/rulesync-mcp.ts
2313
- import { join as join23 } from "path";
2364
+ import { join as join24 } from "path";
2314
2365
  import { z as z10 } from "zod/mini";
2315
2366
  var McpTransportTypeSchema = z10.enum(["stdio", "sse", "http"]);
2316
2367
  var McpServerBaseSchema = z10.object({
@@ -2361,7 +2412,7 @@ var RulesyncMcp = class _RulesyncMcp extends RulesyncFile {
2361
2412
  }
2362
2413
  static async fromFile({ validate = true }) {
2363
2414
  const fileContent = await readFileContent(
2364
- join23(this.getSettablePaths().relativeDirPath, this.getSettablePaths().relativeFilePath)
2415
+ join24(this.getSettablePaths().relativeDirPath, this.getSettablePaths().relativeFilePath)
2365
2416
  );
2366
2417
  return new _RulesyncMcp({
2367
2418
  baseDir: ".",
@@ -2428,7 +2479,7 @@ var AmazonqcliMcp = class _AmazonqcliMcp extends ToolMcp {
2428
2479
  validate = true
2429
2480
  }) {
2430
2481
  const fileContent = await readFileContent(
2431
- join24(
2482
+ join25(
2432
2483
  baseDir,
2433
2484
  this.getSettablePaths().relativeDirPath,
2434
2485
  this.getSettablePaths().relativeFilePath
@@ -2464,7 +2515,7 @@ var AmazonqcliMcp = class _AmazonqcliMcp extends ToolMcp {
2464
2515
  };
2465
2516
 
2466
2517
  // src/mcp/claudecode-mcp.ts
2467
- import { join as join25 } from "path";
2518
+ import { join as join26 } from "path";
2468
2519
  var ClaudecodeMcp = class _ClaudecodeMcp extends ToolMcp {
2469
2520
  static getSettablePaths() {
2470
2521
  return {
@@ -2477,7 +2528,7 @@ var ClaudecodeMcp = class _ClaudecodeMcp extends ToolMcp {
2477
2528
  validate = true
2478
2529
  }) {
2479
2530
  const fileContent = await readFileContent(
2480
- join25(
2531
+ join26(
2481
2532
  baseDir,
2482
2533
  this.getSettablePaths().relativeDirPath,
2483
2534
  this.getSettablePaths().relativeFilePath
@@ -2513,7 +2564,7 @@ var ClaudecodeMcp = class _ClaudecodeMcp extends ToolMcp {
2513
2564
  };
2514
2565
 
2515
2566
  // src/mcp/cline-mcp.ts
2516
- import { join as join26 } from "path";
2567
+ import { join as join27 } from "path";
2517
2568
  var ClineMcp = class _ClineMcp extends ToolMcp {
2518
2569
  static getSettablePaths() {
2519
2570
  return {
@@ -2526,7 +2577,7 @@ var ClineMcp = class _ClineMcp extends ToolMcp {
2526
2577
  validate = true
2527
2578
  }) {
2528
2579
  const fileContent = await readFileContent(
2529
- join26(
2580
+ join27(
2530
2581
  baseDir,
2531
2582
  this.getSettablePaths().relativeDirPath,
2532
2583
  this.getSettablePaths().relativeFilePath
@@ -2562,7 +2613,7 @@ var ClineMcp = class _ClineMcp extends ToolMcp {
2562
2613
  };
2563
2614
 
2564
2615
  // src/mcp/copilot-mcp.ts
2565
- import { join as join27 } from "path";
2616
+ import { join as join28 } from "path";
2566
2617
  var CopilotMcp = class _CopilotMcp extends ToolMcp {
2567
2618
  static getSettablePaths() {
2568
2619
  return {
@@ -2575,7 +2626,7 @@ var CopilotMcp = class _CopilotMcp extends ToolMcp {
2575
2626
  validate = true
2576
2627
  }) {
2577
2628
  const fileContent = await readFileContent(
2578
- join27(
2629
+ join28(
2579
2630
  baseDir,
2580
2631
  this.getSettablePaths().relativeDirPath,
2581
2632
  this.getSettablePaths().relativeFilePath
@@ -2611,7 +2662,7 @@ var CopilotMcp = class _CopilotMcp extends ToolMcp {
2611
2662
  };
2612
2663
 
2613
2664
  // src/mcp/cursor-mcp.ts
2614
- import { join as join28 } from "path";
2665
+ import { join as join29 } from "path";
2615
2666
  var CursorMcp = class _CursorMcp extends ToolMcp {
2616
2667
  static getSettablePaths() {
2617
2668
  return {
@@ -2624,7 +2675,7 @@ var CursorMcp = class _CursorMcp extends ToolMcp {
2624
2675
  validate = true
2625
2676
  }) {
2626
2677
  const fileContent = await readFileContent(
2627
- join28(
2678
+ join29(
2628
2679
  baseDir,
2629
2680
  this.getSettablePaths().relativeDirPath,
2630
2681
  this.getSettablePaths().relativeFilePath
@@ -2671,7 +2722,7 @@ var CursorMcp = class _CursorMcp extends ToolMcp {
2671
2722
  };
2672
2723
 
2673
2724
  // src/mcp/roo-mcp.ts
2674
- import { join as join29 } from "path";
2725
+ import { join as join30 } from "path";
2675
2726
  var RooMcp = class _RooMcp extends ToolMcp {
2676
2727
  static getSettablePaths() {
2677
2728
  return {
@@ -2684,7 +2735,7 @@ var RooMcp = class _RooMcp extends ToolMcp {
2684
2735
  validate = true
2685
2736
  }) {
2686
2737
  const fileContent = await readFileContent(
2687
- join29(
2738
+ join30(
2688
2739
  baseDir,
2689
2740
  this.getSettablePaths().relativeDirPath,
2690
2741
  this.getSettablePaths().relativeFilePath
@@ -2895,14 +2946,6 @@ import { basename as basename16, join as join54 } from "path";
2895
2946
  import { XMLBuilder } from "fast-xml-parser";
2896
2947
  import { z as z20 } from "zod/mini";
2897
2948
 
2898
- // src/constants/paths.ts
2899
- import { join as join30 } from "path";
2900
- var RULESYNC_DIR = ".rulesync";
2901
- var RULESYNC_RULES_DIR = join30(".rulesync", "rules");
2902
- var RULESYNC_RULES_DIR_LEGACY = ".rulesync";
2903
- var RULESYNC_MCP_FILE = join30(".rulesync", ".mcp.json");
2904
- var RULESYNC_SUBAGENTS_DIR = join30(".rulesync", "subagents");
2905
-
2906
2949
  // src/subagents/simulated-subagent.ts
2907
2950
  import { basename as basename11, join as join31 } from "path";
2908
2951
  import { z as z12 } from "zod/mini";
@@ -3202,7 +3245,7 @@ var RulesyncSubagent = class _RulesyncSubagent extends RulesyncFile {
3202
3245
  static async fromFile({
3203
3246
  relativeFilePath
3204
3247
  }) {
3205
- const fileContent = await readFileContent(join32(RULESYNC_SUBAGENTS_DIR, relativeFilePath));
3248
+ const fileContent = await readFileContent(join32(".rulesync/subagents", relativeFilePath));
3206
3249
  const { frontmatter, body: content } = parseFrontmatter(fileContent);
3207
3250
  const result = RulesyncSubagentFrontmatterSchema.safeParse(frontmatter);
3208
3251
  if (!result.success) {
@@ -3736,6 +3779,12 @@ var ToolRule = class extends ToolFile {
3736
3779
  this.description = description;
3737
3780
  this.globs = globs;
3738
3781
  }
3782
+ static getSettablePaths() {
3783
+ throw new Error("Please implement this method in the subclass.");
3784
+ }
3785
+ static getSettablePathsGlobal() {
3786
+ throw new Error("Please implement this method in the subclass.");
3787
+ }
3739
3788
  static async fromFile(_params) {
3740
3789
  throw new Error("Please implement this method in the subclass.");
3741
3790
  }
@@ -3747,16 +3796,32 @@ var ToolRule = class extends ToolFile {
3747
3796
  rulesyncRule,
3748
3797
  validate = true,
3749
3798
  rootPath = { relativeDirPath: ".", relativeFilePath: "AGENTS.md" },
3750
- nonRootPath = { relativeDirPath: ".agents/memories" }
3799
+ nonRootPath
3751
3800
  }) {
3752
3801
  const fileContent = rulesyncRule.getBody();
3802
+ const isRoot = rulesyncRule.getFrontmatter().root ?? false;
3803
+ if (isRoot) {
3804
+ return {
3805
+ baseDir,
3806
+ relativeDirPath: rootPath.relativeDirPath,
3807
+ relativeFilePath: rootPath.relativeFilePath,
3808
+ fileContent,
3809
+ validate,
3810
+ root: true,
3811
+ description: rulesyncRule.getFrontmatter().description,
3812
+ globs: rulesyncRule.getFrontmatter().globs
3813
+ };
3814
+ }
3815
+ if (!nonRootPath) {
3816
+ throw new Error("nonRootPath is not set");
3817
+ }
3753
3818
  return {
3754
3819
  baseDir,
3755
- relativeDirPath: rulesyncRule.getFrontmatter().root ? rootPath.relativeDirPath : nonRootPath.relativeDirPath,
3756
- relativeFilePath: rulesyncRule.getFrontmatter().root ? rootPath.relativeFilePath : rulesyncRule.getRelativeFilePath(),
3820
+ relativeDirPath: nonRootPath.relativeDirPath,
3821
+ relativeFilePath: rulesyncRule.getRelativeFilePath(),
3757
3822
  fileContent,
3758
3823
  validate,
3759
- root: rulesyncRule.getFrontmatter().root ?? false,
3824
+ root: false,
3760
3825
  description: rulesyncRule.getFrontmatter().description,
3761
3826
  globs: rulesyncRule.getFrontmatter().globs
3762
3827
  };
@@ -3785,7 +3850,7 @@ var ToolRule = class extends ToolFile {
3785
3850
  toRulesyncRuleDefault() {
3786
3851
  return new RulesyncRule({
3787
3852
  baseDir: this.getBaseDir(),
3788
- relativeDirPath: RULESYNC_RULES_DIR,
3853
+ relativeDirPath: ".rulesync/rules",
3789
3854
  relativeFilePath: this.getRelativeFilePath(),
3790
3855
  frontmatter: {
3791
3856
  root: this.isRoot(),
@@ -3961,7 +4026,7 @@ var AugmentcodeLegacyRule = class _AugmentcodeLegacyRule extends ToolRule {
3961
4026
  baseDir: this.getBaseDir(),
3962
4027
  frontmatter: rulesyncFrontmatter,
3963
4028
  body: this.getFileContent(),
3964
- relativeDirPath: RULESYNC_RULES_DIR,
4029
+ relativeDirPath: ".rulesync/rules",
3965
4030
  relativeFilePath: this.getRelativeFilePath(),
3966
4031
  validate: true
3967
4032
  });
@@ -4086,39 +4151,68 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
4086
4151
  relativeFilePath: "CLAUDE.md"
4087
4152
  },
4088
4153
  nonRoot: {
4089
- relativeDirPath: ".claude/memories"
4154
+ relativeDirPath: join41(".claude", "memories")
4155
+ }
4156
+ };
4157
+ }
4158
+ static getSettablePathsGlobal() {
4159
+ return {
4160
+ root: {
4161
+ relativeDirPath: ".claude",
4162
+ relativeFilePath: "CLAUDE.md"
4090
4163
  }
4091
4164
  };
4092
4165
  }
4093
4166
  static async fromFile({
4094
4167
  baseDir = ".",
4095
4168
  relativeFilePath,
4096
- validate = true
4169
+ validate = true,
4170
+ global = false
4097
4171
  }) {
4098
- const isRoot = relativeFilePath === this.getSettablePaths().root.relativeFilePath;
4099
- const relativePath = isRoot ? this.getSettablePaths().root.relativeFilePath : join41(this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath);
4172
+ const paths = global ? this.getSettablePathsGlobal() : this.getSettablePaths();
4173
+ const isRoot = relativeFilePath === paths.root.relativeFilePath;
4174
+ if (isRoot) {
4175
+ const relativePath2 = paths.root.relativeFilePath;
4176
+ const fileContent2 = await readFileContent(
4177
+ join41(baseDir, paths.root.relativeDirPath, relativePath2)
4178
+ );
4179
+ return new _ClaudecodeRule({
4180
+ baseDir,
4181
+ relativeDirPath: paths.root.relativeDirPath,
4182
+ relativeFilePath: paths.root.relativeFilePath,
4183
+ fileContent: fileContent2,
4184
+ validate,
4185
+ root: true
4186
+ });
4187
+ }
4188
+ if (!paths.nonRoot) {
4189
+ throw new Error("nonRoot path is not set");
4190
+ }
4191
+ const relativePath = join41(paths.nonRoot.relativeDirPath, relativeFilePath);
4100
4192
  const fileContent = await readFileContent(join41(baseDir, relativePath));
4101
4193
  return new _ClaudecodeRule({
4102
4194
  baseDir,
4103
- relativeDirPath: isRoot ? this.getSettablePaths().root.relativeDirPath : this.getSettablePaths().nonRoot.relativeDirPath,
4104
- relativeFilePath: isRoot ? this.getSettablePaths().root.relativeFilePath : relativeFilePath,
4195
+ relativeDirPath: paths.nonRoot.relativeDirPath,
4196
+ relativeFilePath,
4105
4197
  fileContent,
4106
4198
  validate,
4107
- root: isRoot
4199
+ root: false
4108
4200
  });
4109
4201
  }
4110
4202
  static fromRulesyncRule({
4111
4203
  baseDir = ".",
4112
4204
  rulesyncRule,
4113
- validate = true
4205
+ validate = true,
4206
+ global = false
4114
4207
  }) {
4208
+ const paths = global ? this.getSettablePathsGlobal() : this.getSettablePaths();
4115
4209
  return new _ClaudecodeRule(
4116
4210
  this.buildToolRuleParamsDefault({
4117
4211
  baseDir,
4118
4212
  rulesyncRule,
4119
4213
  validate,
4120
- rootPath: this.getSettablePaths().root,
4121
- nonRootPath: this.getSettablePaths().nonRoot
4214
+ rootPath: paths.root,
4215
+ nonRootPath: paths.nonRoot
4122
4216
  })
4123
4217
  );
4124
4218
  }
@@ -4208,35 +4302,64 @@ var CodexcliRule = class _CodexcliRule extends ToolRule {
4208
4302
  }
4209
4303
  };
4210
4304
  }
4305
+ static getSettablePathsGlobal() {
4306
+ return {
4307
+ root: {
4308
+ relativeDirPath: ".codex",
4309
+ relativeFilePath: "AGENTS.md"
4310
+ }
4311
+ };
4312
+ }
4211
4313
  static async fromFile({
4212
4314
  baseDir = ".",
4213
4315
  relativeFilePath,
4214
- validate = true
4316
+ validate = true,
4317
+ global = false
4215
4318
  }) {
4216
- const isRoot = relativeFilePath === "AGENTS.md";
4217
- const relativePath = isRoot ? "AGENTS.md" : join43(this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath);
4319
+ const paths = global ? this.getSettablePathsGlobal() : this.getSettablePaths();
4320
+ const isRoot = relativeFilePath === paths.root.relativeFilePath;
4321
+ if (isRoot) {
4322
+ const relativePath2 = paths.root.relativeFilePath;
4323
+ const fileContent2 = await readFileContent(
4324
+ join43(baseDir, paths.root.relativeDirPath, relativePath2)
4325
+ );
4326
+ return new _CodexcliRule({
4327
+ baseDir,
4328
+ relativeDirPath: paths.root.relativeDirPath,
4329
+ relativeFilePath: paths.root.relativeFilePath,
4330
+ fileContent: fileContent2,
4331
+ validate,
4332
+ root: true
4333
+ });
4334
+ }
4335
+ if (!paths.nonRoot) {
4336
+ throw new Error("nonRoot path is not set");
4337
+ }
4338
+ const relativePath = join43(paths.nonRoot.relativeDirPath, relativeFilePath);
4218
4339
  const fileContent = await readFileContent(join43(baseDir, relativePath));
4219
4340
  return new _CodexcliRule({
4220
4341
  baseDir,
4221
- relativeDirPath: isRoot ? this.getSettablePaths().root.relativeDirPath : this.getSettablePaths().nonRoot.relativeDirPath,
4222
- relativeFilePath: isRoot ? "AGENTS.md" : relativeFilePath,
4342
+ relativeDirPath: paths.nonRoot.relativeDirPath,
4343
+ relativeFilePath,
4223
4344
  fileContent,
4224
4345
  validate,
4225
- root: isRoot
4346
+ root: false
4226
4347
  });
4227
4348
  }
4228
4349
  static fromRulesyncRule({
4229
4350
  baseDir = ".",
4230
4351
  rulesyncRule,
4231
- validate = true
4352
+ validate = true,
4353
+ global = false
4232
4354
  }) {
4355
+ const paths = global ? this.getSettablePathsGlobal() : this.getSettablePaths();
4233
4356
  return new _CodexcliRule(
4234
4357
  this.buildToolRuleParamsAgentsmd({
4235
4358
  baseDir,
4236
4359
  rulesyncRule,
4237
4360
  validate,
4238
- rootPath: this.getSettablePaths().root,
4239
- nonRootPath: this.getSettablePaths().nonRoot
4361
+ rootPath: paths.root,
4362
+ nonRootPath: paths.nonRoot
4240
4363
  })
4241
4364
  );
4242
4365
  }
@@ -4301,7 +4424,7 @@ var CopilotRule = class _CopilotRule extends ToolRule {
4301
4424
  baseDir: this.getBaseDir(),
4302
4425
  frontmatter: rulesyncFrontmatter,
4303
4426
  body: this.body,
4304
- relativeDirPath: RULESYNC_RULES_DIR,
4427
+ relativeDirPath: ".rulesync/rules",
4305
4428
  relativeFilePath: this.getRelativeFilePath(),
4306
4429
  validate: true
4307
4430
  });
@@ -5084,18 +5207,22 @@ var rulesProcessorToolTargets = [
5084
5207
  "windsurf"
5085
5208
  ];
5086
5209
  var RulesProcessorToolTargetSchema = z20.enum(rulesProcessorToolTargets);
5210
+ var rulesProcessorToolTargetsGlobal = ["claudecode", "codexcli"];
5087
5211
  var RulesProcessor = class extends FeatureProcessor {
5088
5212
  toolTarget;
5089
5213
  simulateCommands;
5090
5214
  simulateSubagents;
5215
+ global;
5091
5216
  constructor({
5092
- baseDir = process.cwd(),
5217
+ baseDir = ".",
5093
5218
  toolTarget,
5094
5219
  simulateCommands = false,
5095
- simulateSubagents = false
5220
+ simulateSubagents = false,
5221
+ global = false
5096
5222
  }) {
5097
5223
  super({ baseDir });
5098
5224
  this.toolTarget = RulesProcessorToolTargetSchema.parse(toolTarget);
5225
+ this.global = global;
5099
5226
  this.simulateCommands = simulateCommands;
5100
5227
  this.simulateSubagents = simulateSubagents;
5101
5228
  }
@@ -5148,7 +5275,8 @@ var RulesProcessor = class extends FeatureProcessor {
5148
5275
  return ClaudecodeRule.fromRulesyncRule({
5149
5276
  baseDir: this.baseDir,
5150
5277
  rulesyncRule,
5151
- validate: true
5278
+ validate: true,
5279
+ global: this.global
5152
5280
  });
5153
5281
  case "cline":
5154
5282
  if (!ClineRule.isTargetedByRulesyncRule(rulesyncRule)) {
@@ -5166,7 +5294,8 @@ var RulesProcessor = class extends FeatureProcessor {
5166
5294
  return CodexcliRule.fromRulesyncRule({
5167
5295
  baseDir: this.baseDir,
5168
5296
  rulesyncRule,
5169
- validate: true
5297
+ validate: true,
5298
+ global: this.global
5170
5299
  });
5171
5300
  case "copilot":
5172
5301
  if (!CopilotRule.isTargetedByRulesyncRule(rulesyncRule)) {
@@ -5404,14 +5533,28 @@ var RulesProcessor = class extends FeatureProcessor {
5404
5533
  * Load and parse rulesync rule files from .rulesync/rules/ directory
5405
5534
  */
5406
5535
  async loadRulesyncFiles() {
5407
- const files = await findFilesByGlobs(join54(RULESYNC_RULES_DIR, "*.md"));
5536
+ const files = await findFilesByGlobs(join54(".rulesync/rules", "*.md"));
5408
5537
  logger.debug(`Found ${files.length} rulesync files`);
5409
- return Promise.all(
5538
+ const rulesyncRules = await Promise.all(
5410
5539
  files.map((file) => RulesyncRule.fromFile({ relativeFilePath: basename16(file) }))
5411
5540
  );
5541
+ const rootRules = rulesyncRules.filter((rule) => rule.getFrontmatter().root);
5542
+ if (rootRules.length > 1) {
5543
+ throw new Error("Multiple root rulesync rules found");
5544
+ }
5545
+ if (this.global) {
5546
+ const nonRootRules = rulesyncRules.filter((rule) => !rule.getFrontmatter().root);
5547
+ if (nonRootRules.length > 0) {
5548
+ logger.warn(
5549
+ `${nonRootRules.length} non-root rulesync rules found, but it's in global mode, so ignoring them`
5550
+ );
5551
+ }
5552
+ return rootRules;
5553
+ }
5554
+ return rulesyncRules;
5412
5555
  }
5413
5556
  async loadRulesyncFilesLegacy() {
5414
- const legacyFiles = await findFilesByGlobs(join54(RULESYNC_RULES_DIR_LEGACY, "*.md"));
5557
+ const legacyFiles = await findFilesByGlobs(join54(".rulesync", "*.md"));
5415
5558
  logger.debug(`Found ${legacyFiles.length} legacy rulesync files`);
5416
5559
  return Promise.all(
5417
5560
  legacyFiles.map((file) => RulesyncRule.fromFileLegacy({ relativeFilePath: basename16(file) }))
@@ -5484,7 +5627,8 @@ var RulesProcessor = class extends FeatureProcessor {
5484
5627
  rootFilePaths.map(
5485
5628
  (filePath) => root.fromFile({
5486
5629
  baseDir: this.baseDir,
5487
- relativeFilePath: basename16(filePath)
5630
+ relativeFilePath: basename16(filePath),
5631
+ global: this.global
5488
5632
  })
5489
5633
  )
5490
5634
  );
@@ -5501,7 +5645,8 @@ var RulesProcessor = class extends FeatureProcessor {
5501
5645
  nonRootFilePaths.map(
5502
5646
  (filePath) => nonRoot.fromFile({
5503
5647
  baseDir: this.baseDir,
5504
- relativeFilePath: basename16(filePath)
5648
+ relativeFilePath: basename16(filePath),
5649
+ global: this.global
5505
5650
  })
5506
5651
  )
5507
5652
  );
@@ -5590,18 +5735,20 @@ var RulesProcessor = class extends FeatureProcessor {
5590
5735
  * Load Claude Code rule configuration from CLAUDE.md file
5591
5736
  */
5592
5737
  async loadClaudecodeRules() {
5593
- const settablePaths = ClaudecodeRule.getSettablePaths();
5594
- return await this.loadToolRulesDefault({
5738
+ const settablePaths = this.global ? ClaudecodeRule.getSettablePathsGlobal() : ClaudecodeRule.getSettablePaths();
5739
+ return this.loadToolRulesDefault({
5595
5740
  root: {
5596
5741
  relativeDirPath: settablePaths.root.relativeDirPath,
5597
5742
  relativeFilePath: settablePaths.root.relativeFilePath,
5598
5743
  fromFile: (params) => ClaudecodeRule.fromFile(params)
5599
5744
  },
5600
- nonRoot: {
5601
- relativeDirPath: settablePaths.nonRoot.relativeDirPath,
5602
- fromFile: (params) => ClaudecodeRule.fromFile(params),
5603
- extension: "md"
5604
- }
5745
+ ...settablePaths.nonRoot ? {
5746
+ nonRoot: {
5747
+ relativeDirPath: settablePaths.nonRoot.relativeDirPath,
5748
+ fromFile: (params) => ClaudecodeRule.fromFile(params),
5749
+ extension: "md"
5750
+ }
5751
+ } : {}
5605
5752
  });
5606
5753
  }
5607
5754
  /**
@@ -5621,18 +5768,20 @@ var RulesProcessor = class extends FeatureProcessor {
5621
5768
  * Load OpenAI Codex CLI rule configuration from AGENTS.md and .codex/memories/*.md files
5622
5769
  */
5623
5770
  async loadCodexcliRules() {
5624
- const settablePaths = CodexcliRule.getSettablePaths();
5771
+ const settablePaths = this.global ? CodexcliRule.getSettablePathsGlobal() : CodexcliRule.getSettablePaths();
5625
5772
  return await this.loadToolRulesDefault({
5626
5773
  root: {
5627
5774
  relativeDirPath: settablePaths.root.relativeDirPath,
5628
5775
  relativeFilePath: settablePaths.root.relativeFilePath,
5629
5776
  fromFile: (params) => CodexcliRule.fromFile(params)
5630
5777
  },
5631
- nonRoot: {
5632
- relativeDirPath: settablePaths.nonRoot.relativeDirPath,
5633
- fromFile: (params) => CodexcliRule.fromFile(params),
5634
- extension: "md"
5635
- }
5778
+ ...settablePaths.nonRoot ? {
5779
+ nonRoot: {
5780
+ relativeDirPath: settablePaths.nonRoot.relativeDirPath,
5781
+ fromFile: (params) => CodexcliRule.fromFile(params),
5782
+ extension: "md"
5783
+ }
5784
+ } : {}
5636
5785
  });
5637
5786
  }
5638
5787
  /**
@@ -5784,6 +5933,9 @@ var RulesProcessor = class extends FeatureProcessor {
5784
5933
  static getToolTargets() {
5785
5934
  return rulesProcessorToolTargets;
5786
5935
  }
5936
+ static getToolTargetsGlobal() {
5937
+ return rulesProcessorToolTargetsGlobal;
5938
+ }
5787
5939
  generateXmlReferencesSection(toolRules) {
5788
5940
  const toolRulesWithoutRoot = toolRules.filter((rule) => !rule.isRoot());
5789
5941
  if (toolRulesWithoutRoot.length === 0) {
@@ -5888,52 +6040,76 @@ async function generateCommand(options) {
5888
6040
  process.exit(1);
5889
6041
  }
5890
6042
  logger.info(`Base directories: ${config.getBaseDirs().join(", ")}`);
6043
+ const totalRulesOutputs = await generateRules(config);
6044
+ const totalIgnoreOutputs = await generateIgnore(config);
6045
+ const totalMcpOutputs = await generateMcp(config);
6046
+ const totalCommandOutputs = await generateCommands(config);
6047
+ const totalSubagentOutputs = await generateSubagents(config);
6048
+ const totalGenerated = totalRulesOutputs + totalMcpOutputs + totalCommandOutputs + totalIgnoreOutputs + totalSubagentOutputs;
6049
+ if (totalGenerated === 0) {
6050
+ const enabledFeatures = config.getFeatures().join(", ");
6051
+ logger.warn(`\u26A0\uFE0F No files generated for enabled features: ${enabledFeatures}`);
6052
+ return;
6053
+ }
6054
+ if (totalGenerated > 0) {
6055
+ const parts = [];
6056
+ if (totalRulesOutputs > 0) parts.push(`${totalRulesOutputs} rules`);
6057
+ if (totalIgnoreOutputs > 0) parts.push(`${totalIgnoreOutputs} ignore files`);
6058
+ if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP files`);
6059
+ if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
6060
+ if (totalSubagentOutputs > 0) parts.push(`${totalSubagentOutputs} subagents`);
6061
+ logger.success(`\u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`);
6062
+ }
6063
+ }
6064
+ async function generateRules(config) {
6065
+ if (!config.getFeatures().includes("rules")) {
6066
+ logger.debug("Skipping rule generation (not in --features)");
6067
+ return 0;
6068
+ }
5891
6069
  let totalRulesOutputs = 0;
5892
- if (config.getFeatures().includes("rules")) {
5893
- logger.info("Generating rule files...");
5894
- for (const baseDir of config.getBaseDirs()) {
5895
- for (const toolTarget of intersection(config.getTargets(), RulesProcessor.getToolTargets())) {
5896
- const processor = new RulesProcessor({
5897
- baseDir,
5898
- toolTarget,
5899
- simulateCommands: config.getExperimentalSimulateCommands(),
5900
- simulateSubagents: config.getExperimentalSimulateSubagents()
5901
- });
5902
- if (config.getDelete()) {
5903
- const oldToolFiles = await processor.loadToolFilesToDelete();
5904
- await processor.removeAiFiles(oldToolFiles);
5905
- }
5906
- let rulesyncFiles = await processor.loadRulesyncFiles();
5907
- if (rulesyncFiles.length === 0) {
5908
- rulesyncFiles = await processor.loadRulesyncFilesLegacy();
5909
- }
5910
- const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
5911
- const writtenCount = await processor.writeAiFiles(toolFiles);
5912
- totalRulesOutputs += writtenCount;
5913
- logger.success(`Generated ${writtenCount} ${toolTarget} rule(s) in ${baseDir}`);
6070
+ logger.info("Generating rule files...");
6071
+ const toolTargets = config.getExperimentalGlobal() ? intersection(config.getTargets(), RulesProcessor.getToolTargetsGlobal()) : intersection(config.getTargets(), RulesProcessor.getToolTargets());
6072
+ for (const baseDir of config.getBaseDirs()) {
6073
+ for (const toolTarget of toolTargets) {
6074
+ const processor = new RulesProcessor({
6075
+ baseDir,
6076
+ toolTarget,
6077
+ global: config.getExperimentalGlobal(),
6078
+ simulateCommands: config.getExperimentalSimulateCommands(),
6079
+ simulateSubagents: config.getExperimentalSimulateSubagents()
6080
+ });
6081
+ if (config.getDelete()) {
6082
+ const oldToolFiles = await processor.loadToolFilesToDelete();
6083
+ await processor.removeAiFiles(oldToolFiles);
6084
+ }
6085
+ let rulesyncFiles = await processor.loadRulesyncFiles();
6086
+ if (rulesyncFiles.length === 0) {
6087
+ rulesyncFiles = await processor.loadRulesyncFilesLegacy();
5914
6088
  }
6089
+ const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
6090
+ const writtenCount = await processor.writeAiFiles(toolFiles);
6091
+ totalRulesOutputs += writtenCount;
6092
+ logger.success(`Generated ${writtenCount} ${toolTarget} rule(s) in ${baseDir}`);
5915
6093
  }
5916
- } else {
5917
- logger.info("Skipping rule generation (not in --features)");
5918
6094
  }
5919
- let totalMcpOutputs = 0;
5920
- if (config.getFeatures().includes("mcp")) {
5921
- logger.info("Generating MCP files...");
5922
- const supportedMcpTargets = [
5923
- "amazonqcli",
5924
- "claudecode",
5925
- "cline",
5926
- "copilot",
5927
- "cursor",
5928
- "roo"
5929
- ];
5930
- const mcpSupportedTargets = config.getTargets().filter((target) => {
5931
- return supportedMcpTargets.some((supportedTarget) => supportedTarget === target);
5932
- });
6095
+ return totalRulesOutputs;
6096
+ }
6097
+ async function generateIgnore(config) {
6098
+ if (!config.getFeatures().includes("ignore")) {
6099
+ logger.debug("Skipping ignore file generation (not in --features)");
6100
+ return 0;
6101
+ }
6102
+ if (config.getExperimentalGlobal()) {
6103
+ logger.debug("Skipping ignore file generation (not supported in global mode)");
6104
+ return 0;
6105
+ }
6106
+ let totalIgnoreOutputs = 0;
6107
+ logger.info("Generating ignore files...");
6108
+ for (const toolTarget of intersection(config.getTargets(), IgnoreProcessor.getToolTargets())) {
5933
6109
  for (const baseDir of config.getBaseDirs()) {
5934
- for (const toolTarget of intersection(mcpSupportedTargets, McpProcessor.getToolTargets())) {
5935
- const processor = new McpProcessor({
5936
- baseDir,
6110
+ try {
6111
+ const processor = new IgnoreProcessor({
6112
+ baseDir: baseDir === process.cwd() ? "." : baseDir,
5937
6113
  toolTarget
5938
6114
  });
5939
6115
  if (config.getDelete()) {
@@ -5941,117 +6117,133 @@ async function generateCommand(options) {
5941
6117
  await processor.removeAiFiles(oldToolFiles);
5942
6118
  }
5943
6119
  const rulesyncFiles = await processor.loadRulesyncFiles();
5944
- const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
5945
- const writtenCount = await processor.writeAiFiles(toolFiles);
5946
- totalMcpOutputs += writtenCount;
5947
- logger.success(
5948
- `Generated ${writtenCount} ${toolTarget} MCP configuration(s) in ${baseDir}`
6120
+ if (rulesyncFiles.length > 0) {
6121
+ const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
6122
+ const writtenCount = await processor.writeAiFiles(toolFiles);
6123
+ totalIgnoreOutputs += writtenCount;
6124
+ logger.success(`Generated ${writtenCount} ${toolTarget} ignore file(s) in ${baseDir}`);
6125
+ }
6126
+ } catch (error) {
6127
+ logger.warn(
6128
+ `Failed to generate ${toolTarget} ignore files for ${baseDir}:`,
6129
+ error instanceof Error ? error.message : String(error)
5949
6130
  );
6131
+ continue;
5950
6132
  }
5951
6133
  }
5952
- } else {
5953
- logger.info("Skipping MCP configuration generation (not in --features)");
5954
6134
  }
5955
- let totalCommandOutputs = 0;
5956
- if (config.getFeatures().includes("commands")) {
5957
- logger.info("Generating command files...");
5958
- for (const baseDir of config.getBaseDirs()) {
5959
- for (const toolTarget of intersection(
5960
- config.getTargets(),
5961
- CommandsProcessor.getToolTargets({
5962
- includeSimulated: config.getExperimentalSimulateCommands()
5963
- })
5964
- )) {
5965
- const processor = new CommandsProcessor({
5966
- baseDir,
5967
- toolTarget
5968
- });
5969
- if (config.getDelete()) {
5970
- const oldToolFiles = await processor.loadToolFilesToDelete();
5971
- await processor.removeAiFiles(oldToolFiles);
5972
- }
5973
- const rulesyncFiles = await processor.loadRulesyncFiles();
5974
- const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
5975
- const writtenCount = await processor.writeAiFiles(toolFiles);
5976
- totalCommandOutputs += writtenCount;
5977
- logger.success(`Generated ${writtenCount} ${toolTarget} command(s) in ${baseDir}`);
6135
+ return totalIgnoreOutputs;
6136
+ }
6137
+ async function generateMcp(config) {
6138
+ if (!config.getFeatures().includes("mcp")) {
6139
+ logger.debug("Skipping MCP configuration generation (not in --features)");
6140
+ return 0;
6141
+ }
6142
+ if (config.getExperimentalGlobal()) {
6143
+ logger.debug("Skipping MCP configuration generation (not supported in global mode)");
6144
+ return 0;
6145
+ }
6146
+ let totalMcpOutputs = 0;
6147
+ logger.info("Generating MCP files...");
6148
+ const supportedMcpTargets = [
6149
+ "amazonqcli",
6150
+ "claudecode",
6151
+ "cline",
6152
+ "copilot",
6153
+ "cursor",
6154
+ "roo"
6155
+ ];
6156
+ const mcpSupportedTargets = config.getTargets().filter((target) => {
6157
+ return supportedMcpTargets.some((supportedTarget) => supportedTarget === target);
6158
+ });
6159
+ for (const baseDir of config.getBaseDirs()) {
6160
+ for (const toolTarget of intersection(mcpSupportedTargets, McpProcessor.getToolTargets())) {
6161
+ const processor = new McpProcessor({
6162
+ baseDir,
6163
+ toolTarget
6164
+ });
6165
+ if (config.getDelete()) {
6166
+ const oldToolFiles = await processor.loadToolFilesToDelete();
6167
+ await processor.removeAiFiles(oldToolFiles);
5978
6168
  }
6169
+ const rulesyncFiles = await processor.loadRulesyncFiles();
6170
+ const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
6171
+ const writtenCount = await processor.writeAiFiles(toolFiles);
6172
+ totalMcpOutputs += writtenCount;
6173
+ logger.success(`Generated ${writtenCount} ${toolTarget} MCP configuration(s) in ${baseDir}`);
5979
6174
  }
5980
- } else {
5981
- logger.info("Skipping command file generation (not in --features)");
5982
6175
  }
5983
- let totalIgnoreOutputs = 0;
5984
- if (config.getFeatures().includes("ignore")) {
5985
- logger.info("Generating ignore files...");
5986
- for (const toolTarget of intersection(config.getTargets(), IgnoreProcessor.getToolTargets())) {
5987
- for (const baseDir of config.getBaseDirs()) {
5988
- try {
5989
- const processor = new IgnoreProcessor({
5990
- baseDir: baseDir === process.cwd() ? "." : baseDir,
5991
- toolTarget
5992
- });
5993
- if (config.getDelete()) {
5994
- const oldToolFiles = await processor.loadToolFilesToDelete();
5995
- await processor.removeAiFiles(oldToolFiles);
5996
- }
5997
- const rulesyncFiles = await processor.loadRulesyncFiles();
5998
- if (rulesyncFiles.length > 0) {
5999
- const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
6000
- const writtenCount = await processor.writeAiFiles(toolFiles);
6001
- totalIgnoreOutputs += writtenCount;
6002
- logger.success(`Generated ${writtenCount} ${toolTarget} ignore file(s) in ${baseDir}`);
6003
- }
6004
- } catch (error) {
6005
- logger.warn(
6006
- `Failed to generate ${toolTarget} ignore files for ${baseDir}:`,
6007
- error instanceof Error ? error.message : String(error)
6008
- );
6009
- continue;
6010
- }
6176
+ return totalMcpOutputs;
6177
+ }
6178
+ async function generateCommands(config) {
6179
+ if (!config.getFeatures().includes("commands")) {
6180
+ logger.debug("Skipping command file generation (not in --features)");
6181
+ return 0;
6182
+ }
6183
+ if (config.getExperimentalGlobal()) {
6184
+ logger.debug("Skipping command file generation (not supported in global mode)");
6185
+ return 0;
6186
+ }
6187
+ let totalCommandOutputs = 0;
6188
+ logger.info("Generating command files...");
6189
+ for (const baseDir of config.getBaseDirs()) {
6190
+ for (const toolTarget of intersection(
6191
+ config.getTargets(),
6192
+ CommandsProcessor.getToolTargets({
6193
+ includeSimulated: config.getExperimentalSimulateCommands()
6194
+ })
6195
+ )) {
6196
+ const processor = new CommandsProcessor({
6197
+ baseDir,
6198
+ toolTarget
6199
+ });
6200
+ if (config.getDelete()) {
6201
+ const oldToolFiles = await processor.loadToolFilesToDelete();
6202
+ await processor.removeAiFiles(oldToolFiles);
6011
6203
  }
6204
+ const rulesyncFiles = await processor.loadRulesyncFiles();
6205
+ const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
6206
+ const writtenCount = await processor.writeAiFiles(toolFiles);
6207
+ totalCommandOutputs += writtenCount;
6208
+ logger.success(`Generated ${writtenCount} ${toolTarget} command(s) in ${baseDir}`);
6012
6209
  }
6013
6210
  }
6211
+ return totalCommandOutputs;
6212
+ }
6213
+ async function generateSubagents(config) {
6214
+ if (!config.getFeatures().includes("subagents")) {
6215
+ logger.debug("Skipping subagent file generation (not in --features)");
6216
+ return 0;
6217
+ }
6218
+ if (config.getExperimentalGlobal()) {
6219
+ logger.debug("Skipping subagent file generation (not supported in global mode)");
6220
+ return 0;
6221
+ }
6014
6222
  let totalSubagentOutputs = 0;
6015
- if (config.getFeatures().includes("subagents")) {
6016
- logger.info("Generating subagent files...");
6017
- for (const baseDir of config.getBaseDirs()) {
6018
- for (const toolTarget of intersection(
6019
- config.getTargets(),
6020
- SubagentsProcessor.getToolTargets({
6021
- includeSimulated: config.getExperimentalSimulateSubagents()
6022
- })
6023
- )) {
6024
- const processor = new SubagentsProcessor({
6025
- baseDir,
6026
- toolTarget
6027
- });
6028
- if (config.getDelete()) {
6029
- const oldToolFiles = await processor.loadToolFilesToDelete();
6030
- await processor.removeAiFiles(oldToolFiles);
6031
- }
6032
- const rulesyncFiles = await processor.loadRulesyncFiles();
6033
- const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
6034
- const writtenCount = await processor.writeAiFiles(toolFiles);
6035
- totalSubagentOutputs += writtenCount;
6036
- logger.success(`Generated ${writtenCount} ${toolTarget} subagent(s) in ${baseDir}`);
6223
+ logger.info("Generating subagent files...");
6224
+ for (const baseDir of config.getBaseDirs()) {
6225
+ for (const toolTarget of intersection(
6226
+ config.getTargets(),
6227
+ SubagentsProcessor.getToolTargets({
6228
+ includeSimulated: config.getExperimentalSimulateSubagents()
6229
+ })
6230
+ )) {
6231
+ const processor = new SubagentsProcessor({
6232
+ baseDir,
6233
+ toolTarget
6234
+ });
6235
+ if (config.getDelete()) {
6236
+ const oldToolFiles = await processor.loadToolFilesToDelete();
6237
+ await processor.removeAiFiles(oldToolFiles);
6037
6238
  }
6239
+ const rulesyncFiles = await processor.loadRulesyncFiles();
6240
+ const toolFiles = await processor.convertRulesyncFilesToToolFiles(rulesyncFiles);
6241
+ const writtenCount = await processor.writeAiFiles(toolFiles);
6242
+ totalSubagentOutputs += writtenCount;
6243
+ logger.success(`Generated ${writtenCount} ${toolTarget} subagent(s) in ${baseDir}`);
6038
6244
  }
6039
6245
  }
6040
- const totalGenerated = totalRulesOutputs + totalMcpOutputs + totalCommandOutputs + totalIgnoreOutputs + totalSubagentOutputs;
6041
- if (totalGenerated === 0) {
6042
- const enabledFeatures = config.getFeatures().join(", ");
6043
- logger.warn(`\u26A0\uFE0F No files generated for enabled features: ${enabledFeatures}`);
6044
- return;
6045
- }
6046
- if (totalGenerated > 0) {
6047
- const parts = [];
6048
- if (totalRulesOutputs > 0) parts.push(`${totalRulesOutputs} rules`);
6049
- if (totalIgnoreOutputs > 0) parts.push(`${totalIgnoreOutputs} ignore files`);
6050
- if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP files`);
6051
- if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
6052
- if (totalSubagentOutputs > 0) parts.push(`${totalSubagentOutputs} subagents`);
6053
- logger.success(`\u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`);
6054
- }
6246
+ return totalSubagentOutputs;
6055
6247
  }
6056
6248
 
6057
6249
  // src/cli/commands/gitignore.ts
@@ -6148,116 +6340,186 @@ async function importCommand(options) {
6148
6340
  const config = await ConfigResolver.resolve(options);
6149
6341
  logger.setVerbose(config.getVerbose());
6150
6342
  const tool = config.getTargets()[0];
6151
- let rulesCreated = 0;
6152
- if (config.getFeatures().includes("rules")) {
6153
- if (RulesProcessor.getToolTargets().includes(tool)) {
6154
- const rulesProcessor = new RulesProcessor({
6155
- baseDir: ".",
6156
- toolTarget: tool
6157
- });
6158
- const toolFiles = await rulesProcessor.loadToolFiles();
6159
- if (toolFiles.length > 0) {
6160
- const rulesyncFiles = await rulesProcessor.convertToolFilesToRulesyncFiles(toolFiles);
6161
- const writtenCount = await rulesProcessor.writeAiFiles(rulesyncFiles);
6162
- rulesCreated = writtenCount;
6163
- }
6164
- if (config.getVerbose() && rulesCreated > 0) {
6165
- logger.success(`Created ${rulesCreated} rule files`);
6166
- }
6167
- }
6343
+ await importRules(config, tool);
6344
+ await importIgnore(config, tool);
6345
+ await importMcp(config, tool);
6346
+ await importCommands(config, tool);
6347
+ await importSubagents(config, tool);
6348
+ }
6349
+ async function importRules(config, tool) {
6350
+ if (!config.getFeatures().includes("rules")) {
6351
+ return 0;
6352
+ }
6353
+ if (!RulesProcessor.getToolTargets().includes(tool)) {
6354
+ return 0;
6355
+ }
6356
+ const global = config.getExperimentalGlobal();
6357
+ if (global && !RulesProcessor.getToolTargetsGlobal().includes(tool)) {
6358
+ logger.error(`${tool} is not supported in global mode`);
6359
+ return 0;
6360
+ }
6361
+ const rulesProcessor = new RulesProcessor({
6362
+ baseDir: ".",
6363
+ toolTarget: tool,
6364
+ global
6365
+ });
6366
+ const toolFiles = await rulesProcessor.loadToolFiles();
6367
+ if (toolFiles.length === 0) {
6368
+ return 0;
6168
6369
  }
6169
- let ignoreFileCreated = 0;
6170
- if (config.getFeatures().includes("ignore")) {
6171
- if (IgnoreProcessor.getToolTargets().includes(tool)) {
6172
- const ignoreProcessor = new IgnoreProcessor({
6173
- baseDir: ".",
6174
- toolTarget: tool
6175
- });
6176
- const toolFiles = await ignoreProcessor.loadToolFiles();
6177
- if (toolFiles.length > 0) {
6178
- const rulesyncFiles = await ignoreProcessor.convertToolFilesToRulesyncFiles(toolFiles);
6179
- const writtenCount = await ignoreProcessor.writeAiFiles(rulesyncFiles);
6180
- ignoreFileCreated = writtenCount;
6181
- if (config.getVerbose()) {
6182
- logger.success(
6183
- `Created ignore files from ${toolFiles.length} tool ignore configurations`
6184
- );
6185
- }
6186
- }
6187
- }
6188
- if (config.getVerbose() && ignoreFileCreated > 0) {
6189
- logger.success(`Created ${ignoreFileCreated} ignore files`);
6190
- }
6370
+ const rulesyncFiles = await rulesProcessor.convertToolFilesToRulesyncFiles(toolFiles);
6371
+ const writtenCount = await rulesProcessor.writeAiFiles(rulesyncFiles);
6372
+ if (config.getVerbose() && writtenCount > 0) {
6373
+ logger.success(`Created ${writtenCount} rule files`);
6191
6374
  }
6192
- let mcpCreated = 0;
6193
- if (config.getFeatures().includes("mcp")) {
6194
- if (McpProcessor.getToolTargets().includes(tool)) {
6195
- const mcpProcessor = new McpProcessor({
6196
- baseDir: ".",
6197
- toolTarget: tool
6198
- });
6199
- const toolFiles = await mcpProcessor.loadToolFiles();
6200
- if (toolFiles.length > 0) {
6201
- const rulesyncFiles = await mcpProcessor.convertToolFilesToRulesyncFiles(toolFiles);
6202
- const writtenCount = await mcpProcessor.writeAiFiles(rulesyncFiles);
6203
- mcpCreated = writtenCount;
6204
- }
6205
- }
6375
+ return writtenCount;
6376
+ }
6377
+ async function importIgnore(config, tool) {
6378
+ if (!config.getFeatures().includes("ignore")) {
6379
+ return 0;
6206
6380
  }
6207
- if (config.getVerbose() && mcpCreated > 0) {
6208
- logger.success(`Created ${mcpCreated} MCP files`);
6381
+ if (config.getExperimentalGlobal()) {
6382
+ logger.debug("Skipping ignore file import (not supported in global mode)");
6383
+ return 0;
6209
6384
  }
6210
- let subagentsCreated = 0;
6211
- if (config.getFeatures().includes("subagents")) {
6212
- const supportedTargets = SubagentsProcessor.getToolTargets({ includeSimulated: false });
6213
- if (supportedTargets.includes(tool)) {
6214
- const subagentsProcessor = new SubagentsProcessor({
6215
- baseDir: ".",
6216
- toolTarget: tool
6217
- });
6218
- const toolFiles = await subagentsProcessor.loadToolFiles();
6219
- if (toolFiles.length > 0) {
6220
- const rulesyncFiles = await subagentsProcessor.convertToolFilesToRulesyncFiles(toolFiles);
6221
- const writtenCount = await subagentsProcessor.writeAiFiles(rulesyncFiles);
6222
- subagentsCreated += writtenCount;
6223
- }
6224
- }
6225
- if (config.getVerbose() && subagentsCreated > 0) {
6226
- logger.success(`Created ${subagentsCreated} subagent files`);
6227
- }
6385
+ if (!IgnoreProcessor.getToolTargets().includes(tool)) {
6386
+ return 0;
6228
6387
  }
6229
- let commandsCreated = 0;
6230
- if (config.getFeatures().includes("commands")) {
6231
- const supportedTargets = CommandsProcessor.getToolTargets({ includeSimulated: false });
6232
- if (supportedTargets.includes(tool)) {
6233
- const commandsProcessor = new CommandsProcessor({
6234
- baseDir: ".",
6235
- toolTarget: tool
6236
- });
6237
- const toolFiles = await commandsProcessor.loadToolFiles();
6238
- if (toolFiles.length > 0) {
6239
- const rulesyncFiles = await commandsProcessor.convertToolFilesToRulesyncFiles(toolFiles);
6240
- const writtenCount = await commandsProcessor.writeAiFiles(rulesyncFiles);
6241
- commandsCreated = writtenCount;
6242
- }
6243
- }
6244
- if (config.getVerbose() && commandsCreated > 0) {
6245
- logger.success(`Created ${commandsCreated} command files`);
6246
- }
6388
+ const ignoreProcessor = new IgnoreProcessor({
6389
+ baseDir: ".",
6390
+ toolTarget: tool
6391
+ });
6392
+ const toolFiles = await ignoreProcessor.loadToolFiles();
6393
+ if (toolFiles.length === 0) {
6394
+ return 0;
6395
+ }
6396
+ const rulesyncFiles = await ignoreProcessor.convertToolFilesToRulesyncFiles(toolFiles);
6397
+ const writtenCount = await ignoreProcessor.writeAiFiles(rulesyncFiles);
6398
+ if (config.getVerbose()) {
6399
+ logger.success(`Created ignore files from ${toolFiles.length} tool ignore configurations`);
6400
+ }
6401
+ if (config.getVerbose() && writtenCount > 0) {
6402
+ logger.success(`Created ${writtenCount} ignore files`);
6403
+ }
6404
+ return writtenCount;
6405
+ }
6406
+ async function importMcp(config, tool) {
6407
+ if (!config.getFeatures().includes("mcp")) {
6408
+ return 0;
6409
+ }
6410
+ if (config.getExperimentalGlobal()) {
6411
+ logger.debug("Skipping MCP file import (not supported in global mode)");
6412
+ return 0;
6413
+ }
6414
+ if (!McpProcessor.getToolTargets().includes(tool)) {
6415
+ return 0;
6416
+ }
6417
+ const mcpProcessor = new McpProcessor({
6418
+ baseDir: ".",
6419
+ toolTarget: tool
6420
+ });
6421
+ const toolFiles = await mcpProcessor.loadToolFiles();
6422
+ if (toolFiles.length === 0) {
6423
+ return 0;
6247
6424
  }
6425
+ const rulesyncFiles = await mcpProcessor.convertToolFilesToRulesyncFiles(toolFiles);
6426
+ const writtenCount = await mcpProcessor.writeAiFiles(rulesyncFiles);
6427
+ if (config.getVerbose() && writtenCount > 0) {
6428
+ logger.success(`Created ${writtenCount} MCP files`);
6429
+ }
6430
+ return writtenCount;
6431
+ }
6432
+ async function importCommands(config, tool) {
6433
+ if (!config.getFeatures().includes("commands")) {
6434
+ return 0;
6435
+ }
6436
+ if (config.getExperimentalGlobal()) {
6437
+ logger.debug("Skipping command file import (not supported in global mode)");
6438
+ return 0;
6439
+ }
6440
+ const supportedTargets = CommandsProcessor.getToolTargets({ includeSimulated: false });
6441
+ if (!supportedTargets.includes(tool)) {
6442
+ return 0;
6443
+ }
6444
+ const commandsProcessor = new CommandsProcessor({
6445
+ baseDir: ".",
6446
+ toolTarget: tool
6447
+ });
6448
+ const toolFiles = await commandsProcessor.loadToolFiles();
6449
+ if (toolFiles.length === 0) {
6450
+ return 0;
6451
+ }
6452
+ const rulesyncFiles = await commandsProcessor.convertToolFilesToRulesyncFiles(toolFiles);
6453
+ const writtenCount = await commandsProcessor.writeAiFiles(rulesyncFiles);
6454
+ if (config.getVerbose() && writtenCount > 0) {
6455
+ logger.success(`Created ${writtenCount} command files`);
6456
+ }
6457
+ return writtenCount;
6458
+ }
6459
+ async function importSubagents(config, tool) {
6460
+ if (!config.getFeatures().includes("subagents")) {
6461
+ return 0;
6462
+ }
6463
+ if (config.getExperimentalGlobal()) {
6464
+ logger.debug("Skipping subagent file import (not supported in global mode)");
6465
+ return 0;
6466
+ }
6467
+ const supportedTargets = SubagentsProcessor.getToolTargets({ includeSimulated: false });
6468
+ if (!supportedTargets.includes(tool)) {
6469
+ return 0;
6470
+ }
6471
+ const subagentsProcessor = new SubagentsProcessor({
6472
+ baseDir: ".",
6473
+ toolTarget: tool
6474
+ });
6475
+ const toolFiles = await subagentsProcessor.loadToolFiles();
6476
+ if (toolFiles.length === 0) {
6477
+ return 0;
6478
+ }
6479
+ const rulesyncFiles = await subagentsProcessor.convertToolFilesToRulesyncFiles(toolFiles);
6480
+ const writtenCount = await subagentsProcessor.writeAiFiles(rulesyncFiles);
6481
+ if (config.getVerbose() && writtenCount > 0) {
6482
+ logger.success(`Created ${writtenCount} subagent files`);
6483
+ }
6484
+ return writtenCount;
6248
6485
  }
6249
6486
 
6250
6487
  // src/cli/commands/init.ts
6251
6488
  import { join as join56 } from "path";
6252
6489
  async function initCommand() {
6253
6490
  logger.info("Initializing rulesync...");
6254
- await ensureDir(RULESYNC_DIR);
6491
+ await ensureDir(".rulesync");
6255
6492
  await createSampleFiles();
6493
+ await createConfigFile();
6256
6494
  logger.success("rulesync initialized successfully!");
6257
6495
  logger.info("Next steps:");
6258
- logger.info(`1. Edit rule files in ${RULESYNC_RULES_DIR}/`);
6496
+ logger.info(`1. Edit rule files in .rulesync/rules/`);
6259
6497
  logger.info("2. Run 'rulesync generate' to create configuration files");
6260
6498
  }
6499
+ async function createConfigFile() {
6500
+ if (await fileExists("rulesync.jsonc")) {
6501
+ logger.info("Skipped rulesync.jsonc (already exists)");
6502
+ return;
6503
+ }
6504
+ await writeFileContent(
6505
+ "rulesync.jsonc",
6506
+ JSON.stringify(
6507
+ {
6508
+ targets: ["copilot", "cursor", "claudecode", "codexcli"],
6509
+ features: ["rules", "ignore", "mcp", "commands", "subagents"],
6510
+ baseDirs: ["."],
6511
+ delete: true,
6512
+ verbose: false,
6513
+ experimentalGlobal: false,
6514
+ experimentalSimulateCommands: false,
6515
+ experimentalSimulateSubagents: false
6516
+ },
6517
+ null,
6518
+ 2
6519
+ )
6520
+ );
6521
+ logger.success("Created rulesync.jsonc");
6522
+ }
6261
6523
  async function createSampleFiles() {
6262
6524
  const sampleFile = {
6263
6525
  filename: "overview.md",
@@ -6294,10 +6556,10 @@ globs: ["**/*"]
6294
6556
  - Follow single responsibility principle
6295
6557
  `
6296
6558
  };
6297
- const filepath = join56(RULESYNC_RULES_DIR, sampleFile.filename);
6298
- await ensureDir(RULESYNC_RULES_DIR);
6559
+ const filepath = join56(".rulesync/rules", sampleFile.filename);
6560
+ await ensureDir(".rulesync/rules");
6299
6561
  await ensureDir(RulesyncCommand.getSettablePaths().relativeDirPath);
6300
- await ensureDir(RULESYNC_SUBAGENTS_DIR);
6562
+ await ensureDir(".rulesync/subagents");
6301
6563
  if (!await fileExists(filepath)) {
6302
6564
  await writeFileContent(filepath, sampleFile.content);
6303
6565
  logger.success(`Created ${filepath}`);
@@ -6307,7 +6569,7 @@ globs: ["**/*"]
6307
6569
  }
6308
6570
 
6309
6571
  // src/cli/index.ts
6310
- var getVersion = () => "1.2.5";
6572
+ var getVersion = () => "2.0.0";
6311
6573
  var main = async () => {
6312
6574
  const program = new Command();
6313
6575
  const version = getVersion();
@@ -6331,7 +6593,7 @@ var main = async () => {
6331
6593
  (value) => {
6332
6594
  return value.split(",").map((f) => f.trim());
6333
6595
  }
6334
- ).option("-V, --verbose", "Verbose output").action(async (options) => {
6596
+ ).option("-V, --verbose", "Verbose output").option("-g, --experimental-global", "Import for global(user scope) configuration files").action(async (options) => {
6335
6597
  try {
6336
6598
  await importCommand({
6337
6599
  targets: options.targets,
@@ -6344,7 +6606,7 @@ var main = async () => {
6344
6606
  process.exit(1);
6345
6607
  }
6346
6608
  });
6347
- program.command("generate").description("Generate configuration files for AI tools").option("--all", "[DEPRECATED] Generate for all supported AI tools (use --targets * instead)").option(
6609
+ program.command("generate").description("Generate configuration files for AI tools").option(
6348
6610
  "-t, --targets <tools>",
6349
6611
  "Comma-separated list of tools to generate for (e.g., 'copilot,cursor,cline' or '*' for all)",
6350
6612
  (value) => {
@@ -6359,7 +6621,7 @@ var main = async () => {
6359
6621
  ).option("--delete", "Delete all existing files in output directories before generating").option(
6360
6622
  "-b, --base-dir <paths>",
6361
6623
  "Base directories to generate files (comma-separated for multiple paths)"
6362
- ).option("-V, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option(
6624
+ ).option("-V, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("-g, --experimental-global", "Generate for global(user scope) configuration files").option(
6363
6625
  "--experimental-simulate-commands",
6364
6626
  "Generate simulated commands (experimental feature). This feature is only available for copilot, cursor and codexcli."
6365
6627
  ).option(
@@ -6374,6 +6636,7 @@ var main = async () => {
6374
6636
  delete: options.delete,
6375
6637
  baseDirs: options.baseDirs,
6376
6638
  configPath: options.config,
6639
+ experimentalGlobal: options.experimentalGlobal,
6377
6640
  experimentalSimulateCommands: options.experimentalSimulateCommands,
6378
6641
  experimentalSimulateSubagents: options.experimentalSimulateSubagents
6379
6642
  });
@@ -6382,7 +6645,6 @@ var main = async () => {
6382
6645
  process.exit(1);
6383
6646
  }
6384
6647
  });
6385
- program.command("config").description("Show or initialize rulesync configuration").option("--init", "Initialize a new configuration file").action(configCommand);
6386
6648
  program.parse();
6387
6649
  };
6388
6650
  main().catch((error) => {