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.
- package/README.md +86 -47
- package/dist/{amazonqcli-MW7XTVPN.js → amazonqcli-PWXCSRAN.js} +2 -2
- package/dist/{augmentcode-WCZCL7VR.js → augmentcode-4AFYW4BU.js} +2 -2
- package/dist/{chunk-YC2BC7Z2.js → chunk-35VMCHXQ.js} +1 -1
- package/dist/{chunk-6SLEITCQ.js → chunk-3QGD3CH5.js} +1 -1
- package/dist/{chunk-VRWNZTGW.js → chunk-5GKH5TQ4.js} +1 -1
- package/dist/{chunk-4NWMCTN5.js → chunk-7BIZ5Y6F.js} +1 -1
- package/dist/{chunk-LTWEI4PW.js → chunk-7QVQO6MQ.js} +1 -1
- package/dist/{chunk-UGY5ALND.js → chunk-B2HD24KC.js} +1 -1
- package/dist/{chunk-M2AUM37M.js → chunk-CS7AV6JT.js} +1 -0
- package/dist/{chunk-6AXPFPKI.js → chunk-KONQNQY3.js} +1 -1
- package/dist/{chunk-FL5BF6JM.js → chunk-OARJESSZ.js} +1 -1
- package/dist/{chunk-JXOLLTNV.js → chunk-V36ICGOY.js} +1 -1
- package/dist/{chunk-GIAQWZQ4.js → chunk-WCON5BAI.js} +1 -1
- package/dist/{chunk-DM2B7XUB.js → chunk-WFOWHPBC.js} +1 -1
- package/dist/{chunk-N6DASHJL.js → chunk-WYYQXVHC.js} +1 -1
- package/dist/{chunk-I4NVS7GE.js → chunk-YZUDL4GW.js} +1 -1
- package/dist/{chunk-TX2CE4RR.js → chunk-ZMGXHLYP.js} +1 -1
- package/dist/{claudecode-RZSJPPBU.js → claudecode-OC7VHCF6.js} +3 -3
- package/dist/{cline-JTWWBQQ4.js → cline-A4KFSAQE.js} +3 -3
- package/dist/{codexcli-ATMFGRJR.js → codexcli-YP3X7FWB.js} +3 -3
- package/dist/{copilot-H3CLGKDP.js → copilot-3LIMXQ7O.js} +2 -2
- package/dist/{cursor-ZUN5RZU6.js → cursor-QV72CDJC.js} +3 -3
- package/dist/{geminicli-Q5HPIQCU.js → geminicli-XPSJJS65.js} +3 -3
- package/dist/index.cjs +1888 -518
- package/dist/index.js +1866 -511
- package/dist/{junie-JCLVC3MI.js → junie-265XIW43.js} +3 -3
- package/dist/{kiro-CNF6433S.js → kiro-6OHFHN5X.js} +2 -2
- package/dist/{opencode-EBS3CED2.js → opencode-C5QAYVJ5.js} +2 -2
- package/dist/{qwencode-JIT6KW7E.js → qwencode-5RR24UW6.js} +3 -3
- package/dist/{roo-KBTRH4TZ.js → roo-5IXVBUHD.js} +3 -3
- package/dist/{windsurf-ZAAWL6JJ.js → windsurf-DVQUJJJ5.js} +3 -3
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
3
|
-
import "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
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-
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
22
|
-
import "./chunk-
|
|
23
|
-
import "./chunk-
|
|
24
|
-
import "./chunk-
|
|
25
|
-
import "./chunk-
|
|
26
|
-
import "./chunk-
|
|
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-
|
|
33
|
+
} from "./chunk-CS7AV6JT.js";
|
|
34
34
|
|
|
35
35
|
// src/cli/index.ts
|
|
36
36
|
import { Command } from "commander";
|
|
37
37
|
|
|
38
|
-
// src/types/
|
|
38
|
+
// src/types/config-options.ts
|
|
39
39
|
import { z } from "zod/mini";
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
51
|
-
var OutputSchema =
|
|
123
|
+
import { z as z3 } from "zod/mini";
|
|
124
|
+
var OutputSchema = z3.object({
|
|
52
125
|
tool: ToolTargetSchema,
|
|
53
|
-
filepath:
|
|
54
|
-
content:
|
|
126
|
+
filepath: z3.string(),
|
|
127
|
+
content: z3.string()
|
|
55
128
|
});
|
|
56
|
-
var BaseFrontmatterSchema =
|
|
57
|
-
description:
|
|
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
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
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/
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
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:
|
|
2536
|
-
generateDetailContent:
|
|
2537
|
-
generateRootContent:
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
2862
|
-
cursor: async (servers, dir) => (await import("./cursor-
|
|
2863
|
-
cline: async (servers, dir) => (await import("./cline-
|
|
2864
|
-
codexcli: async (servers, dir) => (await import("./codexcli-
|
|
2865
|
-
opencode: async (servers, dir) => (await import("./opencode-
|
|
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-
|
|
2870
|
-
geminicli: async (servers, dir) => (await import("./geminicli-
|
|
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-
|
|
2875
|
-
junie: async (servers, dir) => (await import("./junie-
|
|
2876
|
-
qwencode: async (servers, dir) => (await import("./qwencode-
|
|
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-
|
|
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/
|
|
2902
|
-
|
|
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
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
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
|
-
|
|
2935
|
-
|
|
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
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
3521
|
+
${subagent.content}`;
|
|
3522
|
+
return {
|
|
3523
|
+
filename: `${subagent.filename}.md`,
|
|
3524
|
+
content
|
|
3525
|
+
};
|
|
3526
|
+
});
|
|
2941
3527
|
}
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
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
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
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
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
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
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
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
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
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
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
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
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
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
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
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
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
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
|
-
|
|
3046
|
-
|
|
3047
|
-
logger.
|
|
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
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
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
|
-
}
|
|
3079
|
-
logger.
|
|
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
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
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
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
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
|
|
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
|
|
3198
|
-
import
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
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
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
4266
|
-
|
|
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 =
|
|
5463
|
+
const frontmatter2 = matter3.stringify("", simplifiedFrontmatter);
|
|
4280
5464
|
return frontmatter2 + rule.content;
|
|
4281
5465
|
}
|
|
4282
|
-
const 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
|
-
|
|
4290
|
-
|
|
4291
|
-
if (options.
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
if (
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
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
|
-
|
|
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("
|
|
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(
|
|
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("
|
|
5557
|
+
logger.info(" Detailed errors:");
|
|
4338
5558
|
for (const error of result.errors) {
|
|
4339
|
-
logger.info(`
|
|
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
|
|
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 :
|
|
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 =
|
|
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 (
|
|
5742
|
+
const handleChange = async (path8) => {
|
|
4520
5743
|
if (isGenerating) return;
|
|
4521
5744
|
isGenerating = true;
|
|
4522
5745
|
logger.log(`
|
|
4523
|
-
\u{1F4DD} Detected change in ${
|
|
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", (
|
|
5756
|
+
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path8) => {
|
|
4534
5757
|
logger.log(`
|
|
4535
|
-
\u{1F5D1}\uFE0F Removed ${
|
|
4536
|
-
handleChange(
|
|
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.
|
|
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("
|
|
4554
|
-
|
|
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
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
if (
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
if (options.
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
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);
|