rulesync 0.65.0 → 0.67.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 (33) hide show
  1. package/README.md +86 -47
  2. package/dist/{amazonqcli-MW7XTVPN.js → amazonqcli-PWXCSRAN.js} +2 -2
  3. package/dist/{augmentcode-WCZCL7VR.js → augmentcode-4AFYW4BU.js} +2 -2
  4. package/dist/{chunk-YC2BC7Z2.js → chunk-35VMCHXQ.js} +1 -1
  5. package/dist/{chunk-6SLEITCQ.js → chunk-3QGD3CH5.js} +1 -1
  6. package/dist/{chunk-VRWNZTGW.js → chunk-5GKH5TQ4.js} +1 -1
  7. package/dist/{chunk-4NWMCTN5.js → chunk-7BIZ5Y6F.js} +1 -1
  8. package/dist/{chunk-LTWEI4PW.js → chunk-7QVQO6MQ.js} +1 -1
  9. package/dist/{chunk-UGY5ALND.js → chunk-B2HD24KC.js} +1 -1
  10. package/dist/{chunk-M2AUM37M.js → chunk-CS7AV6JT.js} +1 -0
  11. package/dist/{chunk-6AXPFPKI.js → chunk-KONQNQY3.js} +1 -1
  12. package/dist/{chunk-FL5BF6JM.js → chunk-OARJESSZ.js} +1 -1
  13. package/dist/{chunk-JXOLLTNV.js → chunk-V36ICGOY.js} +1 -1
  14. package/dist/{chunk-GIAQWZQ4.js → chunk-WCON5BAI.js} +1 -1
  15. package/dist/{chunk-DM2B7XUB.js → chunk-WFOWHPBC.js} +1 -1
  16. package/dist/{chunk-N6DASHJL.js → chunk-WYYQXVHC.js} +1 -1
  17. package/dist/{chunk-I4NVS7GE.js → chunk-YZUDL4GW.js} +1 -1
  18. package/dist/{chunk-TX2CE4RR.js → chunk-ZMGXHLYP.js} +1 -1
  19. package/dist/{claudecode-RZSJPPBU.js → claudecode-OC7VHCF6.js} +3 -3
  20. package/dist/{cline-JTWWBQQ4.js → cline-A4KFSAQE.js} +3 -3
  21. package/dist/{codexcli-ATMFGRJR.js → codexcli-YP3X7FWB.js} +3 -3
  22. package/dist/{copilot-H3CLGKDP.js → copilot-3LIMXQ7O.js} +2 -2
  23. package/dist/{cursor-ZUN5RZU6.js → cursor-QV72CDJC.js} +3 -3
  24. package/dist/{geminicli-Q5HPIQCU.js → geminicli-XPSJJS65.js} +3 -3
  25. package/dist/index.cjs +1888 -518
  26. package/dist/index.js +1866 -511
  27. package/dist/{junie-JCLVC3MI.js → junie-265XIW43.js} +3 -3
  28. package/dist/{kiro-CNF6433S.js → kiro-6OHFHN5X.js} +2 -2
  29. package/dist/{opencode-EBS3CED2.js → opencode-C5QAYVJ5.js} +2 -2
  30. package/dist/{qwencode-JIT6KW7E.js → qwencode-5RR24UW6.js} +3 -3
  31. package/dist/{roo-KBTRH4TZ.js → roo-5IXVBUHD.js} +3 -3
  32. package/dist/{windsurf-ZAAWL6JJ.js → windsurf-DVQUJJJ5.js} +3 -3
  33. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-6AXPFPKI.js";
3
- import "./chunk-N6DASHJL.js";
4
- import "./chunk-6SLEITCQ.js";
5
- import "./chunk-TX2CE4RR.js";
6
- import "./chunk-VRWNZTGW.js";
7
- import "./chunk-LTWEI4PW.js";
2
+ import "./chunk-KONQNQY3.js";
3
+ import "./chunk-WYYQXVHC.js";
4
+ import "./chunk-3QGD3CH5.js";
5
+ import "./chunk-ZMGXHLYP.js";
6
+ import "./chunk-5GKH5TQ4.js";
7
+ import "./chunk-7QVQO6MQ.js";
8
8
  import {
9
9
  ensureDir,
10
10
  fileExists,
@@ -16,119 +16,133 @@ import {
16
16
  removeDirectory,
17
17
  resolvePath,
18
18
  writeFileContent
19
- } from "./chunk-DM2B7XUB.js";
20
- import "./chunk-I4NVS7GE.js";
21
- import "./chunk-YC2BC7Z2.js";
22
- import "./chunk-FL5BF6JM.js";
23
- import "./chunk-UGY5ALND.js";
24
- import "./chunk-4NWMCTN5.js";
25
- import "./chunk-GIAQWZQ4.js";
26
- import "./chunk-JXOLLTNV.js";
19
+ } from "./chunk-WFOWHPBC.js";
20
+ import "./chunk-YZUDL4GW.js";
21
+ import "./chunk-35VMCHXQ.js";
22
+ import "./chunk-OARJESSZ.js";
23
+ import "./chunk-B2HD24KC.js";
24
+ import "./chunk-7BIZ5Y6F.js";
25
+ import "./chunk-WCON5BAI.js";
26
+ import "./chunk-V36ICGOY.js";
27
27
  import {
28
28
  ALL_TOOL_TARGETS,
29
29
  RulesyncTargetsSchema,
30
30
  ToolTargetSchema,
31
31
  ToolTargetsSchema,
32
32
  isToolTarget
33
- } from "./chunk-M2AUM37M.js";
33
+ } from "./chunk-CS7AV6JT.js";
34
34
 
35
35
  // src/cli/index.ts
36
36
  import { Command } from "commander";
37
37
 
38
- // src/types/claudecode.ts
38
+ // src/types/config-options.ts
39
39
  import { z } from "zod/mini";
40
- var ClaudeSettingsSchema = z.looseObject({
41
- permissions: z._default(
42
- z.looseObject({
43
- deny: z._default(z.array(z.string()), [])
40
+ var FEATURE_TYPES = ["rules", "commands", "mcp", "ignore", "subagents"];
41
+ var FeatureTypeSchema = z.enum(FEATURE_TYPES);
42
+ var FeaturesSchema = z.union([z.array(FeatureTypeSchema), z.literal("*")]);
43
+ var OutputPathsSchema = z.object({
44
+ agentsmd: z.optional(z.string()),
45
+ amazonqcli: z.optional(z.string()),
46
+ augmentcode: z.optional(z.string()),
47
+ "augmentcode-legacy": z.optional(z.string()),
48
+ copilot: z.optional(z.string()),
49
+ cursor: z.optional(z.string()),
50
+ cline: z.optional(z.string()),
51
+ claudecode: z.optional(z.string()),
52
+ codexcli: z.optional(z.string()),
53
+ opencode: z.optional(z.string()),
54
+ qwencode: z.optional(z.string()),
55
+ roo: z.optional(z.string()),
56
+ geminicli: z.optional(z.string()),
57
+ kiro: z.optional(z.string()),
58
+ junie: z.optional(z.string()),
59
+ windsurf: z.optional(z.string())
60
+ });
61
+ var ConfigOptionsSchema = z.object({
62
+ aiRulesDir: z.optional(z.string()),
63
+ outputPaths: z.optional(OutputPathsSchema),
64
+ watchEnabled: z.optional(z.boolean()),
65
+ defaultTargets: z.optional(ToolTargetsSchema),
66
+ targets: z.optional(z.array(ToolTargetSchema)),
67
+ exclude: z.optional(z.array(ToolTargetSchema)),
68
+ features: z.optional(FeaturesSchema),
69
+ verbose: z.optional(z.boolean()),
70
+ delete: z.optional(z.boolean()),
71
+ baseDir: z.optional(z.union([z.string(), z.array(z.string())])),
72
+ legacy: z.optional(z.boolean()),
73
+ watch: z.optional(
74
+ z.object({
75
+ enabled: z.optional(z.boolean()),
76
+ interval: z.optional(z.number()),
77
+ ignore: z.optional(z.array(z.string()))
78
+ })
79
+ )
80
+ });
81
+ var MergedConfigSchema = z.object({
82
+ aiRulesDir: z.string(),
83
+ outputPaths: z.record(ToolTargetSchema, z.string()),
84
+ watchEnabled: z.boolean(),
85
+ defaultTargets: ToolTargetsSchema,
86
+ targets: z.optional(z.array(ToolTargetSchema)),
87
+ exclude: z.optional(z.array(ToolTargetSchema)),
88
+ features: z.optional(FeaturesSchema),
89
+ verbose: z.optional(z.boolean()),
90
+ delete: z.optional(z.boolean()),
91
+ baseDir: z.optional(z.union([z.string(), z.array(z.string())])),
92
+ configPath: z.optional(z.string()),
93
+ legacy: z.optional(z.boolean()),
94
+ watch: z.optional(
95
+ z.object({
96
+ enabled: z.optional(z.boolean()),
97
+ interval: z.optional(z.number()),
98
+ ignore: z.optional(z.array(z.string()))
99
+ })
100
+ )
101
+ });
102
+
103
+ // src/cli/commands/add.ts
104
+ import { mkdir, writeFile } from "fs/promises";
105
+ import * as path from "path";
106
+
107
+ // src/utils/config-loader.ts
108
+ import { loadConfig as loadC12Config } from "c12";
109
+ import { $ZodError } from "zod/v4/core";
110
+
111
+ // src/types/claudecode.ts
112
+ import { z as z2 } from "zod/mini";
113
+ var ClaudeSettingsSchema = z2.looseObject({
114
+ permissions: z2._default(
115
+ z2.looseObject({
116
+ deny: z2._default(z2.array(z2.string()), [])
44
117
  }),
45
118
  { deny: [] }
46
119
  )
47
120
  });
48
121
 
49
122
  // src/types/shared.ts
50
- import { z as z2 } from "zod/mini";
51
- var OutputSchema = z2.object({
123
+ import { z as z3 } from "zod/mini";
124
+ var OutputSchema = z3.object({
52
125
  tool: ToolTargetSchema,
53
- filepath: z2.string(),
54
- content: z2.string()
126
+ filepath: z3.string(),
127
+ content: z3.string()
55
128
  });
56
- var BaseFrontmatterSchema = z2.object({
57
- description: z2.optional(z2.string())
129
+ var BaseFrontmatterSchema = z3.object({
130
+ description: z3.optional(z3.string())
58
131
  });
59
132
 
60
133
  // src/types/commands.ts
61
134
  var CommandFrontmatterSchema = BaseFrontmatterSchema;
62
135
 
63
136
  // src/types/config.ts
64
- import { z as z3 } from "zod/mini";
65
- var ConfigSchema = z3.object({
66
- aiRulesDir: z3.string(),
67
- outputPaths: z3.record(ToolTargetSchema, z3.string()),
68
- watchEnabled: z3.boolean(),
69
- defaultTargets: ToolTargetsSchema,
70
- claudecodeCommands: z3.optional(z3.string()),
71
- geminicliCommands: z3.optional(z3.string()),
72
- legacy: z3.optional(z3.boolean())
73
- });
74
-
75
- // src/types/config-options.ts
76
137
  import { z as z4 } from "zod/mini";
77
- var OutputPathsSchema = z4.object({
78
- amazonqcli: z4.optional(z4.string()),
79
- augmentcode: z4.optional(z4.string()),
80
- "augmentcode-legacy": z4.optional(z4.string()),
81
- copilot: z4.optional(z4.string()),
82
- cursor: z4.optional(z4.string()),
83
- cline: z4.optional(z4.string()),
84
- claudecode: z4.optional(z4.string()),
85
- codexcli: z4.optional(z4.string()),
86
- opencode: z4.optional(z4.string()),
87
- qwencode: z4.optional(z4.string()),
88
- roo: z4.optional(z4.string()),
89
- geminicli: z4.optional(z4.string()),
90
- kiro: z4.optional(z4.string()),
91
- junie: z4.optional(z4.string()),
92
- windsurf: z4.optional(z4.string())
93
- });
94
- var ConfigOptionsSchema = z4.object({
95
- aiRulesDir: z4.optional(z4.string()),
96
- outputPaths: z4.optional(OutputPathsSchema),
97
- watchEnabled: z4.optional(z4.boolean()),
98
- defaultTargets: z4.optional(ToolTargetsSchema),
99
- targets: z4.optional(z4.array(ToolTargetSchema)),
100
- exclude: z4.optional(z4.array(ToolTargetSchema)),
101
- verbose: z4.optional(z4.boolean()),
102
- delete: z4.optional(z4.boolean()),
103
- baseDir: z4.optional(z4.union([z4.string(), z4.array(z4.string())])),
104
- legacy: z4.optional(z4.boolean()),
105
- watch: z4.optional(
106
- z4.object({
107
- enabled: z4.optional(z4.boolean()),
108
- interval: z4.optional(z4.number()),
109
- ignore: z4.optional(z4.array(z4.string()))
110
- })
111
- )
112
- });
113
- var MergedConfigSchema = z4.object({
138
+ var ConfigSchema = z4.object({
114
139
  aiRulesDir: z4.string(),
115
140
  outputPaths: z4.record(ToolTargetSchema, z4.string()),
116
141
  watchEnabled: z4.boolean(),
117
142
  defaultTargets: ToolTargetsSchema,
118
- targets: z4.optional(z4.array(ToolTargetSchema)),
119
- exclude: z4.optional(z4.array(ToolTargetSchema)),
120
- verbose: z4.optional(z4.boolean()),
121
- delete: z4.optional(z4.boolean()),
122
- baseDir: z4.optional(z4.union([z4.string(), z4.array(z4.string())])),
123
- configPath: z4.optional(z4.string()),
124
- legacy: z4.optional(z4.boolean()),
125
- watch: z4.optional(
126
- z4.object({
127
- enabled: z4.optional(z4.boolean()),
128
- interval: z4.optional(z4.number()),
129
- ignore: z4.optional(z4.array(z4.string()))
130
- })
131
- )
143
+ claudecodeCommands: z4.optional(z4.string()),
144
+ geminicliCommands: z4.optional(z4.string()),
145
+ legacy: z4.optional(z4.boolean())
132
146
  });
133
147
 
134
148
  // src/types/mcp.ts
@@ -181,19 +195,12 @@ var GenerateOptionsSchema = z6.object({
181
195
  watch: z6.optional(z6.boolean())
182
196
  });
183
197
 
184
- // src/cli/commands/add.ts
185
- import { mkdir, writeFile } from "fs/promises";
186
- import * as path from "path";
187
-
188
- // src/utils/config-loader.ts
189
- import { loadConfig as loadC12Config } from "c12";
190
- import { $ZodError } from "zod/v4/core";
191
-
192
198
  // src/utils/config.ts
193
199
  function getDefaultConfig() {
194
200
  return {
195
201
  aiRulesDir: ".rulesync",
196
202
  outputPaths: {
203
+ agentsmd: ".agents/memories",
197
204
  amazonqcli: ".amazonq/rules",
198
205
  augmentcode: ".",
199
206
  "augmentcode-legacy": ".",
@@ -311,6 +318,11 @@ function generateMinimalConfig(options) {
311
318
  if (comma) lines[lines.length - 1] += comma;
312
319
  lines.push(` "exclude": ${JSON.stringify(options.exclude)}`);
313
320
  }
321
+ if (options.features) {
322
+ const comma = lines.length > 1 ? "," : "";
323
+ if (comma) lines[lines.length - 1] += comma;
324
+ lines.push(` "features": ${JSON.stringify(options.features)}`);
325
+ }
314
326
  if (options.aiRulesDir) {
315
327
  const comma = lines.length > 1 ? "," : "";
316
328
  if (comma) lines[lines.length - 1] += comma;
@@ -351,6 +363,7 @@ function generateMinimalConfig(options) {
351
363
  function generateSampleConfig(options) {
352
364
  const targets = options?.targets || ALL_TOOL_TARGETS;
353
365
  const excludeValue = options?.exclude ? JSON.stringify(options.exclude) : null;
366
+ const featuresValue = options?.features || ["rules", "commands", "mcp", "ignore", "subagents"];
354
367
  const aiRulesDir = options?.aiRulesDir || null;
355
368
  const baseDir = options?.baseDir || null;
356
369
  const deleteFlag = options?.delete || false;
@@ -362,6 +375,10 @@ function generateSampleConfig(options) {
362
375
 
363
376
  // Tools to exclude from generation (overrides targets)
364
377
  ${excludeValue ? `"exclude": ${excludeValue},` : '// "exclude": ["roo"],'}
378
+
379
+ // Features to generate (rules, commands, mcp, ignore, subagents)
380
+ // Use "*" to generate all features, or specify an array of features
381
+ "features": ${JSON.stringify(featuresValue)},
365
382
  ${aiRulesDir ? `
366
383
  // Directory containing AI rule files
367
384
  "aiRulesDir": "${aiRulesDir}",` : ""}
@@ -392,23 +409,6 @@ function generateSampleConfig(options) {
392
409
  }
393
410
  `;
394
411
  }
395
- function mergeWithCliOptions(config, cliOptions) {
396
- const merged = { ...config };
397
- if (cliOptions.verbose !== void 0) {
398
- merged.verbose = cliOptions.verbose;
399
- }
400
- if (cliOptions.delete !== void 0) {
401
- merged.delete = cliOptions.delete;
402
- }
403
- if (cliOptions.baseDirs && cliOptions.baseDirs.length > 0) {
404
- merged.baseDir = cliOptions.baseDirs;
405
- }
406
- if (cliOptions.tools && cliOptions.tools.length > 0) {
407
- merged.defaultTargets = cliOptions.tools;
408
- merged.exclude = void 0;
409
- }
410
- return merged;
411
- }
412
412
 
413
413
  // src/cli/commands/add.ts
414
414
  function sanitizeFilename(filename) {
@@ -511,6 +511,11 @@ Default Targets: ${config.defaultTargets.join(", ")}`);
511
511
  if (config.exclude && config.exclude.length > 0) {
512
512
  logger.log(`Excluded Targets: ${config.exclude.join(", ")}`);
513
513
  }
514
+ if (config.features) {
515
+ const featuresDisplay = config.features === "*" ? "*" : Array.isArray(config.features) ? config.features.join(", ") : String(config.features);
516
+ logger.log(`
517
+ Features: ${featuresDisplay}`);
518
+ }
514
519
  logger.log("\nOutput Paths:");
515
520
  for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
516
521
  logger.log(` ${tool}: ${outputPath}`);
@@ -641,6 +646,10 @@ const config: ConfigOptions = {
641
646
  // Available: ${ALL_TOOL_TARGETS.join(", ")}
642
647
  targets: ${JSON.stringify(ALL_TOOL_TARGETS)},
643
648
 
649
+ // Features to generate (rules, commands, mcp, ignore, subagents)
650
+ // Use "*" to generate all features, or specify an array of features
651
+ features: ["rules", "commands", "mcp", "ignore", "subagents"],
652
+
644
653
  // Custom output paths for specific tools
645
654
  // outputPaths: {
646
655
  // copilot: ".github/copilot-instructions.md",
@@ -662,6 +671,9 @@ export default config;`;
662
671
  if (options.exclude) {
663
672
  configLines.push(` exclude: ${JSON.stringify(options.exclude)}`);
664
673
  }
674
+ if (options.features) {
675
+ configLines.push(` features: ${JSON.stringify(options.features)}`);
676
+ }
665
677
  if (options.aiRulesDir) {
666
678
  configLines.push(` aiRulesDir: "${options.aiRulesDir}"`);
667
679
  }
@@ -958,6 +970,473 @@ async function generateCommands(projectRoot, baseDir, targets) {
958
970
  return outputs;
959
971
  }
960
972
 
973
+ // src/utils/feature-validator.ts
974
+ function isFeatureType(value) {
975
+ return FEATURE_TYPES.some((type) => type === value);
976
+ }
977
+ function validateFeatures(features) {
978
+ if (features === void 0) {
979
+ return "*";
980
+ }
981
+ if (features === "*") {
982
+ return "*";
983
+ }
984
+ if (!Array.isArray(features)) {
985
+ throw new Error('Features must be an array of feature names or "*"');
986
+ }
987
+ if (features.length === 0) {
988
+ throw new Error('Features array cannot be empty. Use "*" to include all features');
989
+ }
990
+ const validFeatures = [];
991
+ const invalidFeatures = [];
992
+ for (const feature of features) {
993
+ if (isFeatureType(feature)) {
994
+ validFeatures.push(feature);
995
+ } else {
996
+ invalidFeatures.push(feature);
997
+ }
998
+ }
999
+ if (invalidFeatures.length > 0) {
1000
+ throw new Error(
1001
+ `Invalid feature types: ${invalidFeatures.join(", ")}. Valid features are: ${FEATURE_TYPES.join(", ")}`
1002
+ );
1003
+ }
1004
+ return [...new Set(validFeatures)];
1005
+ }
1006
+ function expandWildcard() {
1007
+ return [...FEATURE_TYPES];
1008
+ }
1009
+ function normalizeFeatures(features) {
1010
+ if (features === "*" || features === void 0) {
1011
+ return expandWildcard();
1012
+ }
1013
+ return features;
1014
+ }
1015
+
1016
+ // src/core/config/cli-parser.ts
1017
+ var CliParser = class {
1018
+ /**
1019
+ * CLI引数を解析する
1020
+ * @param cliOptions - 生のCLIオプション
1021
+ * @returns 解析されたCliOptions
1022
+ */
1023
+ parse(cliOptions) {
1024
+ const parsed = {};
1025
+ if (cliOptions.tools && Array.isArray(cliOptions.tools) && cliOptions.tools.length > 0) {
1026
+ parsed.tools = cliOptions.tools;
1027
+ }
1028
+ if (cliOptions.features !== void 0) {
1029
+ try {
1030
+ parsed.features = validateFeatures(cliOptions.features);
1031
+ } catch (error) {
1032
+ throw new Error(
1033
+ `Invalid features: ${error instanceof Error ? error.message : String(error)}`
1034
+ );
1035
+ }
1036
+ }
1037
+ if (typeof cliOptions.verbose === "boolean") {
1038
+ parsed.verbose = cliOptions.verbose;
1039
+ }
1040
+ if (typeof cliOptions.delete === "boolean") {
1041
+ parsed.delete = cliOptions.delete;
1042
+ }
1043
+ if (cliOptions.baseDirs && Array.isArray(cliOptions.baseDirs) && cliOptions.baseDirs.length > 0) {
1044
+ parsed.baseDirs = cliOptions.baseDirs;
1045
+ }
1046
+ if (typeof cliOptions.config === "string") {
1047
+ parsed.config = cliOptions.config;
1048
+ }
1049
+ if (typeof cliOptions.noConfig === "boolean") {
1050
+ parsed.noConfig = cliOptions.noConfig;
1051
+ }
1052
+ return parsed;
1053
+ }
1054
+ /**
1055
+ * CLI引数が有効かどうかを検証する
1056
+ * @param cliOptions - 検証するCLIオプション
1057
+ * @returns 検証結果
1058
+ */
1059
+ validate(cliOptions) {
1060
+ const errors = [];
1061
+ if (cliOptions.config && cliOptions.noConfig) {
1062
+ errors.push("--config and --no-config cannot be used together");
1063
+ }
1064
+ if (cliOptions.baseDirs && cliOptions.baseDirs.length === 0) {
1065
+ errors.push("--base-dirs cannot be empty");
1066
+ }
1067
+ if (cliOptions.tools && cliOptions.tools.length === 0) {
1068
+ errors.push("--tools cannot be empty");
1069
+ }
1070
+ return {
1071
+ valid: errors.length === 0,
1072
+ errors
1073
+ };
1074
+ }
1075
+ };
1076
+
1077
+ // src/core/config/config-file-loader.ts
1078
+ import { isAbsolute, resolve } from "path";
1079
+ var ConfigFileLoader = class {
1080
+ /**
1081
+ * 設定ファイルを読み込む
1082
+ * @param options - 設定ファイルの読み込みオプション
1083
+ * @returns 設定ファイルの読み込み結果
1084
+ */
1085
+ async load(options = {}) {
1086
+ try {
1087
+ return await loadConfig(options);
1088
+ } catch (error) {
1089
+ throw new Error(
1090
+ `Failed to load configuration file: ${error instanceof Error ? error.message : String(error)}`
1091
+ );
1092
+ }
1093
+ }
1094
+ /**
1095
+ * 設定ファイルが存在するかどうかを確認する
1096
+ * @param configPath - 設定ファイルのパス
1097
+ * @returns ファイルが存在するかどうか
1098
+ */
1099
+ async exists(configPath) {
1100
+ try {
1101
+ const { access } = await import("fs/promises");
1102
+ await access(configPath);
1103
+ return true;
1104
+ } catch {
1105
+ return false;
1106
+ }
1107
+ }
1108
+ /**
1109
+ * 設定ファイルのパスを解決する
1110
+ * @param configPath - 指定された設定ファイルのパス
1111
+ * @param workingDirectory - 作業ディレクトリ
1112
+ * @returns 解決された設定ファイルのパス
1113
+ */
1114
+ resolvePath(configPath, workingDirectory) {
1115
+ if (!configPath) {
1116
+ return void 0;
1117
+ }
1118
+ if (isAbsolute(configPath)) {
1119
+ return configPath;
1120
+ }
1121
+ const baseDir = workingDirectory || process.cwd();
1122
+ return resolve(baseDir, configPath);
1123
+ }
1124
+ };
1125
+
1126
+ // src/core/config/config-merger.ts
1127
+ var ConfigMerger = class {
1128
+ /**
1129
+ * 設定ファイルとCLI引数をマージする
1130
+ * @param fileConfig - 設定ファイルから読み込まれた設定
1131
+ * @param cliOptions - CLI引数から解析された設定
1132
+ * @returns マージされた設定
1133
+ */
1134
+ merge(fileConfig, cliOptions) {
1135
+ const merged = { ...fileConfig };
1136
+ if (cliOptions.verbose !== void 0) {
1137
+ merged.verbose = cliOptions.verbose;
1138
+ }
1139
+ if (cliOptions.delete !== void 0) {
1140
+ merged.delete = cliOptions.delete;
1141
+ }
1142
+ if (cliOptions.baseDirs && cliOptions.baseDirs.length > 0) {
1143
+ merged.baseDir = cliOptions.baseDirs;
1144
+ }
1145
+ if (cliOptions.tools && cliOptions.tools.length > 0) {
1146
+ merged.defaultTargets = cliOptions.tools;
1147
+ merged.exclude = void 0;
1148
+ }
1149
+ if (cliOptions.features !== void 0) {
1150
+ merged.features = cliOptions.features;
1151
+ }
1152
+ return merged;
1153
+ }
1154
+ /**
1155
+ * 設定値のマージメタデータを生成する
1156
+ * @param fileConfig - 設定ファイルの設定
1157
+ * @param cliOptions - CLI引数の設定
1158
+ * @param merged - マージされた設定
1159
+ * @returns マージメタデータ
1160
+ */
1161
+ generateMetadata(fileConfig, cliOptions, merged) {
1162
+ const metadata = {};
1163
+ if (merged.verbose !== void 0) {
1164
+ metadata.verbose = {
1165
+ source: cliOptions.verbose !== void 0 ? "cli_args" /* CLI_ARGS */ : "config_file" /* CONFIG_FILE */,
1166
+ value: merged.verbose
1167
+ };
1168
+ }
1169
+ if (merged.delete !== void 0) {
1170
+ metadata.delete = {
1171
+ source: cliOptions.delete !== void 0 ? "cli_args" /* CLI_ARGS */ : "config_file" /* CONFIG_FILE */,
1172
+ value: merged.delete
1173
+ };
1174
+ }
1175
+ if (merged.baseDir !== void 0) {
1176
+ metadata.baseDir = {
1177
+ source: cliOptions.baseDirs ? "cli_args" /* CLI_ARGS */ : "config_file" /* CONFIG_FILE */,
1178
+ value: merged.baseDir
1179
+ };
1180
+ }
1181
+ metadata.defaultTargets = {
1182
+ source: cliOptions.tools ? "cli_args" /* CLI_ARGS */ : "config_file" /* CONFIG_FILE */,
1183
+ value: merged.defaultTargets
1184
+ };
1185
+ if (merged.exclude !== void 0) {
1186
+ metadata.exclude = {
1187
+ source: "config_file" /* CONFIG_FILE */,
1188
+ // excludeはCLIから直接指定されない
1189
+ value: merged.exclude
1190
+ };
1191
+ }
1192
+ if (merged.features !== void 0) {
1193
+ metadata.features = {
1194
+ source: cliOptions.features !== void 0 ? "cli_args" /* CLI_ARGS */ : "config_file" /* CONFIG_FILE */,
1195
+ value: merged.features
1196
+ };
1197
+ }
1198
+ return metadata;
1199
+ }
1200
+ /**
1201
+ * マージされた設定を検証する
1202
+ * @param config - マージされた設定
1203
+ * @returns 検証結果
1204
+ */
1205
+ validate(config) {
1206
+ const errors = [];
1207
+ if (!config.defaultTargets || config.defaultTargets.length === 0) {
1208
+ errors.push("At least one tool must be specified in targets or CLI arguments");
1209
+ }
1210
+ if (!config.aiRulesDir) {
1211
+ errors.push("aiRulesDir must be specified");
1212
+ }
1213
+ if (!config.outputPaths) {
1214
+ errors.push("outputPaths must be specified");
1215
+ }
1216
+ return {
1217
+ valid: errors.length === 0,
1218
+ errors
1219
+ };
1220
+ }
1221
+ };
1222
+
1223
+ // src/core/config/validators.ts
1224
+ var ConfigValidationError = class extends Error {
1225
+ constructor(message, errors) {
1226
+ super(message);
1227
+ this.errors = errors;
1228
+ this.name = "ConfigValidationError";
1229
+ }
1230
+ };
1231
+ var ConfigValidator = class {
1232
+ /**
1233
+ * CLI引数を検証する
1234
+ * @param cliOptions - 検証するCLI引数
1235
+ * @throws {ConfigValidationError} バリデーションエラーが発生した場合
1236
+ */
1237
+ validateCliOptions(cliOptions) {
1238
+ const errors = [];
1239
+ if (cliOptions.config && cliOptions.noConfig) {
1240
+ errors.push("--config and --no-config cannot be used together");
1241
+ }
1242
+ if (cliOptions.baseDirs && cliOptions.baseDirs.length === 0) {
1243
+ errors.push("--base-dirs cannot be empty");
1244
+ }
1245
+ if (cliOptions.tools && cliOptions.tools.length === 0) {
1246
+ errors.push("--tools cannot be empty");
1247
+ }
1248
+ if (cliOptions.baseDirs) {
1249
+ for (const [index, dir] of cliOptions.baseDirs.entries()) {
1250
+ if (typeof dir !== "string" || dir.trim() === "") {
1251
+ errors.push(`Base directory at index ${index} must be a non-empty string`);
1252
+ }
1253
+ }
1254
+ }
1255
+ if (errors.length > 0) {
1256
+ throw new ConfigValidationError("CLI options validation failed", errors);
1257
+ }
1258
+ }
1259
+ /**
1260
+ * マージされた設定を検証する
1261
+ * @param config - 検証する設定
1262
+ * @throws {ConfigValidationError} バリデーションエラーが発生した場合
1263
+ */
1264
+ validateMergedConfig(config) {
1265
+ const errors = [];
1266
+ if (!config.aiRulesDir || config.aiRulesDir.trim() === "") {
1267
+ errors.push("aiRulesDir must be specified and non-empty");
1268
+ }
1269
+ if (!config.outputPaths) {
1270
+ errors.push("outputPaths must be specified");
1271
+ }
1272
+ if (!config.defaultTargets || config.defaultTargets.length === 0) {
1273
+ errors.push("At least one tool must be specified in defaultTargets");
1274
+ }
1275
+ if (config.watchEnabled === void 0) {
1276
+ errors.push("watchEnabled must be specified");
1277
+ }
1278
+ if (config.baseDir !== void 0) {
1279
+ const dirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
1280
+ for (const [index, dir] of dirs.entries()) {
1281
+ if (typeof dir !== "string" || dir.trim() === "") {
1282
+ errors.push(`Base directory at index ${index} must be a non-empty string`);
1283
+ }
1284
+ }
1285
+ }
1286
+ if (config.exclude && config.defaultTargets) {
1287
+ const invalidExcludes = config.exclude.filter(
1288
+ (excludeTool) => !config.defaultTargets.includes(excludeTool)
1289
+ );
1290
+ if (invalidExcludes.length > 0) {
1291
+ errors.push(`Exclude contains tools not in defaultTargets: ${invalidExcludes.join(", ")}`);
1292
+ }
1293
+ }
1294
+ if (errors.length > 0) {
1295
+ throw new ConfigValidationError("Merged configuration validation failed", errors);
1296
+ }
1297
+ }
1298
+ /**
1299
+ * 設定ファイルのパスが有効かどうかを検証する
1300
+ * @param configPath - 設定ファイルのパス
1301
+ * @throws {ConfigValidationError} パスが無効な場合
1302
+ */
1303
+ validateConfigPath(configPath) {
1304
+ if (configPath !== void 0 && typeof configPath !== "string") {
1305
+ throw new ConfigValidationError("Configuration validation failed", [
1306
+ "Config path must be a string"
1307
+ ]);
1308
+ }
1309
+ if (typeof configPath === "string" && configPath.trim() === "") {
1310
+ throw new ConfigValidationError("Configuration validation failed", [
1311
+ "Config path cannot be empty"
1312
+ ]);
1313
+ }
1314
+ }
1315
+ /**
1316
+ * 作業ディレクトリが有効かどうかを検証する
1317
+ * @param workingDirectory - 作業ディレクトリのパス
1318
+ * @throws {ConfigValidationError} パスが無効な場合
1319
+ */
1320
+ validateWorkingDirectory(workingDirectory) {
1321
+ if (workingDirectory !== void 0 && typeof workingDirectory !== "string") {
1322
+ throw new ConfigValidationError("Configuration validation failed", [
1323
+ "Working directory must be a string"
1324
+ ]);
1325
+ }
1326
+ if (typeof workingDirectory === "string" && workingDirectory.trim() === "") {
1327
+ throw new ConfigValidationError("Configuration validation failed", [
1328
+ "Working directory cannot be empty"
1329
+ ]);
1330
+ }
1331
+ }
1332
+ };
1333
+
1334
+ // src/core/config/config-resolver.ts
1335
+ var ConfigResolver = class {
1336
+ cliParser;
1337
+ configLoader;
1338
+ merger;
1339
+ validator;
1340
+ constructor(cliParser, configLoader, merger, validator) {
1341
+ this.cliParser = cliParser || new CliParser();
1342
+ this.configLoader = configLoader || new ConfigFileLoader();
1343
+ this.merger = merger || new ConfigMerger();
1344
+ this.validator = validator || new ConfigValidator();
1345
+ }
1346
+ /**
1347
+ * 設定を解決してマージされた設定を返す
1348
+ * @param options - 設定解決のオプション
1349
+ * @returns 解決された設定と詳細情報
1350
+ */
1351
+ async resolve(options) {
1352
+ try {
1353
+ this.validator.validateWorkingDirectory(options.workingDirectory);
1354
+ const parsedCliOptions = this.cliParser.parse(options.cliOptions);
1355
+ this.validator.validateCliOptions(parsedCliOptions);
1356
+ const configLoaderOptions = {
1357
+ cwd: options.workingDirectory || process.cwd(),
1358
+ ...parsedCliOptions.config && { configPath: parsedCliOptions.config },
1359
+ ...parsedCliOptions.noConfig && { noConfig: parsedCliOptions.noConfig }
1360
+ };
1361
+ this.validator.validateConfigPath(configLoaderOptions.configPath);
1362
+ const configResult = await this.configLoader.load(configLoaderOptions);
1363
+ const mergedConfig = this.merger.merge(configResult.config, parsedCliOptions);
1364
+ this.validator.validateMergedConfig(mergedConfig);
1365
+ const metadata = this.merger.generateMetadata(
1366
+ configResult.config,
1367
+ parsedCliOptions,
1368
+ mergedConfig
1369
+ );
1370
+ const configSource = this.determineConfigSource(configResult.isEmpty, parsedCliOptions);
1371
+ return {
1372
+ value: mergedConfig,
1373
+ source: configSource,
1374
+ metadata
1375
+ };
1376
+ } catch (error) {
1377
+ if (error instanceof ConfigValidationError) {
1378
+ throw error;
1379
+ }
1380
+ throw new Error(
1381
+ `Failed to resolve configuration: ${error instanceof Error ? error.message : String(error)}`
1382
+ );
1383
+ }
1384
+ }
1385
+ /**
1386
+ * 設定の主要なソースを判定する
1387
+ * @param configFileEmpty - 設定ファイルが空かどうか
1388
+ * @param cliOptions - CLI引数
1389
+ * @returns 主要な設定ソース
1390
+ */
1391
+ determineConfigSource(configFileEmpty, cliOptions) {
1392
+ if (cliOptions.tools || cliOptions.verbose !== void 0 || cliOptions.delete !== void 0) {
1393
+ return "cli_args" /* CLI_ARGS */;
1394
+ }
1395
+ if (!configFileEmpty) {
1396
+ return "config_file" /* CONFIG_FILE */;
1397
+ }
1398
+ return "default" /* DEFAULT */;
1399
+ }
1400
+ /**
1401
+ * 設定解決の詳細情報を取得する(デバッグ用)
1402
+ * @param options - 設定解決のオプション
1403
+ * @returns 詳細な解決情報
1404
+ */
1405
+ async resolveWithDetails(options) {
1406
+ const parsedCliOptions = this.cliParser.parse(options.cliOptions);
1407
+ const configLoaderOptions = {
1408
+ cwd: options.workingDirectory || process.cwd(),
1409
+ ...parsedCliOptions.config && { configPath: parsedCliOptions.config },
1410
+ ...parsedCliOptions.noConfig && { noConfig: parsedCliOptions.noConfig }
1411
+ };
1412
+ const configFileResult = await this.configLoader.load(configLoaderOptions);
1413
+ try {
1414
+ const result = await this.resolve(options);
1415
+ return {
1416
+ result,
1417
+ details: {
1418
+ parsedCliOptions,
1419
+ configFileResult
1420
+ }
1421
+ };
1422
+ } catch (error) {
1423
+ const validationErrors = error instanceof ConfigValidationError ? error.errors : [error instanceof Error ? error.message : String(error)];
1424
+ const partialConfig = this.merger.merge(configFileResult.config, parsedCliOptions);
1425
+ return {
1426
+ result: {
1427
+ value: partialConfig,
1428
+ source: this.determineConfigSource(configFileResult.isEmpty, parsedCliOptions)
1429
+ },
1430
+ details: {
1431
+ parsedCliOptions,
1432
+ configFileResult,
1433
+ validationErrors
1434
+ }
1435
+ };
1436
+ }
1437
+ }
1438
+ };
1439
+
961
1440
  // src/generators/ignore/shared-factory.ts
962
1441
  import { join as join4 } from "path";
963
1442
 
@@ -1730,6 +2209,41 @@ function generateWindsurfIgnore(rules, config, baseDir) {
1730
2209
  return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
1731
2210
  }
1732
2211
 
2212
+ // src/generators/rules/agentsmd.ts
2213
+ async function generateAgentsMdConfig(rules, config, baseDir) {
2214
+ const outputs = [];
2215
+ const nonEmptyRules = rules.filter((rule) => rule.content.trim().length > 0);
2216
+ if (nonEmptyRules.length > 0) {
2217
+ const rootRule = nonEmptyRules.find((rule) => rule.frontmatter.root);
2218
+ const detailRules = nonEmptyRules.filter((rule) => !rule.frontmatter.root);
2219
+ if (rootRule) {
2220
+ const agentsPath = resolvePath("AGENTS.md", baseDir);
2221
+ const agentsContent = generateAgentsMarkdown(rootRule);
2222
+ outputs.push({
2223
+ tool: "agentsmd",
2224
+ filepath: agentsPath,
2225
+ content: agentsContent
2226
+ });
2227
+ }
2228
+ for (const rule of detailRules) {
2229
+ const memoryPath = resolvePath(`.agents/memories/${rule.filename}.md`, baseDir);
2230
+ const memoryContent = generateMemoryMarkdown(rule);
2231
+ outputs.push({
2232
+ tool: "agentsmd",
2233
+ filepath: memoryPath,
2234
+ content: memoryContent
2235
+ });
2236
+ }
2237
+ }
2238
+ return outputs;
2239
+ }
2240
+ function generateAgentsMarkdown(rootRule) {
2241
+ return rootRule.content.trim();
2242
+ }
2243
+ function generateMemoryMarkdown(rule) {
2244
+ return rule.content.trim();
2245
+ }
2246
+
1733
2247
  // src/generators/rules/shared-helpers.ts
1734
2248
  import { join as join7 } from "path";
1735
2249
 
@@ -1801,15 +2315,29 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
1801
2315
  }
1802
2316
  async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
1803
2317
  const outputs = [];
1804
- for (const rule of rules) {
1805
- const content = generatorConfig.generateContent(rule);
1806
- const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
1807
- const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join7(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
2318
+ const nonEmptyRules = rules.filter((rule) => rule.content.trim().length > 0);
2319
+ if (nonEmptyRules.length === 0) {
2320
+ return outputs;
2321
+ }
2322
+ if (generatorConfig.singleFileMode && generatorConfig.fileName && generatorConfig.generateCombinedContent) {
2323
+ const filepath = resolvePath(generatorConfig.fileName, baseDir);
2324
+ const content = generatorConfig.generateCombinedContent(nonEmptyRules);
1808
2325
  outputs.push({
1809
2326
  tool: generatorConfig.tool,
1810
2327
  filepath,
1811
2328
  content
1812
2329
  });
2330
+ } else if (generatorConfig.fileExtension) {
2331
+ for (const rule of nonEmptyRules) {
2332
+ const content = generatorConfig.generateContent(rule);
2333
+ const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
2334
+ const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join7(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
2335
+ outputs.push({
2336
+ tool: generatorConfig.tool,
2337
+ filepath,
2338
+ content
2339
+ });
2340
+ }
1813
2341
  }
1814
2342
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1815
2343
  if (ignorePatterns.patterns.length > 0 && generatorConfig.ignoreFileName) {
@@ -2102,6 +2630,15 @@ function determineCursorRuleType(frontmatter) {
2102
2630
  }
2103
2631
  var GENERATOR_REGISTRY = {
2104
2632
  // Simple generators - generate one file per rule
2633
+ agentsmd: {
2634
+ type: "complex",
2635
+ tool: "agentsmd",
2636
+ fileExtension: ".md",
2637
+ ignoreFileName: ".agentsignore",
2638
+ generateContent: (rule) => rule.content.trim()
2639
+ // NOTE: AGENTS.md specific logic is handled in the actual generator file
2640
+ // Root rule goes to AGENTS.md, detail rules go to .agents/memories/
2641
+ },
2105
2642
  amazonqcli: {
2106
2643
  type: "complex",
2107
2644
  tool: "amazonqcli",
@@ -2390,10 +2927,63 @@ var generateKiroConfig = createSimpleGenerator("kiro");
2390
2927
  var generateRooConfig = createSimpleGenerator("roo");
2391
2928
  var generateQwencodeConfig = createSimpleGenerator("qwencode");
2392
2929
 
2393
- // src/utils/xml-document-generator.ts
2394
- import { XMLBuilder } from "fast-xml-parser";
2395
- function generateRootMarkdownWithXmlDocs(rootRule, memoryRules, config) {
2396
- const lines = [];
2930
+ // src/generators/rules/base-generator.ts
2931
+ async function generateBaseRulesConfig(rules, config, generatorConfig, baseDir) {
2932
+ const unifiedConfig = {
2933
+ tool: generatorConfig.tool,
2934
+ fileName: generatorConfig.fileName,
2935
+ ...generatorConfig.ignoreFileName ? { ignoreFileName: generatorConfig.ignoreFileName } : {},
2936
+ singleFileMode: true,
2937
+ generateCombinedContent: generatorConfig.generateContent,
2938
+ generateContent: () => ""
2939
+ // Not used in single file mode
2940
+ };
2941
+ return generateRulesConfig(rules, config, unifiedConfig, baseDir);
2942
+ }
2943
+ function categorizeRules(rules) {
2944
+ return rules.reduce(
2945
+ (acc, rule) => {
2946
+ if (rule.frontmatter.root) {
2947
+ acc.root.push(rule);
2948
+ } else {
2949
+ acc.detail.push(rule);
2950
+ }
2951
+ return acc;
2952
+ },
2953
+ { root: [], detail: [] }
2954
+ );
2955
+ }
2956
+ function generateMarkdownContent(rules) {
2957
+ const sections = [];
2958
+ const categorized = categorizeRules(rules);
2959
+ if (categorized.root.length > 0) {
2960
+ sections.push(...categorized.root.map((rule) => rule.content.trim()));
2961
+ }
2962
+ if (categorized.detail.length > 0) {
2963
+ sections.push(...categorized.detail.map((rule) => rule.content.trim()));
2964
+ }
2965
+ return sections.join("\n\n").trim();
2966
+ }
2967
+
2968
+ // src/generators/rules/codexcli.ts
2969
+ async function generateCodexConfig(rules, config, baseDir) {
2970
+ return generateBaseRulesConfig(
2971
+ rules,
2972
+ config,
2973
+ {
2974
+ fileName: "AGENTS.md",
2975
+ ignoreFileName: ".codexignore",
2976
+ generateContent: generateMarkdownContent,
2977
+ tool: "codexcli"
2978
+ },
2979
+ baseDir
2980
+ );
2981
+ }
2982
+
2983
+ // src/utils/xml-document-generator.ts
2984
+ import { XMLBuilder } from "fast-xml-parser";
2985
+ function generateRootMarkdownWithXmlDocs(rootRule, memoryRules, config) {
2986
+ const lines = [];
2397
2987
  if (memoryRules.length > 0) {
2398
2988
  lines.push(
2399
2989
  "Please also reference the following documents as needed. In this case, `@` stands for the project root directory."
@@ -2434,47 +3024,6 @@ function generateRootMarkdownWithXmlDocs(rootRule, memoryRules, config) {
2434
3024
  return lines.join("\n");
2435
3025
  }
2436
3026
 
2437
- // src/generators/rules/codexcli.ts
2438
- async function generateCodexConfig(rules, config, baseDir) {
2439
- const outputs = [];
2440
- const nonEmptyRules = rules.filter((rule) => rule.content.trim().length > 0);
2441
- if (nonEmptyRules.length > 0) {
2442
- const generatorConfig = {
2443
- tool: "codexcli",
2444
- fileExtension: ".md",
2445
- ignoreFileName: ".codexignore",
2446
- generateContent: generateCodexMemoryMarkdown,
2447
- generateDetailContent: generateCodexMemoryMarkdown,
2448
- generateRootContent: generateCodexRootMarkdown,
2449
- rootFilePath: "AGENTS.md",
2450
- detailSubDir: ".codex/memories"
2451
- };
2452
- const ruleOutputs = await generateComplexRules(nonEmptyRules, config, generatorConfig, baseDir);
2453
- outputs.push(...ruleOutputs);
2454
- } else {
2455
- const ignorePatterns = await loadIgnorePatterns(baseDir);
2456
- if (ignorePatterns.patterns.length > 0) {
2457
- const ignorePath = resolvePath(".codexignore", baseDir);
2458
- const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
2459
- outputs.push({
2460
- tool: "codexcli",
2461
- filepath: ignorePath,
2462
- content: ignoreContent
2463
- });
2464
- }
2465
- }
2466
- return outputs;
2467
- }
2468
- function generateCodexMemoryMarkdown(rule) {
2469
- return rule.content.trim();
2470
- }
2471
- function generateCodexRootMarkdown(rootRule, memoryRules, _baseDir) {
2472
- return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
2473
- memorySubDir: ".codex/memories",
2474
- fallbackTitle: "OpenAI Codex CLI Configuration"
2475
- });
2476
- }
2477
-
2478
3027
  // src/generators/rules/geminicli.ts
2479
3028
  async function generateGeminiConfig(rules, config, baseDir) {
2480
3029
  const generatorConfig = {
@@ -2532,23 +3081,17 @@ async function generateOpenCodeConfig(rules, config, baseDir) {
2532
3081
  tool: "opencode",
2533
3082
  fileExtension: ".md",
2534
3083
  // ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
2535
- generateContent: generateOpenCodeMarkdown,
2536
- generateDetailContent: generateOpenCodeMarkdown,
2537
- generateRootContent: generateOpenCodeRootMarkdown,
3084
+ generateContent: (rule) => rule.content.trim(),
3085
+ generateDetailContent: (rule) => rule.content.trim(),
3086
+ generateRootContent: (rootRule, memoryRules) => generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
3087
+ memorySubDir: ".opencode/memories",
3088
+ fallbackTitle: "OpenCode Configuration"
3089
+ }),
2538
3090
  rootFilePath: "AGENTS.md",
2539
3091
  detailSubDir: ".opencode/memories"
2540
3092
  };
2541
3093
  return generateComplexRules(rules, config, generatorConfig, baseDir);
2542
3094
  }
2543
- function generateOpenCodeMarkdown(rule) {
2544
- return rule.content.trim();
2545
- }
2546
- function generateOpenCodeRootMarkdown(rootRule, memoryRules, _baseDir) {
2547
- return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
2548
- memorySubDir: ".opencode/memories",
2549
- fallbackTitle: "OpenCode Configuration"
2550
- });
2551
- }
2552
3095
 
2553
3096
  // src/generators/rules/qwencode.ts
2554
3097
  async function generateQwencodeConfig2(rules, config, baseDir) {
@@ -2606,6 +3149,8 @@ function filterRulesForTool(rules, tool, config) {
2606
3149
  }
2607
3150
  async function generateForTool(tool, rules, config, baseDir) {
2608
3151
  switch (tool) {
3152
+ case "agentsmd":
3153
+ return await generateAgentsMdConfig(rules, config, baseDir);
2609
3154
  case "amazonqcli":
2610
3155
  return await generateAmazonqcliConfig(rules, config, baseDir);
2611
3156
  case "augmentcode": {
@@ -2816,10 +3361,12 @@ function parseMcpConfig(projectRoot) {
2816
3361
  async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
2817
3362
  const outputs = [];
2818
3363
  const toolMap = {
3364
+ agentsmd: async () => [],
2819
3365
  amazonqcli: async (servers, dir) => {
2820
3366
  const config = {
2821
3367
  aiRulesDir: ".rulesync",
2822
3368
  outputPaths: {
3369
+ agentsmd: ".agents/memories",
2823
3370
  amazonqcli: ".amazonq/rules",
2824
3371
  augmentcode: ".",
2825
3372
  "augmentcode-legacy": ".",
@@ -2839,45 +3386,45 @@ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
2839
3386
  watchEnabled: false,
2840
3387
  defaultTargets: []
2841
3388
  };
2842
- const results = await (await import("./amazonqcli-MW7XTVPN.js")).generateAmazonqcliMcp(
3389
+ const results = await (await import("./amazonqcli-PWXCSRAN.js")).generateAmazonqcliMcp(
2843
3390
  servers,
2844
3391
  config,
2845
3392
  dir
2846
3393
  );
2847
3394
  return results.map((result) => ({ filepath: result.filepath, content: result.content }));
2848
3395
  },
2849
- augmentcode: async (servers, dir) => (await import("./augmentcode-WCZCL7VR.js")).generateAugmentcodeMcpConfiguration(
3396
+ augmentcode: async (servers, dir) => (await import("./augmentcode-4AFYW4BU.js")).generateAugmentcodeMcpConfiguration(
2850
3397
  servers,
2851
3398
  dir
2852
3399
  ),
2853
- "augmentcode-legacy": async (servers, dir) => (await import("./augmentcode-WCZCL7VR.js")).generateAugmentcodeMcpConfiguration(
3400
+ "augmentcode-legacy": async (servers, dir) => (await import("./augmentcode-4AFYW4BU.js")).generateAugmentcodeMcpConfiguration(
2854
3401
  servers,
2855
3402
  dir
2856
3403
  ),
2857
- claudecode: async (servers, dir) => (await import("./claudecode-RZSJPPBU.js")).generateClaudeMcpConfiguration(
3404
+ claudecode: async (servers, dir) => (await import("./claudecode-OC7VHCF6.js")).generateClaudeMcpConfiguration(
2858
3405
  servers,
2859
3406
  dir
2860
3407
  ),
2861
- copilot: async (servers, dir) => (await import("./copilot-H3CLGKDP.js")).generateCopilotMcpConfiguration(servers, dir),
2862
- cursor: async (servers, dir) => (await import("./cursor-ZUN5RZU6.js")).generateCursorMcpConfiguration(servers, dir),
2863
- cline: async (servers, dir) => (await import("./cline-JTWWBQQ4.js")).generateClineMcpConfiguration(servers, dir),
2864
- codexcli: async (servers, dir) => (await import("./codexcli-ATMFGRJR.js")).generateCodexMcpConfiguration(servers, dir),
2865
- opencode: async (servers, dir) => (await import("./opencode-EBS3CED2.js")).generateOpenCodeMcpConfiguration(
3408
+ copilot: async (servers, dir) => (await import("./copilot-3LIMXQ7O.js")).generateCopilotMcpConfiguration(servers, dir),
3409
+ cursor: async (servers, dir) => (await import("./cursor-QV72CDJC.js")).generateCursorMcpConfiguration(servers, dir),
3410
+ cline: async (servers, dir) => (await import("./cline-A4KFSAQE.js")).generateClineMcpConfiguration(servers, dir),
3411
+ codexcli: async (servers, dir) => (await import("./codexcli-YP3X7FWB.js")).generateCodexMcpConfiguration(servers, dir),
3412
+ opencode: async (servers, dir) => (await import("./opencode-C5QAYVJ5.js")).generateOpenCodeMcpConfiguration(
2866
3413
  servers,
2867
3414
  dir
2868
3415
  ),
2869
- roo: async (servers, dir) => (await import("./roo-KBTRH4TZ.js")).generateRooMcpConfiguration(servers, dir),
2870
- geminicli: async (servers, dir) => (await import("./geminicli-Q5HPIQCU.js")).generateGeminiCliMcpConfiguration(
3416
+ roo: async (servers, dir) => (await import("./roo-5IXVBUHD.js")).generateRooMcpConfiguration(servers, dir),
3417
+ geminicli: async (servers, dir) => (await import("./geminicli-XPSJJS65.js")).generateGeminiCliMcpConfiguration(
2871
3418
  servers,
2872
3419
  dir
2873
3420
  ),
2874
- kiro: async (servers, dir) => (await import("./kiro-CNF6433S.js")).generateKiroMcpConfiguration(servers, dir),
2875
- junie: async (servers, dir) => (await import("./junie-JCLVC3MI.js")).generateJunieMcpConfiguration(servers, dir),
2876
- qwencode: async (servers, dir) => (await import("./qwencode-JIT6KW7E.js")).generateQwenCodeMcpConfiguration(
3421
+ kiro: async (servers, dir) => (await import("./kiro-6OHFHN5X.js")).generateKiroMcpConfiguration(servers, dir),
3422
+ junie: async (servers, dir) => (await import("./junie-265XIW43.js")).generateJunieMcpConfiguration(servers, dir),
3423
+ qwencode: async (servers, dir) => (await import("./qwencode-5RR24UW6.js")).generateQwenCodeMcpConfiguration(
2877
3424
  servers,
2878
3425
  dir
2879
3426
  ),
2880
- windsurf: async (servers, dir) => (await import("./windsurf-ZAAWL6JJ.js")).generateWindsurfMcpConfiguration(
3427
+ windsurf: async (servers, dir) => (await import("./windsurf-DVQUJJJ5.js")).generateWindsurfMcpConfiguration(
2881
3428
  servers,
2882
3429
  dir
2883
3430
  )
@@ -2898,220 +3445,572 @@ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
2898
3445
  return outputs;
2899
3446
  }
2900
3447
 
2901
- // src/cli/commands/generate.ts
2902
- async function generateCommand(options = {}) {
2903
- const configLoaderOptions = {
2904
- ...options.config !== void 0 && { configPath: options.config },
2905
- ...options.noConfig !== void 0 && { noConfig: options.noConfig }
2906
- };
2907
- const configResult = await loadConfig(configLoaderOptions);
2908
- const cliOptions = {
2909
- tools: options.tools,
2910
- ...options.verbose !== void 0 && { verbose: options.verbose },
2911
- ...options.delete !== void 0 && { delete: options.delete },
2912
- ...options.baseDirs !== void 0 && { baseDirs: options.baseDirs }
2913
- };
2914
- const config = mergeWithCliOptions(configResult.config, cliOptions);
2915
- if (!config.defaultTargets || config.defaultTargets.length === 0) {
2916
- const errorMessage = `\u274C Error: At least one tool must be specified.
3448
+ // src/core/subagent-generator.ts
3449
+ import path7 from "path";
2917
3450
 
2918
- Available tools:
2919
- --augmentcode Generate for AugmentCode
2920
- --augmentcode-legacy Generate for AugmentCode legacy format
2921
- --copilot Generate for GitHub Copilot
2922
- --cursor Generate for Cursor
2923
- --cline Generate for Cline
2924
- --codexcli Generate for OpenAI Codex CLI
2925
- --claudecode Generate for Claude Code
2926
- --roo Generate for Roo Code
2927
- --geminicli Generate for Gemini CLI
2928
- --junie Generate for JetBrains Junie
2929
- --qwencode Generate for Qwen Code
2930
- --kiro Generate for Kiro IDE
2931
- --opencode Generate for OpenCode
2932
- --windsurf Generate for Windsurf
3451
+ // src/generators/subagents/base.ts
3452
+ import path5 from "path";
3453
+ var BaseSubagentGenerator = class {
3454
+ /**
3455
+ * Generate subagent files for the tool
3456
+ */
3457
+ async generate(rules, config, baseDir, parsedSubagents) {
3458
+ const toolName = this.getToolName();
3459
+ const agentsDir = this.getAgentsDirectory();
3460
+ const outputs = [];
3461
+ try {
3462
+ const outputDir = resolvePath(
3463
+ path5.join(config.outputPaths[toolName] ?? "", agentsDir),
3464
+ baseDir
3465
+ );
3466
+ await ensureDir(outputDir);
3467
+ let subagentOutputs;
3468
+ if (parsedSubagents && parsedSubagents.length > 0) {
3469
+ logger.debug(
3470
+ `Generating ${parsedSubagents.length} subagents from parsed data for ${toolName}`
3471
+ );
3472
+ subagentOutputs = this.generateFromParsedSubagents(parsedSubagents);
3473
+ } else {
3474
+ logger.debug(`Generating subagents from rules for ${toolName}`);
3475
+ subagentOutputs = this.generateFromRules(rules);
3476
+ }
3477
+ for (const subagent of subagentOutputs) {
3478
+ const filePath = path5.join(outputDir, subagent.filename);
3479
+ outputs.push({
3480
+ tool: toolName,
3481
+ filepath: filePath,
3482
+ content: subagent.content
3483
+ });
3484
+ }
3485
+ if (outputs.length > 0) {
3486
+ logger.info(`Generated ${outputs.length} subagent files for ${toolName}`);
3487
+ } else {
3488
+ logger.debug(`No subagents generated for ${toolName}`);
3489
+ }
3490
+ return outputs;
3491
+ } catch (error) {
3492
+ logger.error(`Error generating subagents for ${toolName}:`, error);
3493
+ return [];
3494
+ }
3495
+ }
3496
+ };
2933
3497
 
2934
- Example:
2935
- rulesync generate --copilot --cursor
3498
+ // src/generators/subagents/claudecode.ts
3499
+ var ClaudeCodeSubagentGenerator = class extends BaseSubagentGenerator {
3500
+ getToolName() {
3501
+ return "claudecode";
3502
+ }
3503
+ getAgentsDirectory() {
3504
+ return ".claude/agents";
3505
+ }
3506
+ generateFromRules(_rules) {
3507
+ logger.debug("Skipping rule-to-subagent conversion (deprecated behavior)");
3508
+ return [];
3509
+ }
3510
+ generateFromParsedSubagents(subagents) {
3511
+ return subagents.map((subagent) => {
3512
+ const frontmatterLines = ["---"];
3513
+ frontmatterLines.push(`name: ${subagent.frontmatter.name}`);
3514
+ frontmatterLines.push(`description: ${subagent.frontmatter.description}`);
3515
+ if (subagent.frontmatter.claudecode?.model) {
3516
+ frontmatterLines.push(`model: ${subagent.frontmatter.claudecode.model}`);
3517
+ }
3518
+ frontmatterLines.push("---");
3519
+ const content = `${frontmatterLines.join("\n")}
2936
3520
 
2937
- Or specify tools in rulesync.jsonc:
2938
- "tools": ["copilot", "cursor"]`;
2939
- logger.error(errorMessage);
2940
- process.exit(1);
3521
+ ${subagent.content}`;
3522
+ return {
3523
+ filename: `${subagent.filename}.md`,
3524
+ content
3525
+ };
3526
+ });
2941
3527
  }
2942
- logger.setVerbose(config.verbose || false);
2943
- let baseDirs;
2944
- if (config.baseDir) {
2945
- baseDirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
2946
- } else if (options.baseDirs) {
2947
- baseDirs = options.baseDirs;
2948
- } else {
2949
- baseDirs = [process.cwd()];
3528
+ processContent(subagent) {
3529
+ const frontmatterLines = ["---"];
3530
+ frontmatterLines.push(`name: ${subagent.frontmatter.name}`);
3531
+ frontmatterLines.push(`description: ${subagent.frontmatter.description}`);
3532
+ if (subagent.frontmatter.claudecode?.model) {
3533
+ frontmatterLines.push(`model: ${subagent.frontmatter.claudecode.model}`);
3534
+ }
3535
+ frontmatterLines.push("---");
3536
+ return `${frontmatterLines.join("\n")}
3537
+
3538
+ ${subagent.content}`;
2950
3539
  }
2951
- logger.info(`Loaded configuration from: ${configResult.filepath}`);
2952
- logger.log("Generating configuration files...");
2953
- if (!await fileExists(config.aiRulesDir)) {
2954
- logger.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
2955
- process.exit(1);
3540
+ };
3541
+
3542
+ // src/parsers/subagent-parser.ts
3543
+ import { readdir, readFile } from "fs/promises";
3544
+ import path6 from "path";
3545
+ import matter2 from "gray-matter";
3546
+
3547
+ // src/types/subagent.ts
3548
+ import { z as z7 } from "zod/mini";
3549
+ var ClaudeCodeConfigSchema = z7.object({
3550
+ model: z7.optional(z7.string())
3551
+ });
3552
+ var SubagentFrontmatterSchema = z7.object({
3553
+ name: z7.string(),
3554
+ description: z7.string(),
3555
+ targets: z7.optional(z7.array(z7.string())),
3556
+ // Tool-specific configurations
3557
+ claudecode: z7.optional(ClaudeCodeConfigSchema)
3558
+ });
3559
+
3560
+ // src/parsers/subagent-parser.ts
3561
+ async function parseSubagentFile(filepath) {
3562
+ try {
3563
+ const fileContent = await readFile(filepath, "utf-8");
3564
+ const { data: frontmatter, content } = matter2(fileContent);
3565
+ const result = SubagentFrontmatterSchema.safeParse(frontmatter);
3566
+ if (!result.success) {
3567
+ logger.warn(`Invalid frontmatter in ${filepath}: ${result.error.message}`);
3568
+ return null;
3569
+ }
3570
+ const parsedFrontmatter = result.data;
3571
+ if (!parsedFrontmatter.targets) {
3572
+ parsedFrontmatter.targets = ["*"];
3573
+ }
3574
+ const filename = path6.basename(filepath, path6.extname(filepath));
3575
+ return {
3576
+ frontmatter: parsedFrontmatter,
3577
+ content: content.trim(),
3578
+ filename,
3579
+ filepath
3580
+ };
3581
+ } catch (error) {
3582
+ logger.error(`Error parsing subagent file ${filepath}:`, error);
3583
+ return null;
2956
3584
  }
3585
+ }
3586
+ async function parseSubagentsFromDirectory(agentsDir) {
2957
3587
  try {
2958
- logger.info(`Parsing rules from ${config.aiRulesDir}...`);
2959
- const rules = await parseRulesFromDirectory(config.aiRulesDir);
2960
- if (rules.length === 0) {
2961
- logger.warn("\u26A0\uFE0F No rules found in .rulesync directory");
2962
- return;
3588
+ if (!await fileExists(agentsDir)) {
3589
+ logger.debug(`Agents directory does not exist: ${agentsDir}`);
3590
+ return [];
2963
3591
  }
2964
- logger.info(`Found ${rules.length} rule(s)`);
2965
- logger.info(`Base directories: ${baseDirs.join(", ")}`);
2966
- if (config.delete) {
2967
- logger.info("Deleting existing output directories...");
2968
- const targetTools = config.defaultTargets;
2969
- const deleteTasks = [];
2970
- const commandsDir = join13(config.aiRulesDir, "commands");
2971
- const hasCommands = await fileExists(commandsDir);
2972
- let hasCommandFiles = false;
2973
- if (hasCommands) {
2974
- const { readdir } = await import("fs/promises");
2975
- try {
2976
- const files = await readdir(commandsDir);
2977
- hasCommandFiles = files.some((file) => file.endsWith(".md"));
2978
- } catch {
2979
- hasCommandFiles = false;
3592
+ const entries = await readdir(agentsDir);
3593
+ const mdFiles = entries.filter((file) => file.endsWith(".md"));
3594
+ const files = mdFiles.map((file) => path6.join(agentsDir, file));
3595
+ if (files.length === 0) {
3596
+ logger.debug(`No subagent files found in ${agentsDir}`);
3597
+ return [];
3598
+ }
3599
+ logger.debug(`Found ${files.length} subagent files in ${agentsDir}`);
3600
+ const parsePromises = files.map((file) => parseSubagentFile(file));
3601
+ const results = await Promise.all(parsePromises);
3602
+ const subagents = results.filter((result) => result !== null);
3603
+ logger.info(`Successfully parsed ${subagents.length} subagents`);
3604
+ return subagents;
3605
+ } catch (error) {
3606
+ logger.error(`Error parsing subagents from directory ${agentsDir}:`, error);
3607
+ return [];
3608
+ }
3609
+ }
3610
+
3611
+ // src/core/subagent-generator.ts
3612
+ async function generateSubagents(rulesyncDir, outputDir, tools, rules) {
3613
+ const outputs = [];
3614
+ const subagentsDir = path7.join(rulesyncDir, "subagents");
3615
+ let parsedSubagents = [];
3616
+ try {
3617
+ parsedSubagents = await parseSubagentsFromDirectory(subagentsDir);
3618
+ if (parsedSubagents.length > 0) {
3619
+ logger.debug(`Found ${parsedSubagents.length} subagent files in ${subagentsDir}`);
3620
+ }
3621
+ } catch (error) {
3622
+ logger.debug(`No subagents directory found or error reading: ${error}`);
3623
+ }
3624
+ if (!tools || tools.length === 0) {
3625
+ return outputs;
3626
+ }
3627
+ const config = {
3628
+ aiRulesDir: rulesyncDir,
3629
+ outputPaths: {
3630
+ agentsmd: ".",
3631
+ amazonqcli: ".",
3632
+ augmentcode: ".augment",
3633
+ "augmentcode-legacy": ".",
3634
+ copilot: ".github",
3635
+ cursor: ".cursor",
3636
+ cline: ".",
3637
+ claudecode: ".",
3638
+ codexcli: ".",
3639
+ opencode: ".",
3640
+ qwencode: ".",
3641
+ roo: ".roo",
3642
+ geminicli: ".gemini",
3643
+ kiro: ".kiro",
3644
+ junie: ".junie",
3645
+ windsurf: ".windsurf"
3646
+ },
3647
+ watchEnabled: false,
3648
+ defaultTargets: tools
3649
+ };
3650
+ for (const tool of tools) {
3651
+ try {
3652
+ let generator;
3653
+ switch (tool) {
3654
+ case "claudecode":
3655
+ generator = new ClaudeCodeSubagentGenerator();
3656
+ break;
3657
+ // Add other tool generators here as they are implemented
3658
+ default:
3659
+ logger.debug(`Subagent generation not yet implemented for ${tool}`);
3660
+ continue;
3661
+ }
3662
+ const generatedOutputs = await generator.generate(
3663
+ parsedSubagents.length > 0 ? [] : rules || [],
3664
+ config,
3665
+ outputDir,
3666
+ parsedSubagents
3667
+ );
3668
+ for (const output of generatedOutputs) {
3669
+ outputs.push({
3670
+ tool,
3671
+ filepath: output.filepath,
3672
+ content: output.content
3673
+ });
3674
+ }
3675
+ } catch (error) {
3676
+ logger.error(`Failed to generate subagents for ${tool}:`, error);
3677
+ }
3678
+ }
3679
+ return outputs;
3680
+ }
3681
+
3682
+ // src/cli/commands/shared-utils.ts
3683
+ function showBackwardCompatibilityWarning(commandName, exampleCommand) {
3684
+ const yellow = "\x1B[33m";
3685
+ const gray = "\x1B[90m";
3686
+ const cyan = "\x1B[36m";
3687
+ const reset = "\x1B[0m";
3688
+ logger.warn(
3689
+ `
3690
+ ${yellow}\u26A0\uFE0F Warning: No --features option specified.${reset}
3691
+ ${gray}Currently ${commandName} all features for backward compatibility.${reset}
3692
+ ${gray}In future versions, this behavior may change.${reset}
3693
+ ${gray}Please specify --features explicitly:${reset}
3694
+ ${cyan} ${exampleCommand}${reset}
3695
+ ${gray}Or use --features * to ${commandName.replace("ing", "")} all features.${reset}
3696
+ `
3697
+ );
3698
+ }
3699
+
3700
+ // src/cli/commands/generate.ts
3701
+ async function generateCommand(options = {}) {
3702
+ try {
3703
+ const cliParser = new CliParser();
3704
+ const cliInputs = {};
3705
+ if (options.tools !== void 0) cliInputs.tools = options.tools;
3706
+ if (options.features !== void 0) cliInputs.features = options.features;
3707
+ if (options.verbose !== void 0) cliInputs.verbose = options.verbose;
3708
+ if (options.delete !== void 0) cliInputs.delete = options.delete;
3709
+ if (options.baseDirs !== void 0) cliInputs.baseDirs = options.baseDirs;
3710
+ if (options.config !== void 0) cliInputs.config = options.config;
3711
+ if (options.noConfig !== void 0) cliInputs.noConfig = options.noConfig;
3712
+ const cliOptions = cliParser.parse(cliInputs);
3713
+ const configResolver = new ConfigResolver();
3714
+ const resolutionResult = await configResolver.resolve({
3715
+ cliOptions
3716
+ });
3717
+ const config = resolutionResult.value;
3718
+ let resolvedFeatures;
3719
+ let showWarning = false;
3720
+ if (cliOptions.features !== void 0) {
3721
+ resolvedFeatures = cliOptions.features;
3722
+ } else if (config.features !== void 0) {
3723
+ resolvedFeatures = config.features;
3724
+ } else {
3725
+ resolvedFeatures = "*";
3726
+ showWarning = true;
3727
+ }
3728
+ if (showWarning) {
3729
+ showBackwardCompatibilityWarning(
3730
+ "generating",
3731
+ "rulesync generate --features rules,commands,mcp,ignore"
3732
+ );
3733
+ }
3734
+ const normalizedFeatures = normalizeFeatures(resolvedFeatures);
3735
+ if (!config.defaultTargets || config.defaultTargets.length === 0) {
3736
+ const errorMessage = `\u274C Error: At least one tool must be specified.
3737
+
3738
+ You can specify tools in three ways:
3739
+
3740
+ 1. Use the --targets flag:
3741
+ rulesync generate --targets copilot,cursor
3742
+
3743
+ 2. Use the --all flag to generate for all tools:
3744
+ rulesync generate --all
3745
+
3746
+ 3. Set targets in rulesync.jsonc:
3747
+ {
3748
+ "targets": ["copilot", "cursor"]
3749
+ }
3750
+
3751
+ Available tools:
3752
+ agentsmd, amazonqcli, augmentcode, augmentcode-legacy, copilot, cursor, cline,
3753
+ claudecode, codexcli, opencode, qwencode, roo, geminicli, kiro, junie, windsurf`;
3754
+ logger.error(errorMessage);
3755
+ process.exit(1);
3756
+ }
3757
+ logger.setVerbose(config.verbose || false);
3758
+ let baseDirs;
3759
+ if (config.baseDir) {
3760
+ baseDirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
3761
+ } else if (options.baseDirs) {
3762
+ baseDirs = options.baseDirs;
3763
+ } else {
3764
+ baseDirs = [process.cwd()];
3765
+ }
3766
+ logger.info(`Configuration resolved from: ${resolutionResult.source}`);
3767
+ logger.log("Generating configuration files...");
3768
+ if (!await fileExists(config.aiRulesDir)) {
3769
+ logger.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
3770
+ process.exit(1);
3771
+ }
3772
+ try {
3773
+ logger.info(`Parsing rules from ${config.aiRulesDir}...`);
3774
+ const rules = await parseRulesFromDirectory(config.aiRulesDir);
3775
+ if (rules.length === 0) {
3776
+ logger.warn("\u26A0\uFE0F No rules found in .rulesync directory");
3777
+ return;
3778
+ }
3779
+ logger.info(`Found ${rules.length} rule(s)`);
3780
+ logger.info(`Base directories: ${baseDirs.join(", ")}`);
3781
+ if (config.delete) {
3782
+ logger.info("Deleting existing output directories...");
3783
+ const targetTools = config.defaultTargets;
3784
+ const deleteTasks = [];
3785
+ const commandsDir = join13(config.aiRulesDir, "commands");
3786
+ const hasCommands = await fileExists(commandsDir);
3787
+ let hasCommandFiles = false;
3788
+ if (hasCommands) {
3789
+ const { readdir: readdir2 } = await import("fs/promises");
3790
+ try {
3791
+ const files = await readdir2(commandsDir);
3792
+ hasCommandFiles = files.some((file) => file.endsWith(".md"));
3793
+ } catch {
3794
+ hasCommandFiles = false;
3795
+ }
3796
+ }
3797
+ for (const tool of targetTools) {
3798
+ switch (tool) {
3799
+ case "augmentcode":
3800
+ if (normalizedFeatures.includes("rules")) {
3801
+ deleteTasks.push(removeDirectory(join13(".augment", "rules")));
3802
+ }
3803
+ if (normalizedFeatures.includes("ignore")) {
3804
+ deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
3805
+ }
3806
+ break;
3807
+ case "augmentcode-legacy":
3808
+ if (normalizedFeatures.includes("rules")) {
3809
+ deleteTasks.push(removeClaudeGeneratedFiles());
3810
+ }
3811
+ if (normalizedFeatures.includes("ignore")) {
3812
+ deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
3813
+ }
3814
+ break;
3815
+ case "copilot":
3816
+ if (normalizedFeatures.includes("rules")) {
3817
+ deleteTasks.push(removeDirectory(config.outputPaths.copilot));
3818
+ }
3819
+ break;
3820
+ case "cursor":
3821
+ if (normalizedFeatures.includes("rules")) {
3822
+ deleteTasks.push(removeDirectory(config.outputPaths.cursor));
3823
+ }
3824
+ break;
3825
+ case "cline":
3826
+ if (normalizedFeatures.includes("rules")) {
3827
+ deleteTasks.push(removeDirectory(config.outputPaths.cline));
3828
+ }
3829
+ break;
3830
+ case "claudecode":
3831
+ if (normalizedFeatures.includes("rules")) {
3832
+ deleteTasks.push(removeClaudeGeneratedFiles());
3833
+ }
3834
+ if (normalizedFeatures.includes("commands") && hasCommandFiles) {
3835
+ deleteTasks.push(removeDirectory(join13(".claude", "commands")));
3836
+ }
3837
+ if (normalizedFeatures.includes("subagents")) {
3838
+ deleteTasks.push(removeDirectory(join13(".claude", "agents")));
3839
+ }
3840
+ break;
3841
+ case "roo":
3842
+ if (normalizedFeatures.includes("rules")) {
3843
+ deleteTasks.push(removeDirectory(config.outputPaths.roo));
3844
+ }
3845
+ if (normalizedFeatures.includes("commands") && hasCommandFiles) {
3846
+ deleteTasks.push(removeDirectory(join13(".roo", "commands")));
3847
+ }
3848
+ break;
3849
+ case "geminicli":
3850
+ if (normalizedFeatures.includes("rules")) {
3851
+ deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
3852
+ }
3853
+ if (normalizedFeatures.includes("commands") && hasCommandFiles) {
3854
+ deleteTasks.push(removeDirectory(join13(".gemini", "commands")));
3855
+ }
3856
+ break;
3857
+ case "kiro":
3858
+ if (normalizedFeatures.includes("rules")) {
3859
+ deleteTasks.push(removeDirectory(config.outputPaths.kiro));
3860
+ }
3861
+ break;
3862
+ case "opencode":
3863
+ if (normalizedFeatures.includes("rules")) {
3864
+ deleteTasks.push(removeDirectory(config.outputPaths.opencode));
3865
+ }
3866
+ break;
3867
+ case "qwencode":
3868
+ if (normalizedFeatures.includes("rules")) {
3869
+ deleteTasks.push(removeDirectory(config.outputPaths.qwencode));
3870
+ }
3871
+ break;
3872
+ case "windsurf":
3873
+ if (normalizedFeatures.includes("rules")) {
3874
+ deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
3875
+ }
3876
+ break;
3877
+ }
3878
+ }
3879
+ await Promise.all(deleteTasks);
3880
+ logger.info("Deleted existing output directories");
3881
+ }
3882
+ let totalOutputs = 0;
3883
+ if (normalizedFeatures.includes("rules")) {
3884
+ for (const baseDir of baseDirs) {
3885
+ logger.info(`
3886
+ Generating rule configurations for base directory: ${baseDir}`);
3887
+ const outputs = await generateConfigurations(
3888
+ rules,
3889
+ config,
3890
+ config.defaultTargets,
3891
+ baseDir
3892
+ );
3893
+ if (outputs.length === 0) {
3894
+ logger.warn(`\u26A0\uFE0F No rule configurations generated for ${baseDir}`);
3895
+ continue;
3896
+ }
3897
+ for (const output of outputs) {
3898
+ await writeFileContent(output.filepath, output.content);
3899
+ logger.success(`Generated ${output.tool} rule configuration: ${output.filepath}`);
3900
+ }
3901
+ totalOutputs += outputs.length;
2980
3902
  }
3903
+ } else {
3904
+ logger.info("\nSkipping rule generation (not in --features)");
2981
3905
  }
2982
- for (const tool of targetTools) {
2983
- switch (tool) {
2984
- case "augmentcode":
2985
- deleteTasks.push(removeDirectory(join13(".augment", "rules")));
2986
- deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
2987
- break;
2988
- case "augmentcode-legacy":
2989
- deleteTasks.push(removeClaudeGeneratedFiles());
2990
- deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
2991
- break;
2992
- case "copilot":
2993
- deleteTasks.push(removeDirectory(config.outputPaths.copilot));
2994
- break;
2995
- case "cursor":
2996
- deleteTasks.push(removeDirectory(config.outputPaths.cursor));
2997
- break;
2998
- case "cline":
2999
- deleteTasks.push(removeDirectory(config.outputPaths.cline));
3000
- break;
3001
- case "claudecode":
3002
- deleteTasks.push(removeClaudeGeneratedFiles());
3003
- if (hasCommandFiles) {
3004
- deleteTasks.push(removeDirectory(join13(".claude", "commands")));
3906
+ let totalMcpOutputs = 0;
3907
+ if (normalizedFeatures.includes("mcp")) {
3908
+ logger.info("\nGenerating MCP configurations...");
3909
+ for (const baseDir of baseDirs) {
3910
+ try {
3911
+ const mcpConfig = parseMcpConfig(process.cwd());
3912
+ if (!mcpConfig || !mcpConfig.mcpServers || Object.keys(mcpConfig.mcpServers).length === 0) {
3913
+ logger.info(`No MCP configuration found for ${baseDir}`);
3914
+ continue;
3005
3915
  }
3006
- break;
3007
- case "roo":
3008
- deleteTasks.push(removeDirectory(config.outputPaths.roo));
3009
- if (hasCommandFiles) {
3010
- deleteTasks.push(removeDirectory(join13(".roo", "commands")));
3916
+ const mcpResults = await generateMcpConfigurations(
3917
+ mcpConfig,
3918
+ baseDir === process.cwd() ? "." : baseDir,
3919
+ config.defaultTargets
3920
+ );
3921
+ if (mcpResults.length === 0) {
3922
+ logger.info(`No MCP configurations generated for ${baseDir}`);
3923
+ continue;
3011
3924
  }
3012
- break;
3013
- case "geminicli":
3014
- deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
3015
- if (hasCommandFiles) {
3016
- deleteTasks.push(removeDirectory(join13(".gemini", "commands")));
3925
+ for (const result of mcpResults) {
3926
+ await writeFileContent(result.filepath, result.content);
3927
+ logger.success(`Generated ${result.tool} MCP configuration: ${result.filepath}`);
3928
+ totalMcpOutputs++;
3017
3929
  }
3018
- break;
3019
- case "kiro":
3020
- deleteTasks.push(removeDirectory(config.outputPaths.kiro));
3021
- break;
3022
- case "opencode":
3023
- deleteTasks.push(removeDirectory(config.outputPaths.opencode));
3024
- break;
3025
- case "qwencode":
3026
- deleteTasks.push(removeDirectory(config.outputPaths.qwencode));
3027
- break;
3028
- case "windsurf":
3029
- deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
3030
- break;
3930
+ } catch (error) {
3931
+ logger.error(
3932
+ `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
3933
+ );
3934
+ }
3031
3935
  }
3936
+ } else {
3937
+ logger.info("\nSkipping MCP configuration generation (not in --features)");
3032
3938
  }
3033
- await Promise.all(deleteTasks);
3034
- logger.info("Deleted existing output directories");
3035
- }
3036
- let totalOutputs = 0;
3037
- for (const baseDir of baseDirs) {
3038
- logger.info(`
3039
- Generating configurations for base directory: ${baseDir}`);
3040
- const outputs = await generateConfigurations(rules, config, config.defaultTargets, baseDir);
3041
- if (outputs.length === 0) {
3042
- logger.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
3043
- continue;
3939
+ let totalCommandOutputs = 0;
3940
+ if (normalizedFeatures.includes("commands")) {
3941
+ logger.info("\nGenerating command files...");
3942
+ for (const baseDir of baseDirs) {
3943
+ const commandResults = await generateCommands(
3944
+ process.cwd(),
3945
+ baseDir === process.cwd() ? void 0 : baseDir,
3946
+ config.defaultTargets
3947
+ );
3948
+ if (commandResults.length === 0) {
3949
+ logger.info(`No commands found for ${baseDir}`);
3950
+ continue;
3951
+ }
3952
+ for (const result of commandResults) {
3953
+ await writeFileContent(result.filepath, result.content);
3954
+ logger.success(`Generated ${result.tool} command: ${result.filepath}`);
3955
+ totalCommandOutputs++;
3956
+ }
3957
+ }
3958
+ } else {
3959
+ logger.info("\nSkipping command file generation (not in --features)");
3044
3960
  }
3045
- for (const output of outputs) {
3046
- await writeFileContent(output.filepath, output.content);
3047
- logger.success(`Generated ${output.tool} configuration: ${output.filepath}`);
3961
+ const totalIgnoreOutputs = 0;
3962
+ if (normalizedFeatures.includes("ignore")) {
3963
+ logger.info("\nGenerating ignore files...");
3964
+ logger.info("Ignore file generation is not yet implemented");
3965
+ } else {
3966
+ logger.info("\nSkipping ignore file generation (not in --features)");
3048
3967
  }
3049
- totalOutputs += outputs.length;
3050
- }
3051
- if (totalOutputs === 0) {
3052
- logger.warn("\u26A0\uFE0F No configurations generated");
3053
- return;
3054
- }
3055
- logger.info("\nGenerating MCP configurations...");
3056
- let totalMcpOutputs = 0;
3057
- for (const baseDir of baseDirs) {
3058
- try {
3059
- const mcpConfig = parseMcpConfig(process.cwd());
3060
- if (!mcpConfig || !mcpConfig.mcpServers || Object.keys(mcpConfig.mcpServers).length === 0) {
3061
- logger.info(`No MCP configuration found for ${baseDir}`);
3062
- continue;
3063
- }
3064
- const mcpResults = await generateMcpConfigurations(
3065
- mcpConfig,
3066
- baseDir === process.cwd() ? "." : baseDir,
3067
- config.defaultTargets
3068
- );
3069
- if (mcpResults.length === 0) {
3070
- logger.info(`No MCP configurations generated for ${baseDir}`);
3071
- continue;
3072
- }
3073
- for (const result of mcpResults) {
3074
- await writeFileContent(result.filepath, result.content);
3075
- logger.success(`Generated ${result.tool} MCP configuration: ${result.filepath}`);
3076
- totalMcpOutputs++;
3968
+ let totalSubagentOutputs = 0;
3969
+ if (normalizedFeatures.includes("subagents")) {
3970
+ logger.info("\nGenerating subagent files...");
3971
+ for (const baseDir of baseDirs) {
3972
+ const subagentResults = await generateSubagents(
3973
+ config.aiRulesDir,
3974
+ baseDir === process.cwd() ? void 0 : baseDir,
3975
+ config.defaultTargets,
3976
+ rules
3977
+ );
3978
+ if (subagentResults.length === 0) {
3979
+ logger.info(`No subagents generated for ${baseDir}`);
3980
+ continue;
3981
+ }
3982
+ for (const result of subagentResults) {
3983
+ await writeFileContent(result.filepath, result.content);
3984
+ logger.success(`Generated ${result.tool} subagent: ${result.filepath}`);
3985
+ totalSubagentOutputs++;
3986
+ }
3077
3987
  }
3078
- } catch (error) {
3079
- logger.error(
3080
- `\u274C Failed to generate MCP configurations: ${error instanceof Error ? error.message : String(error)}`
3081
- );
3082
- }
3083
- }
3084
- logger.info("\nGenerating command files...");
3085
- let totalCommandOutputs = 0;
3086
- for (const baseDir of baseDirs) {
3087
- const commandResults = await generateCommands(
3088
- process.cwd(),
3089
- baseDir === process.cwd() ? void 0 : baseDir,
3090
- config.defaultTargets
3091
- );
3092
- if (commandResults.length === 0) {
3093
- logger.info(`No commands found for ${baseDir}`);
3094
- continue;
3988
+ } else {
3989
+ logger.info("\nSkipping subagent file generation (not in --features)");
3095
3990
  }
3096
- for (const result of commandResults) {
3097
- await writeFileContent(result.filepath, result.content);
3098
- logger.success(`Generated ${result.tool} command: ${result.filepath}`);
3099
- totalCommandOutputs++;
3991
+ const totalGenerated = totalOutputs + totalMcpOutputs + totalCommandOutputs + totalIgnoreOutputs + totalSubagentOutputs;
3992
+ if (totalGenerated === 0) {
3993
+ const enabledFeatures = normalizedFeatures.join(", ");
3994
+ logger.warn(`\u26A0\uFE0F No files generated for enabled features: ${enabledFeatures}`);
3995
+ return;
3100
3996
  }
3101
- }
3102
- const totalGenerated = totalOutputs + totalMcpOutputs + totalCommandOutputs;
3103
- if (totalGenerated > 0) {
3104
- const parts = [];
3105
- if (totalOutputs > 0) parts.push(`${totalOutputs} configurations`);
3106
- if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP configurations`);
3107
- if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
3108
- logger.success(
3109
- `
3997
+ if (totalGenerated > 0) {
3998
+ const parts = [];
3999
+ if (totalOutputs > 0) parts.push(`${totalOutputs} configurations`);
4000
+ if (totalMcpOutputs > 0) parts.push(`${totalMcpOutputs} MCP configurations`);
4001
+ if (totalCommandOutputs > 0) parts.push(`${totalCommandOutputs} commands`);
4002
+ if (totalSubagentOutputs > 0) parts.push(`${totalSubagentOutputs} subagents`);
4003
+ logger.success(
4004
+ `
3110
4005
  \u{1F389} All done! Generated ${totalGenerated} file(s) total (${parts.join(" + ")})`
3111
- );
4006
+ );
4007
+ }
4008
+ } catch (error) {
4009
+ logger.error("\u274C Failed to generate configurations:", error);
4010
+ process.exit(1);
3112
4011
  }
3113
4012
  } catch (error) {
3114
- logger.error("\u274C Failed to generate configurations:", error);
4013
+ logger.error("\u274C Failed to resolve configuration:", error);
3115
4014
  process.exit(1);
3116
4015
  }
3117
4016
  }
@@ -3134,7 +4033,9 @@ var gitignoreCommand = async () => {
3134
4033
  "**/CLAUDE.md",
3135
4034
  "**/.claude/memories/",
3136
4035
  "**/.claude/commands/",
4036
+ "**/.claude/agents/",
3137
4037
  "**/AGENTS.md",
4038
+ "**/.agents/",
3138
4039
  "**/.codexignore",
3139
4040
  "**/.roo/rules/",
3140
4041
  "**/.rooignore",
@@ -3194,11 +4095,155 @@ ${linesToAdd.join("\n")}
3194
4095
  };
3195
4096
 
3196
4097
  // src/core/importer.ts
3197
- import { join as join21 } from "path";
3198
- import matter2 from "gray-matter";
4098
+ import { join as join23 } from "path";
4099
+ import matter3 from "gray-matter";
4100
+
4101
+ // src/parsers/agentsmd.ts
4102
+ import { join as join15 } from "path";
4103
+ async function parseAgentsMdConfiguration(baseDir = process.cwd()) {
4104
+ const errors = [];
4105
+ const rules = [];
4106
+ const projectAgentsPath = join15(baseDir, "AGENTS.md");
4107
+ if (await fileExists(projectAgentsPath)) {
4108
+ try {
4109
+ const content = await readFileContent(projectAgentsPath);
4110
+ if (content.trim()) {
4111
+ const frontmatter = {
4112
+ root: true,
4113
+ targets: ["agentsmd"],
4114
+ description: "Project-level AGENTS.md instructions",
4115
+ globs: ["**/*"]
4116
+ };
4117
+ rules.push({
4118
+ frontmatter,
4119
+ content: content.trim(),
4120
+ filename: "project-instructions",
4121
+ filepath: projectAgentsPath
4122
+ });
4123
+ }
4124
+ } catch (error) {
4125
+ const errorMessage = error instanceof Error ? error.message : String(error);
4126
+ errors.push(`Failed to parse AGENTS.md: ${errorMessage}`);
4127
+ }
4128
+ }
4129
+ const memoriesDir = join15(baseDir, ".agents", "memories");
4130
+ if (await fileExists(memoriesDir)) {
4131
+ try {
4132
+ const { readdir: readdir2, stat } = await import("fs/promises");
4133
+ const memoriesPath = memoriesDir;
4134
+ const memoriesStat = await stat(memoriesPath);
4135
+ if (memoriesStat.isDirectory()) {
4136
+ const files = await readdir2(memoriesPath);
4137
+ for (const file of files) {
4138
+ if (file.endsWith(".md")) {
4139
+ const filePath = join15(memoriesPath, file);
4140
+ try {
4141
+ const content = await readFileContent(filePath);
4142
+ if (content.trim()) {
4143
+ const filename = file.replace(/\.md$/, "");
4144
+ const frontmatter = {
4145
+ root: false,
4146
+ targets: ["agentsmd"],
4147
+ description: `AGENTS.md memory: ${filename}`,
4148
+ globs: ["**/*"]
4149
+ };
4150
+ rules.push({
4151
+ frontmatter,
4152
+ content: content.trim(),
4153
+ filename,
4154
+ filepath: filePath
4155
+ });
4156
+ }
4157
+ } catch (error) {
4158
+ const errorMessage = error instanceof Error ? error.message : String(error);
4159
+ errors.push(`Failed to parse memory file ${file}: ${errorMessage}`);
4160
+ }
4161
+ }
4162
+ }
4163
+ }
4164
+ } catch {
4165
+ }
4166
+ }
4167
+ try {
4168
+ const { readdir: readdir2 } = await import("fs/promises");
4169
+ const files = await readdir2(baseDir);
4170
+ for (const file of files) {
4171
+ if (file === "AGENTS.md") continue;
4172
+ if (file.endsWith(".md") && (file.includes("agents") || file.includes("instructions") || file.includes("guidelines") || file.includes("rules"))) {
4173
+ const filePath = join15(baseDir, file);
4174
+ try {
4175
+ const content = await readFileContent(filePath);
4176
+ if (content.trim()) {
4177
+ const filename = file.replace(/\.md$/, "");
4178
+ const frontmatter = {
4179
+ root: false,
4180
+ targets: ["agentsmd"],
4181
+ description: `AGENTS.md instructions: ${filename}`,
4182
+ globs: ["**/*"]
4183
+ };
4184
+ rules.push({
4185
+ frontmatter,
4186
+ content: content.trim(),
4187
+ filename,
4188
+ filepath: filePath
4189
+ });
4190
+ }
4191
+ } catch (error) {
4192
+ const errorMessage = error instanceof Error ? error.message : String(error);
4193
+ errors.push(`Failed to parse ${file}: ${errorMessage}`);
4194
+ }
4195
+ }
4196
+ }
4197
+ for (const file of files) {
4198
+ const filePath = join15(baseDir, file);
4199
+ try {
4200
+ const { stat } = await import("fs/promises");
4201
+ const stats = await stat(filePath);
4202
+ if (stats.isDirectory() && !file.startsWith(".") && file !== "node_modules") {
4203
+ const subAgentsPath = join15(filePath, "AGENTS.md");
4204
+ if (await fileExists(subAgentsPath)) {
4205
+ try {
4206
+ const content = await readFileContent(subAgentsPath);
4207
+ if (content.trim()) {
4208
+ const frontmatter = {
4209
+ root: false,
4210
+ targets: ["agentsmd"],
4211
+ description: `Directory-specific AGENTS.md instructions: ${file}`,
4212
+ globs: [`${file}/**/*`]
4213
+ };
4214
+ rules.push({
4215
+ frontmatter,
4216
+ content: content.trim(),
4217
+ filename: `${file}-agents`,
4218
+ filepath: subAgentsPath
4219
+ });
4220
+ }
4221
+ } catch (error) {
4222
+ const errorMessage = error instanceof Error ? error.message : String(error);
4223
+ errors.push(`Failed to parse ${subAgentsPath}: ${errorMessage}`);
4224
+ }
4225
+ }
4226
+ }
4227
+ } catch {
4228
+ }
4229
+ }
4230
+ } catch (error) {
4231
+ const errorMessage = error instanceof Error ? error.message : String(error);
4232
+ errors.push(`Failed to scan directory for AGENTS.md files: ${errorMessage}`);
4233
+ }
4234
+ if (rules.length === 0) {
4235
+ errors.push(
4236
+ "No AGENTS.md configuration files found. Expected to find AGENTS.md in the project root or memory files in .agents/memories/."
4237
+ );
4238
+ }
4239
+ return {
4240
+ rules,
4241
+ errors
4242
+ };
4243
+ }
3199
4244
 
3200
4245
  // src/parsers/shared-helpers.ts
3201
- import { basename as basename3, join as join15 } from "path";
4246
+ import { basename as basename3, join as join16 } from "path";
3202
4247
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3203
4248
  const errors = [];
3204
4249
  const rules = [];
@@ -3251,11 +4296,11 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
3251
4296
  const dirPath = resolvePath(dirConfig.directory, baseDir);
3252
4297
  if (await fileExists(dirPath)) {
3253
4298
  const result = await safeAsyncOperation(async () => {
3254
- const { readdir } = await import("fs/promises");
3255
- const files = await readdir(dirPath);
4299
+ const { readdir: readdir2 } = await import("fs/promises");
4300
+ const files = await readdir2(dirPath);
3256
4301
  for (const file of files) {
3257
4302
  if (file.endsWith(dirConfig.filePattern)) {
3258
- const filePath = join15(dirPath, file);
4303
+ const filePath = join16(dirPath, file);
3259
4304
  const fileResult = await safeAsyncOperation(async () => {
3260
4305
  const rawContent = await readFileContent(filePath);
3261
4306
  let content;
@@ -3402,11 +4447,11 @@ function parseMainFile(content, filepath, config) {
3402
4447
  async function parseMemoryFiles(memoryDir, config) {
3403
4448
  const rules = [];
3404
4449
  try {
3405
- const { readdir } = await import("fs/promises");
3406
- const files = await readdir(memoryDir);
4450
+ const { readdir: readdir2 } = await import("fs/promises");
4451
+ const files = await readdir2(memoryDir);
3407
4452
  for (const file of files) {
3408
4453
  if (file.endsWith(".md")) {
3409
- const filePath = join15(memoryDir, file);
4454
+ const filePath = join16(memoryDir, file);
3410
4455
  const content = await readFileContent(filePath);
3411
4456
  if (content.trim()) {
3412
4457
  const filename = basename3(file, ".md");
@@ -3432,11 +4477,11 @@ async function parseMemoryFiles(memoryDir, config) {
3432
4477
  async function parseCommandsFiles(commandsDir, config) {
3433
4478
  const rules = [];
3434
4479
  try {
3435
- const { readdir } = await import("fs/promises");
3436
- const files = await readdir(commandsDir);
4480
+ const { readdir: readdir2 } = await import("fs/promises");
4481
+ const files = await readdir2(commandsDir);
3437
4482
  for (const file of files) {
3438
4483
  if (file.endsWith(".md")) {
3439
- const filePath = join15(commandsDir, file);
4484
+ const filePath = join16(commandsDir, file);
3440
4485
  const content = await readFileContent(filePath);
3441
4486
  if (content.trim()) {
3442
4487
  const filename = basename3(file, ".md");
@@ -3528,7 +4573,7 @@ async function parseAmazonqcliConfiguration(baseDir = process.cwd()) {
3528
4573
  }
3529
4574
 
3530
4575
  // src/parsers/augmentcode.ts
3531
- import { basename as basename4, join as join16 } from "path";
4576
+ import { basename as basename4, join as join17 } from "path";
3532
4577
 
3533
4578
  // src/utils/parser-helpers.ts
3534
4579
  function createParseResult() {
@@ -3576,7 +4621,7 @@ async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
3576
4621
  async function parseUnifiedAugmentcode(baseDir, config) {
3577
4622
  const result = createParseResult();
3578
4623
  if (config.rulesDir) {
3579
- const rulesDir = join16(baseDir, config.rulesDir);
4624
+ const rulesDir = join17(baseDir, config.rulesDir);
3580
4625
  if (await fileExists(rulesDir)) {
3581
4626
  const rulesResult = await parseAugmentRules(rulesDir, config);
3582
4627
  addRules(result, rulesResult.rules);
@@ -3589,7 +4634,7 @@ async function parseUnifiedAugmentcode(baseDir, config) {
3589
4634
  }
3590
4635
  }
3591
4636
  if (config.legacyFilePath) {
3592
- const legacyPath = join16(baseDir, config.legacyFilePath);
4637
+ const legacyPath = join17(baseDir, config.legacyFilePath);
3593
4638
  if (await fileExists(legacyPath)) {
3594
4639
  const legacyResult = await parseAugmentGuidelines(legacyPath, config);
3595
4640
  if (legacyResult.rule) {
@@ -3609,11 +4654,11 @@ async function parseAugmentRules(rulesDir, config) {
3609
4654
  const rules = [];
3610
4655
  const errors = [];
3611
4656
  try {
3612
- const { readdir } = await import("fs/promises");
3613
- const files = await readdir(rulesDir);
4657
+ const { readdir: readdir2 } = await import("fs/promises");
4658
+ const files = await readdir2(rulesDir);
3614
4659
  for (const file of files) {
3615
4660
  if (file.endsWith(".md") || file.endsWith(".mdc")) {
3616
- const filePath = join16(rulesDir, file);
4661
+ const filePath = join17(rulesDir, file);
3617
4662
  try {
3618
4663
  const rawContent = await readFileContent(filePath);
3619
4664
  const parsed = parseFrontmatter(rawContent);
@@ -3680,7 +4725,7 @@ async function parseAugmentGuidelines(guidelinesPath, config) {
3680
4725
 
3681
4726
  // src/parsers/claudecode.ts
3682
4727
  async function parseClaudeConfiguration(baseDir = process.cwd()) {
3683
- return parseMemoryBasedConfiguration(baseDir, {
4728
+ const memoryResult = await parseMemoryBasedConfiguration(baseDir, {
3684
4729
  tool: "claudecode",
3685
4730
  mainFileName: "CLAUDE.md",
3686
4731
  memoryDirPath: ".claude/memories",
@@ -3690,6 +4735,24 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
3690
4735
  filenamePrefix: "claude",
3691
4736
  commandsDirPath: ".claude/commands"
3692
4737
  });
4738
+ const result = {
4739
+ rules: memoryResult.rules,
4740
+ errors: memoryResult.errors
4741
+ };
4742
+ if (memoryResult.ignorePatterns) {
4743
+ result.ignorePatterns = memoryResult.ignorePatterns;
4744
+ }
4745
+ if (memoryResult.mcpServers) {
4746
+ result.mcpServers = memoryResult.mcpServers;
4747
+ }
4748
+ const agentsDir = resolvePath(".claude/agents", baseDir);
4749
+ if (await fileExists(agentsDir)) {
4750
+ const subagents = await parseSubagentsFromDirectory(agentsDir);
4751
+ if (subagents.length > 0) {
4752
+ result.subagents = subagents;
4753
+ }
4754
+ }
4755
+ return result;
3693
4756
  }
3694
4757
 
3695
4758
  // src/parsers/cline.ts
@@ -3713,7 +4776,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
3713
4776
  }
3714
4777
 
3715
4778
  // src/parsers/codexcli.ts
3716
- import { join as join17 } from "path";
4779
+ import { join as join18 } from "path";
3717
4780
 
3718
4781
  // src/parsers/copilot.ts
3719
4782
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
@@ -3736,9 +4799,9 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
3736
4799
  }
3737
4800
 
3738
4801
  // src/parsers/cursor.ts
3739
- import { basename as basename5, join as join18 } from "path";
4802
+ import { basename as basename5, join as join19 } from "path";
3740
4803
  import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
3741
- import { z as z7 } from "zod/mini";
4804
+ import { z as z8 } from "zod/mini";
3742
4805
  var customMatterOptions = {
3743
4806
  engines: {
3744
4807
  yaml: {
@@ -3766,7 +4829,7 @@ var customMatterOptions = {
3766
4829
  }
3767
4830
  };
3768
4831
  function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
3769
- const FrontmatterSchema = z7.record(z7.string(), z7.unknown());
4832
+ const FrontmatterSchema = z8.record(z8.string(), z8.unknown());
3770
4833
  const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
3771
4834
  if (!parseResult.success) {
3772
4835
  return {
@@ -3860,7 +4923,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3860
4923
  const rules = [];
3861
4924
  let ignorePatterns;
3862
4925
  let mcpServers;
3863
- const cursorFilePath = join18(baseDir, ".cursorrules");
4926
+ const cursorFilePath = join19(baseDir, ".cursorrules");
3864
4927
  if (await fileExists(cursorFilePath)) {
3865
4928
  try {
3866
4929
  const rawContent = await readFileContent(cursorFilePath);
@@ -3881,14 +4944,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3881
4944
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
3882
4945
  }
3883
4946
  }
3884
- const cursorRulesDir = join18(baseDir, ".cursor", "rules");
4947
+ const cursorRulesDir = join19(baseDir, ".cursor", "rules");
3885
4948
  if (await fileExists(cursorRulesDir)) {
3886
4949
  try {
3887
- const { readdir } = await import("fs/promises");
3888
- const files = await readdir(cursorRulesDir);
4950
+ const { readdir: readdir2 } = await import("fs/promises");
4951
+ const files = await readdir2(cursorRulesDir);
3889
4952
  for (const file of files) {
3890
4953
  if (file.endsWith(".mdc")) {
3891
- const filePath = join18(cursorRulesDir, file);
4954
+ const filePath = join19(cursorRulesDir, file);
3892
4955
  try {
3893
4956
  const rawContent = await readFileContent(filePath);
3894
4957
  const parsed = parseFrontmatter(rawContent, { matterOptions: customMatterOptions });
@@ -3917,7 +4980,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3917
4980
  if (rules.length === 0) {
3918
4981
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
3919
4982
  }
3920
- const cursorIgnorePath = join18(baseDir, ".cursorignore");
4983
+ const cursorIgnorePath = join19(baseDir, ".cursorignore");
3921
4984
  if (await fileExists(cursorIgnorePath)) {
3922
4985
  try {
3923
4986
  const content = await readFileContent(cursorIgnorePath);
@@ -3930,7 +4993,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3930
4993
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
3931
4994
  }
3932
4995
  }
3933
- const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
4996
+ const cursorMcpPath = join19(baseDir, ".cursor", "mcp.json");
3934
4997
  if (await fileExists(cursorMcpPath)) {
3935
4998
  try {
3936
4999
  const content = await readFileContent(cursorMcpPath);
@@ -3953,6 +5016,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
3953
5016
  }
3954
5017
 
3955
5018
  // src/parsers/geminicli.ts
5019
+ import { basename as basename6, join as join20 } from "path";
3956
5020
  async function parseAiexclude(aiexcludePath) {
3957
5021
  try {
3958
5022
  const content = await readFileContent(aiexcludePath);
@@ -3962,8 +5026,51 @@ async function parseAiexclude(aiexcludePath) {
3962
5026
  return [];
3963
5027
  }
3964
5028
  }
5029
+ async function parseGeminiCommands(commandsDir) {
5030
+ const rules = [];
5031
+ try {
5032
+ const { readdir: readdir2 } = await import("fs/promises");
5033
+ const { parse } = await import("smol-toml");
5034
+ const files = await readdir2(commandsDir);
5035
+ for (const file of files) {
5036
+ if (file.endsWith(".toml")) {
5037
+ const filePath = join20(commandsDir, file);
5038
+ const content = await readFileContent(filePath);
5039
+ if (content.trim()) {
5040
+ const filename = basename6(file, ".toml");
5041
+ try {
5042
+ const parsed = parse(content);
5043
+ if (typeof parsed !== "object" || parsed === null) {
5044
+ continue;
5045
+ }
5046
+ const commandConfig = parsed;
5047
+ if (typeof commandConfig.prompt === "string") {
5048
+ const description = typeof commandConfig.description === "string" ? commandConfig.description : `Command: ${filename}`;
5049
+ const frontmatter = {
5050
+ root: false,
5051
+ targets: ["geminicli"],
5052
+ description,
5053
+ globs: ["**/*"]
5054
+ };
5055
+ rules.push({
5056
+ frontmatter,
5057
+ content: commandConfig.prompt,
5058
+ filename,
5059
+ filepath: filePath,
5060
+ type: "command"
5061
+ });
5062
+ }
5063
+ } catch {
5064
+ }
5065
+ }
5066
+ }
5067
+ }
5068
+ } catch {
5069
+ }
5070
+ return rules;
5071
+ }
3965
5072
  async function parseGeminiConfiguration(baseDir = process.cwd()) {
3966
- return parseMemoryBasedConfiguration(baseDir, {
5073
+ const result = await parseMemoryBasedConfiguration(baseDir, {
3967
5074
  tool: "geminicli",
3968
5075
  mainFileName: "GEMINI.md",
3969
5076
  memoryDirPath: ".gemini/memories",
@@ -3974,17 +5081,23 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
3974
5081
  additionalIgnoreFile: {
3975
5082
  path: ".aiexclude",
3976
5083
  parser: parseAiexclude
3977
- },
3978
- commandsDirPath: ".gemini/commands"
5084
+ }
5085
+ // commandsDirPath is removed - Gemini uses .toml files which need special handling
3979
5086
  });
5087
+ const commandsDir = resolvePath(".gemini/commands", baseDir);
5088
+ if (await fileExists(commandsDir)) {
5089
+ const commandsRules = await parseGeminiCommands(commandsDir);
5090
+ result.rules.push(...commandsRules);
5091
+ }
5092
+ return result;
3980
5093
  }
3981
5094
 
3982
5095
  // src/parsers/junie.ts
3983
- import { join as join19 } from "path";
5096
+ import { join as join21 } from "path";
3984
5097
  async function parseJunieConfiguration(baseDir = process.cwd()) {
3985
5098
  const errors = [];
3986
5099
  const rules = [];
3987
- const guidelinesPath = join19(baseDir, ".junie", "guidelines.md");
5100
+ const guidelinesPath = join21(baseDir, ".junie", "guidelines.md");
3988
5101
  if (!await fileExists(guidelinesPath)) {
3989
5102
  errors.push(".junie/guidelines.md file not found");
3990
5103
  return { rules, errors };
@@ -4078,13 +5191,15 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
4078
5191
  }
4079
5192
 
4080
5193
  // src/parsers/windsurf.ts
4081
- import { readFile } from "fs/promises";
4082
- import { join as join20 } from "path";
5194
+ import { readFile as readFile2 } from "fs/promises";
5195
+ import { join as join22 } from "path";
4083
5196
 
4084
5197
  // src/core/importer.ts
4085
5198
  async function importConfiguration(options) {
4086
5199
  const {
4087
5200
  tool,
5201
+ features = ["rules", "commands", "mcp", "ignore", "subagents"],
5202
+ // Default to all features for backward compatibility
4088
5203
  baseDir = process.cwd(),
4089
5204
  rulesDir = ".rulesync",
4090
5205
  verbose = false,
@@ -4094,11 +5209,18 @@ async function importConfiguration(options) {
4094
5209
  let rules = [];
4095
5210
  let ignorePatterns;
4096
5211
  let mcpServers;
5212
+ let subagents;
4097
5213
  if (verbose) {
4098
5214
  logger.log(`Importing ${tool} configuration from ${baseDir}...`);
4099
5215
  }
4100
5216
  try {
4101
5217
  switch (tool) {
5218
+ case "agentsmd": {
5219
+ const agentsmdResult = await parseAgentsMdConfiguration(baseDir);
5220
+ rules = agentsmdResult.rules;
5221
+ errors.push(...agentsmdResult.errors);
5222
+ break;
5223
+ }
4102
5224
  case "amazonqcli": {
4103
5225
  const amazonqResult = await parseAmazonqcliConfiguration(baseDir);
4104
5226
  rules = amazonqResult.rules;
@@ -4124,6 +5246,7 @@ async function importConfiguration(options) {
4124
5246
  errors.push(...claudeResult.errors);
4125
5247
  ignorePatterns = claudeResult.ignorePatterns;
4126
5248
  mcpServers = claudeResult.mcpServers;
5249
+ subagents = claudeResult.subagents;
4127
5250
  break;
4128
5251
  }
4129
5252
  case "cursor": {
@@ -4190,10 +5313,20 @@ async function importConfiguration(options) {
4190
5313
  errors.push(`Failed to parse ${tool} configuration: ${errorMessage}`);
4191
5314
  return { success: false, rulesCreated: 0, errors };
4192
5315
  }
4193
- if (rules.length === 0 && !ignorePatterns && !mcpServers) {
5316
+ if (rules.length === 0 && !ignorePatterns && !mcpServers && !subagents) {
4194
5317
  return { success: false, rulesCreated: 0, errors };
4195
5318
  }
4196
- const rulesDirPath = join21(baseDir, rulesDir);
5319
+ const rulesEnabled = features.includes("rules") || features.includes("commands");
5320
+ const ignoreEnabled = features.includes("ignore");
5321
+ const mcpEnabled = features.includes("mcp");
5322
+ const subagentsEnabled = features.includes("subagents");
5323
+ if (!rulesEnabled && !ignoreEnabled && !mcpEnabled && !subagentsEnabled) {
5324
+ if (verbose) {
5325
+ logger.log("No relevant features enabled for import");
5326
+ }
5327
+ return { success: false, rulesCreated: 0, errors: ["No features enabled for import"] };
5328
+ }
5329
+ const rulesDirPath = join23(baseDir, rulesDir);
4197
5330
  try {
4198
5331
  const { mkdir: mkdir2 } = await import("fs/promises");
4199
5332
  await mkdir2(rulesDirPath, { recursive: true });
@@ -4203,37 +5336,49 @@ async function importConfiguration(options) {
4203
5336
  return { success: false, rulesCreated: 0, errors };
4204
5337
  }
4205
5338
  let rulesCreated = 0;
4206
- for (const rule of rules) {
4207
- try {
4208
- const baseFilename = rule.filename;
4209
- let targetDir = rulesDirPath;
4210
- if (rule.type === "command") {
4211
- targetDir = join21(rulesDirPath, "commands");
4212
- const { mkdir: mkdir2 } = await import("fs/promises");
4213
- await mkdir2(targetDir, { recursive: true });
4214
- } else {
4215
- if (!useLegacyLocation) {
4216
- targetDir = join21(rulesDirPath, "rules");
5339
+ if (rulesEnabled) {
5340
+ for (const rule of rules) {
5341
+ try {
5342
+ const baseFilename = rule.filename;
5343
+ let targetDir = rulesDirPath;
5344
+ if (rule.type === "command") {
5345
+ if (!features.includes("commands")) {
5346
+ continue;
5347
+ }
5348
+ targetDir = join23(rulesDirPath, "commands");
4217
5349
  const { mkdir: mkdir2 } = await import("fs/promises");
4218
5350
  await mkdir2(targetDir, { recursive: true });
5351
+ } else {
5352
+ if (!features.includes("rules")) {
5353
+ continue;
5354
+ }
5355
+ if (!useLegacyLocation) {
5356
+ targetDir = join23(rulesDirPath, "rules");
5357
+ const { mkdir: mkdir2 } = await import("fs/promises");
5358
+ await mkdir2(targetDir, { recursive: true });
5359
+ }
4219
5360
  }
5361
+ const filePath = join23(targetDir, `${baseFilename}.md`);
5362
+ const content = generateRuleFileContent(rule);
5363
+ await writeFileContent(filePath, content);
5364
+ rulesCreated++;
5365
+ if (verbose) {
5366
+ logger.success(`Created rule file: ${filePath}`);
5367
+ }
5368
+ } catch (error) {
5369
+ const errorMessage = error instanceof Error ? error.message : String(error);
5370
+ errors.push(`Failed to create rule file for ${rule.filename}: ${errorMessage}`);
4220
5371
  }
4221
- const filePath = join21(targetDir, `${baseFilename}.md`);
4222
- const content = generateRuleFileContent(rule);
4223
- await writeFileContent(filePath, content);
4224
- rulesCreated++;
4225
- if (verbose) {
4226
- logger.success(`Created rule file: ${filePath}`);
4227
- }
4228
- } catch (error) {
4229
- const errorMessage = error instanceof Error ? error.message : String(error);
4230
- errors.push(`Failed to create rule file for ${rule.filename}: ${errorMessage}`);
5372
+ }
5373
+ } else {
5374
+ if (verbose && rules.length > 0) {
5375
+ logger.log(`Skipping ${rules.length} rule(s) (rules/commands features not enabled)`);
4231
5376
  }
4232
5377
  }
4233
5378
  let ignoreFileCreated = false;
4234
- if (ignorePatterns && ignorePatterns.length > 0) {
5379
+ if (ignoreEnabled && ignorePatterns && ignorePatterns.length > 0) {
4235
5380
  try {
4236
- const rulesyncignorePath = join21(baseDir, ".rulesyncignore");
5381
+ const rulesyncignorePath = join23(baseDir, ".rulesyncignore");
4237
5382
  const ignoreContent = `${ignorePatterns.join("\n")}
4238
5383
  `;
4239
5384
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -4245,11 +5390,13 @@ async function importConfiguration(options) {
4245
5390
  const errorMessage = error instanceof Error ? error.message : String(error);
4246
5391
  errors.push(`Failed to create .rulesyncignore: ${errorMessage}`);
4247
5392
  }
5393
+ } else if (verbose && ignorePatterns && ignorePatterns.length > 0 && !ignoreEnabled) {
5394
+ logger.log(`Skipping ignore patterns (ignore feature not enabled)`);
4248
5395
  }
4249
5396
  let mcpFileCreated = false;
4250
- if (mcpServers && Object.keys(mcpServers).length > 0) {
5397
+ if (mcpEnabled && mcpServers && Object.keys(mcpServers).length > 0) {
4251
5398
  try {
4252
- const mcpPath = join21(baseDir, rulesDir, ".mcp.json");
5399
+ const mcpPath = join23(baseDir, rulesDir, ".mcp.json");
4253
5400
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
4254
5401
  `;
4255
5402
  await writeFileContent(mcpPath, mcpContent);
@@ -4261,14 +5408,51 @@ async function importConfiguration(options) {
4261
5408
  const errorMessage = error instanceof Error ? error.message : String(error);
4262
5409
  errors.push(`Failed to create .mcp.json: ${errorMessage}`);
4263
5410
  }
5411
+ } else if (verbose && mcpServers && Object.keys(mcpServers).length > 0 && !mcpEnabled) {
5412
+ logger.log(`Skipping MCP configuration (mcp feature not enabled)`);
4264
5413
  }
4265
- return {
4266
- success: errors.length === 0 && (rulesCreated > 0 || ignoreFileCreated || mcpFileCreated),
5414
+ let subagentsCreated = 0;
5415
+ if (subagentsEnabled && subagents && subagents.length > 0) {
5416
+ try {
5417
+ const { mkdir: mkdir2 } = await import("fs/promises");
5418
+ const subagentsDir = join23(baseDir, rulesDir, "subagents");
5419
+ await mkdir2(subagentsDir, { recursive: true });
5420
+ for (const subagent of subagents) {
5421
+ try {
5422
+ const filename = `${subagent.filename}.md`;
5423
+ const filepath = join23(subagentsDir, filename);
5424
+ const content = generateSubagentFileContent(subagent);
5425
+ await writeFileContent(filepath, content);
5426
+ subagentsCreated++;
5427
+ if (verbose) {
5428
+ logger.success(`Created subagent: ${filename}`);
5429
+ }
5430
+ } catch (error) {
5431
+ const errorMessage = error instanceof Error ? error.message : String(error);
5432
+ errors.push(`Failed to create subagent ${subagent.filename}: ${errorMessage}`);
5433
+ }
5434
+ }
5435
+ if (verbose && subagentsCreated > 0) {
5436
+ logger.success(`Created ${subagentsCreated} subagent files`);
5437
+ }
5438
+ } catch (error) {
5439
+ const errorMessage = error instanceof Error ? error.message : String(error);
5440
+ errors.push(`Failed to create subagents directory: ${errorMessage}`);
5441
+ }
5442
+ } else if (verbose && subagents && subagents.length > 0 && !subagentsEnabled) {
5443
+ logger.log(`Skipping subagents (subagents feature not enabled)`);
5444
+ }
5445
+ const result = {
5446
+ success: errors.length === 0 && (rulesCreated > 0 || ignoreFileCreated || mcpFileCreated || subagentsCreated > 0),
4267
5447
  rulesCreated,
4268
5448
  errors,
4269
5449
  ignoreFileCreated,
4270
5450
  mcpFileCreated
4271
5451
  };
5452
+ if (subagentsCreated > 0) {
5453
+ result.subagentsCreated = subagentsCreated;
5454
+ }
5455
+ return result;
4272
5456
  }
4273
5457
  function generateRuleFileContent(rule) {
4274
5458
  if (rule.type === "command") {
@@ -4276,69 +5460,108 @@ function generateRuleFileContent(rule) {
4276
5460
  description: rule.frontmatter.description,
4277
5461
  targets: rule.frontmatter.targets
4278
5462
  };
4279
- const frontmatter2 = matter2.stringify("", simplifiedFrontmatter);
5463
+ const frontmatter2 = matter3.stringify("", simplifiedFrontmatter);
4280
5464
  return frontmatter2 + rule.content;
4281
5465
  }
4282
- const frontmatter = matter2.stringify("", rule.frontmatter);
5466
+ const frontmatter = matter3.stringify("", rule.frontmatter);
4283
5467
  return frontmatter + rule.content;
4284
5468
  }
5469
+ function generateSubagentFileContent(subagent) {
5470
+ const frontmatter = matter3.stringify("", subagent.frontmatter);
5471
+ return frontmatter + subagent.content;
5472
+ }
4285
5473
 
4286
5474
  // src/cli/commands/import.ts
4287
5475
  async function importCommand(options = {}) {
4288
5476
  logger.setVerbose(options.verbose || false);
4289
- const tools = [];
4290
- if (options.amazonqcli) tools.push("amazonqcli");
4291
- if (options.augmentcode) tools.push("augmentcode");
4292
- if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
4293
- if (options.claudecode) tools.push("claudecode");
4294
- if (options.cursor) tools.push("cursor");
4295
- if (options.copilot) tools.push("copilot");
4296
- if (options.cline) tools.push("cline");
4297
- if (options.roo) tools.push("roo");
4298
- if (options.geminicli) tools.push("geminicli");
4299
- if (options.qwencode) tools.push("qwencode");
4300
- if (options.opencode) tools.push("opencode");
4301
- if (tools.length === 0) {
4302
- logger.error(
4303
- "\u274C Please specify one tool to import from (--amazonqcli, --augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli, --qwencode, --opencode)"
5477
+ let resolvedFeatures;
5478
+ let showWarning = false;
5479
+ if (options.features !== void 0) {
5480
+ resolvedFeatures = options.features;
5481
+ } else {
5482
+ resolvedFeatures = "*";
5483
+ showWarning = true;
5484
+ }
5485
+ if (showWarning) {
5486
+ showBackwardCompatibilityWarning(
5487
+ "importing",
5488
+ "rulesync import --targets cursor,copilot --features rules,mcp,ignore"
4304
5489
  );
5490
+ }
5491
+ const normalizedFeatures = normalizeFeatures(resolvedFeatures);
5492
+ let tools = [];
5493
+ if (options.targets && options.targets.length > 0) {
5494
+ tools = options.targets;
5495
+ } else {
5496
+ if (options.agentsmd) tools.push("agentsmd");
5497
+ if (options.amazonqcli) tools.push("amazonqcli");
5498
+ if (options.augmentcode) tools.push("augmentcode");
5499
+ if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
5500
+ if (options.claudecode) tools.push("claudecode");
5501
+ if (options.cursor) tools.push("cursor");
5502
+ if (options.copilot) tools.push("copilot");
5503
+ if (options.cline) tools.push("cline");
5504
+ if (options.roo) tools.push("roo");
5505
+ if (options.geminicli) tools.push("geminicli");
5506
+ if (options.junie) tools.push("junie");
5507
+ if (options.qwencode) tools.push("qwencode");
5508
+ if (options.opencode) tools.push("opencode");
5509
+ }
5510
+ if (tools.length === 0) {
5511
+ logger.error("\u274C Please specify a tool to import from using --targets <tool>.");
5512
+ logger.info("Example: rulesync import --targets cursor");
4305
5513
  process.exit(1);
4306
5514
  }
4307
5515
  if (tools.length > 1) {
4308
5516
  logger.error(
4309
- "\u274C Only one tool can be specified at a time. Please run the import command separately for each tool."
5517
+ `\u274C Import command only supports a single target.
5518
+ You specified: ${tools.join(", ")}
5519
+
5520
+ Please run the command separately for each tool:`
4310
5521
  );
5522
+ for (const tool2 of tools) {
5523
+ logger.info(` rulesync import --targets ${tool2}`);
5524
+ }
4311
5525
  process.exit(1);
4312
5526
  }
4313
5527
  const tool = tools[0];
4314
5528
  if (!tool) {
4315
- logger.error("Error: No tool specified");
5529
+ logger.error("\u274C Unexpected error: No tool selected");
4316
5530
  process.exit(1);
4317
5531
  }
4318
5532
  logger.log(`Importing configuration files from ${tool}...`);
4319
5533
  try {
4320
5534
  const result = await importConfiguration({
4321
5535
  tool,
5536
+ features: normalizedFeatures,
4322
5537
  verbose: options.verbose ?? false,
4323
5538
  useLegacyLocation: options.legacy ?? false
4324
5539
  });
4325
5540
  if (result.success) {
4326
- logger.success(`Imported ${result.rulesCreated} rule(s) from ${tool}`);
5541
+ logger.success(`\u2705 Imported ${result.rulesCreated} rule(s) from ${tool}`);
4327
5542
  if (result.ignoreFileCreated) {
4328
- logger.success("Created .rulesyncignore file from ignore patterns");
5543
+ logger.success(" Created .rulesyncignore file from ignore patterns");
4329
5544
  }
4330
5545
  if (result.mcpFileCreated) {
4331
- logger.success("Created .rulesync/.mcp.json file from MCP configuration");
5546
+ logger.success(" Created .rulesync/.mcp.json file from MCP configuration");
4332
5547
  }
5548
+ if (result.subagentsCreated) {
5549
+ logger.success(` Created ${result.subagentsCreated} subagent files`);
5550
+ }
5551
+ logger.success(`
5552
+ \u{1F389} Successfully imported from ${tool}`);
4333
5553
  logger.log("You can now run 'rulesync generate' to create tool-specific configurations.");
4334
5554
  } else if (result.errors.length > 0) {
4335
5555
  logger.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
4336
5556
  if (result.errors.length > 1) {
4337
- logger.info("\nDetailed errors:");
5557
+ logger.info(" Detailed errors:");
4338
5558
  for (const error of result.errors) {
4339
- logger.info(` - ${error}`);
5559
+ logger.info(` - ${error}`);
4340
5560
  }
4341
5561
  }
5562
+ logger.error(`
5563
+ \u274C Failed to import from ${tool}.`);
5564
+ process.exit(1);
4342
5565
  }
4343
5566
  } catch (error) {
4344
5567
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -4348,7 +5571,7 @@ async function importCommand(options = {}) {
4348
5571
  }
4349
5572
 
4350
5573
  // src/cli/commands/init.ts
4351
- import { join as join22 } from "path";
5574
+ import { join as join24 } from "path";
4352
5575
  async function initCommand(options = {}) {
4353
5576
  const configResult = await loadConfig();
4354
5577
  const config = configResult.config;
@@ -4356,7 +5579,7 @@ async function initCommand(options = {}) {
4356
5579
  logger.log("Initializing rulesync...");
4357
5580
  await ensureDir(aiRulesDir);
4358
5581
  const useLegacy = options.legacy ?? config.legacy ?? false;
4359
- const rulesDir = useLegacy ? aiRulesDir : join22(aiRulesDir, "rules");
5582
+ const rulesDir = useLegacy ? aiRulesDir : join24(aiRulesDir, "rules");
4360
5583
  if (!useLegacy) {
4361
5584
  await ensureDir(rulesDir);
4362
5585
  }
@@ -4402,7 +5625,7 @@ globs: ["**/*"]
4402
5625
  - Follow single responsibility principle
4403
5626
  `
4404
5627
  };
4405
- const filepath = join22(rulesDir, sampleFile.filename);
5628
+ const filepath = join24(rulesDir, sampleFile.filename);
4406
5629
  if (!await fileExists(filepath)) {
4407
5630
  await writeFileContent(filepath, sampleFile.content);
4408
5631
  logger.success(`Created ${filepath}`);
@@ -4516,11 +5739,11 @@ async function watchCommand() {
4516
5739
  persistent: true
4517
5740
  });
4518
5741
  let isGenerating = false;
4519
- const handleChange = async (path5) => {
5742
+ const handleChange = async (path8) => {
4520
5743
  if (isGenerating) return;
4521
5744
  isGenerating = true;
4522
5745
  logger.log(`
4523
- \u{1F4DD} Detected change in ${path5}`);
5746
+ \u{1F4DD} Detected change in ${path8}`);
4524
5747
  try {
4525
5748
  await generateCommand({ verbose: false });
4526
5749
  logger.success("Regenerated configuration files");
@@ -4530,10 +5753,10 @@ async function watchCommand() {
4530
5753
  isGenerating = false;
4531
5754
  }
4532
5755
  };
4533
- watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path5) => {
5756
+ watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path8) => {
4534
5757
  logger.log(`
4535
- \u{1F5D1}\uFE0F Removed ${path5}`);
4536
- handleChange(path5);
5758
+ \u{1F5D1}\uFE0F Removed ${path8}`);
5759
+ handleChange(path8);
4537
5760
  }).on("error", (error) => {
4538
5761
  logger.error("Watcher error:", error);
4539
5762
  });
@@ -4544,47 +5767,179 @@ async function watchCommand() {
4544
5767
  });
4545
5768
  }
4546
5769
 
5770
+ // src/cli/utils/targets-parser.ts
5771
+ function parseTargets(targetsInput) {
5772
+ if (!targetsInput) {
5773
+ return [];
5774
+ }
5775
+ let targetStrings;
5776
+ if (Array.isArray(targetsInput)) {
5777
+ targetStrings = targetsInput;
5778
+ } else {
5779
+ targetStrings = targetsInput.split(",").map((target) => target.trim()).filter((target) => target.length > 0);
5780
+ }
5781
+ const results = [];
5782
+ const errors = [];
5783
+ let hasWildcard = false;
5784
+ for (const targetString of targetStrings) {
5785
+ if (targetString === "*" || targetString === "all") {
5786
+ hasWildcard = true;
5787
+ results.push(...ALL_TOOL_TARGETS);
5788
+ } else if (isValidToolTarget(targetString)) {
5789
+ results.push(targetString);
5790
+ } else {
5791
+ errors.push(targetString);
5792
+ }
5793
+ }
5794
+ if (hasWildcard && targetStrings.length > 1) {
5795
+ throw new Error(
5796
+ "Cannot use '*' (all tools) with specific tool targets. Use either '--targets *' for all tools, or specify individual tools."
5797
+ );
5798
+ }
5799
+ if (errors.length > 0) {
5800
+ const validTargets = ALL_TOOL_TARGETS.join(", ");
5801
+ throw new Error(
5802
+ `Invalid tool targets: ${errors.join(", ")}. Valid targets are: ${validTargets}, *, all`
5803
+ );
5804
+ }
5805
+ return [...new Set(results)];
5806
+ }
5807
+ function isValidToolTarget(target) {
5808
+ return ALL_TOOL_TARGETS.includes(target);
5809
+ }
5810
+ function checkDeprecatedFlags(options) {
5811
+ const deprecatedTools = [];
5812
+ const flagToToolMap = {
5813
+ agentsmd: "agentsmd",
5814
+ amazonqcli: "amazonqcli",
5815
+ augmentcode: "augmentcode",
5816
+ "augmentcode-legacy": "augmentcode-legacy",
5817
+ copilot: "copilot",
5818
+ cursor: "cursor",
5819
+ cline: "cline",
5820
+ codexcli: "codexcli",
5821
+ claudecode: "claudecode",
5822
+ roo: "roo",
5823
+ geminicli: "geminicli",
5824
+ junie: "junie",
5825
+ qwencode: "qwencode",
5826
+ kiro: "kiro",
5827
+ opencode: "opencode",
5828
+ windsurf: "windsurf"
5829
+ };
5830
+ for (const [flag, tool] of Object.entries(flagToToolMap)) {
5831
+ if (options[flag]) {
5832
+ deprecatedTools.push(tool);
5833
+ }
5834
+ }
5835
+ return deprecatedTools;
5836
+ }
5837
+ function getDeprecationWarning(deprecatedTools, command = "generate") {
5838
+ const toolsStr = deprecatedTools.join(",");
5839
+ return [
5840
+ "\u26A0\uFE0F DEPRECATED: Individual tool flags are deprecated and will be removed in a future version.",
5841
+ ` Current: rulesync ${command} ${deprecatedTools.map((t) => `--${t}`).join(" ")}`,
5842
+ ` New: rulesync ${command} --targets ${toolsStr}`,
5843
+ " Please update your scripts to use the new --targets flag."
5844
+ ].join("\n");
5845
+ }
5846
+ function mergeAndDeduplicateTools(targetsTools, deprecatedTools, allFlag) {
5847
+ if (allFlag) {
5848
+ logger.warn(
5849
+ [
5850
+ "\u26A0\uFE0F DEPRECATED: The --all flag is deprecated and will be removed in a future version.",
5851
+ " Current: rulesync generate --all",
5852
+ " New: rulesync generate --targets *",
5853
+ " Please update your scripts to use the new --targets flag."
5854
+ ].join("\n")
5855
+ );
5856
+ return [...ALL_TOOL_TARGETS];
5857
+ }
5858
+ const allTools = [...targetsTools, ...deprecatedTools];
5859
+ return [...new Set(allTools)];
5860
+ }
5861
+
4547
5862
  // src/cli/index.ts
4548
5863
  var program = new Command();
4549
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.65.0");
5864
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.67.0");
4550
5865
  program.command("init").description("Initialize rulesync in current directory").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(initCommand);
4551
5866
  program.command("add <filename>").description("Add a new rule file").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(addCommand);
4552
5867
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
4553
- 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("--qwencode", "Import from Qwen Code (QWEN.md)").option("--opencode", "Import from OpenCode (AGENTS.md)").option("-v, --verbose", "Verbose output").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(importCommand);
4554
- program.command("generate").description("Generate configuration files for AI tools").option("--all", "Generate for all supported 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("--qwencode", "Generate only for Qwen Code").option("--kiro", "Generate only for Kiro IDE").option("--opencode", "Generate only for OpenCode").option("--windsurf", "Generate only for Windsurf").option("--delete", "Delete all existing files in output directories before generating").option(
5868
+ program.command("import").description("Import configurations from AI tools to rulesync format").option("-t, --targets <tool>", "Tool to import from (e.g., 'copilot', 'cursor', 'cline')").option(
5869
+ "--features <features>",
5870
+ `Comma-separated list of features to import (${FEATURE_TYPES.join(",")}) or '*' for all`,
5871
+ (value) => {
5872
+ if (value === "*") return "*";
5873
+ return value.split(",").map((f) => f.trim()).filter(Boolean);
5874
+ }
5875
+ ).option("--agentsmd", "[DEPRECATED] Import from AGENTS.md (use --targets agentsmd)").option("--augmentcode", "[DEPRECATED] Import from AugmentCode (use --targets augmentcode)").option(
5876
+ "--augmentcode-legacy",
5877
+ "[DEPRECATED] Import from AugmentCode legacy format (use --targets augmentcode-legacy)"
5878
+ ).option("--claudecode", "[DEPRECATED] Import from Claude Code (use --targets claudecode)").option("--cursor", "[DEPRECATED] Import from Cursor (use --targets cursor)").option("--copilot", "[DEPRECATED] Import from GitHub Copilot (use --targets copilot)").option("--cline", "[DEPRECATED] Import from Cline (use --targets cline)").option("--roo", "[DEPRECATED] Import from Roo Code (use --targets roo)").option("--geminicli", "[DEPRECATED] Import from Gemini CLI (use --targets geminicli)").option("--junie", "[DEPRECATED] Import from JetBrains Junie (use --targets junie)").option("--qwencode", "[DEPRECATED] Import from Qwen Code (use --targets qwencode)").option("--opencode", "[DEPRECATED] Import from OpenCode (use --targets opencode)").option("-v, --verbose", "Verbose output").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(async (options) => {
5879
+ try {
5880
+ let tools = [];
5881
+ const targetsTools = options.targets ? parseTargets(options.targets) : [];
5882
+ const deprecatedTools = checkDeprecatedFlags(options);
5883
+ if (deprecatedTools.length > 0) {
5884
+ logger.warn(getDeprecationWarning(deprecatedTools, "import"));
5885
+ }
5886
+ tools = mergeAndDeduplicateTools(targetsTools, deprecatedTools, false);
5887
+ const importOptions = {
5888
+ ...tools.length > 0 && { targets: tools },
5889
+ ...options.features && { features: options.features },
5890
+ verbose: options.verbose,
5891
+ legacy: options.legacy
5892
+ };
5893
+ await importCommand(importOptions);
5894
+ } catch (error) {
5895
+ logger.error(error instanceof Error ? error.message : String(error));
5896
+ process.exit(1);
5897
+ }
5898
+ });
5899
+ program.command("generate").description("Generate configuration files for AI tools").option("--all", "[DEPRECATED] Generate for all supported AI tools (use --targets * instead)").option(
5900
+ "-t, --targets <tools>",
5901
+ "Comma-separated list of tools to generate for (e.g., 'copilot,cursor,cline' or '*' for all)"
5902
+ ).option(
5903
+ "--features <features>",
5904
+ `Comma-separated list of features to generate (${FEATURE_TYPES.join(",")}) or '*' for all`,
5905
+ (value) => {
5906
+ if (value === "*") return "*";
5907
+ return value.split(",").map((f) => f.trim()).filter(Boolean);
5908
+ }
5909
+ ).option("--agentsmd", "[DEPRECATED] Generate only for AGENTS.md (use --targets agentsmd)").option(
5910
+ "--amazonqcli",
5911
+ "[DEPRECATED] Generate only for Amazon Q Developer CLI (use --targets amazonqcli)"
5912
+ ).option("--augmentcode", "[DEPRECATED] Generate only for AugmentCode (use --targets augmentcode)").option(
5913
+ "--augmentcode-legacy",
5914
+ "[DEPRECATED] Generate only for AugmentCode legacy format (use --targets augmentcode-legacy)"
5915
+ ).option("--copilot", "[DEPRECATED] Generate only for GitHub Copilot (use --targets copilot)").option("--cursor", "[DEPRECATED] Generate only for Cursor (use --targets cursor)").option("--cline", "[DEPRECATED] Generate only for Cline (use --targets cline)").option("--codexcli", "[DEPRECATED] Generate only for OpenAI Codex CLI (use --targets codexcli)").option("--claudecode", "[DEPRECATED] Generate only for Claude Code (use --targets claudecode)").option("--roo", "[DEPRECATED] Generate only for Roo Code (use --targets roo)").option("--geminicli", "[DEPRECATED] Generate only for Gemini CLI (use --targets geminicli)").option("--junie", "[DEPRECATED] Generate only for JetBrains Junie (use --targets junie)").option("--qwencode", "[DEPRECATED] Generate only for Qwen Code (use --targets qwencode)").option("--kiro", "[DEPRECATED] Generate only for Kiro IDE (use --targets kiro)").option("--opencode", "[DEPRECATED] Generate only for OpenCode (use --targets opencode)").option("--windsurf", "[DEPRECATED] Generate only for Windsurf (use --targets windsurf)").option("--delete", "Delete all existing files in output directories before generating").option(
4555
5916
  "-b, --base-dir <paths>",
4556
5917
  "Base directories to generate files (comma-separated for multiple paths)"
4557
5918
  ).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
4558
- const tools = [];
4559
- if (options.all) {
4560
- tools.push(...ALL_TOOL_TARGETS);
4561
- } else {
4562
- if (options.augmentcode) tools.push("augmentcode");
4563
- if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
4564
- if (options.copilot) tools.push("copilot");
4565
- if (options.cursor) tools.push("cursor");
4566
- if (options.cline) tools.push("cline");
4567
- if (options.codexcli) tools.push("codexcli");
4568
- if (options.claudecode) tools.push("claudecode");
4569
- if (options.roo) tools.push("roo");
4570
- if (options.geminicli) tools.push("geminicli");
4571
- if (options.junie) tools.push("junie");
4572
- if (options.qwencode) tools.push("qwencode");
4573
- if (options.kiro) tools.push("kiro");
4574
- if (options.opencode) tools.push("opencode");
4575
- if (options.windsurf) tools.push("windsurf");
4576
- }
4577
- const generateOptions = {
4578
- verbose: options.verbose,
4579
- tools: tools.length > 0 ? tools : void 0,
4580
- delete: options.delete,
4581
- config: options.config,
4582
- noConfig: options.noConfig
4583
- };
4584
- if (options.baseDir) {
4585
- generateOptions.baseDirs = options.baseDir.split(",").map((dir) => dir.trim()).filter((dir) => dir.length > 0);
5919
+ try {
5920
+ let tools = [];
5921
+ const targetsTools = options.targets ? parseTargets(options.targets) : [];
5922
+ const deprecatedTools = checkDeprecatedFlags(options);
5923
+ if (deprecatedTools.length > 0) {
5924
+ logger.warn(getDeprecationWarning(deprecatedTools, "generate"));
5925
+ }
5926
+ tools = mergeAndDeduplicateTools(targetsTools, deprecatedTools, options.all === true);
5927
+ const generateOptions = {
5928
+ verbose: options.verbose,
5929
+ tools: tools.length > 0 ? tools : void 0,
5930
+ features: options.features,
5931
+ delete: options.delete,
5932
+ config: options.config,
5933
+ noConfig: options.noConfig
5934
+ };
5935
+ if (options.baseDir) {
5936
+ generateOptions.baseDirs = options.baseDir.split(",").map((dir) => dir.trim()).filter((dir) => dir.length > 0);
5937
+ }
5938
+ await generateCommand(generateOptions);
5939
+ } catch (error) {
5940
+ logger.error(error instanceof Error ? error.message : String(error));
5941
+ process.exit(1);
4586
5942
  }
4587
- await generateCommand(generateOptions);
4588
5943
  });
4589
5944
  program.command("validate").description("Validate rulesync configuration").action(validateCommand);
4590
5945
  program.command("status").description("Show current status of rulesync").action(statusCommand);