rulesync 0.62.0 → 0.64.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 +15 -3
- package/dist/amazonqcli-WVGYACHI.js +9 -0
- package/dist/{augmentcode-HIZIQG2W.js → augmentcode-DTHPPXWO.js} +2 -2
- package/dist/{chunk-M7NL7G7A.js → chunk-4NAQ5HL4.js} +1 -1
- package/dist/{chunk-GQTMTBX4.js → chunk-6LSN7HSJ.js} +76 -4
- package/dist/chunk-DMTCLQ4T.js +17 -0
- package/dist/{chunk-YTU3SCQO.js → chunk-E2J3UBBK.js} +9 -3
- package/dist/{chunk-LXTA7DBA.js → chunk-EID75W45.js} +1 -1
- package/dist/{chunk-U4PLVMCG.js → chunk-FVPZQEWP.js} +1 -1
- package/dist/{chunk-NETSYSMD.js → chunk-HHJIL3YZ.js} +1 -1
- package/dist/{chunk-UEAYL4NT.js → chunk-JX55DU6Y.js} +1 -1
- package/dist/{chunk-KUGTKMNW.js → chunk-KKWJVA56.js} +5 -2
- package/dist/chunk-LURFNGH4.js +17 -0
- package/dist/{chunk-AUUSMVCT.js → chunk-LYVES5YR.js} +2 -0
- package/dist/{chunk-4PSTOKKD.js → chunk-TBXG53FV.js} +1 -1
- package/dist/{chunk-2CW2KFB3.js → chunk-TQOL7OKY.js} +1 -1
- package/dist/chunk-YPJW7Z5M.js +210 -0
- package/dist/{claudecode-YTEFACCT.js → claudecode-SSYLLUXX.js} +3 -3
- package/dist/{cline-CKNUDEA3.js → cline-5EUGKNZ6.js} +3 -3
- package/dist/{codexcli-7SDGYI7D.js → codexcli-IGM2ADYK.js} +3 -3
- package/dist/{copilot-MOR3HHJX.js → copilot-HSQO7ZCJ.js} +2 -2
- package/dist/{cursor-YJGH7W24.js → cursor-ZB3XNGBK.js} +3 -3
- package/dist/{geminicli-E7KZTZ2G.js → geminicli-FNRKH5GX.js} +3 -3
- package/dist/index.cjs +1144 -694
- package/dist/index.js +815 -609
- package/dist/{junie-5LEQU4BO.js → junie-3YGOSOGF.js} +3 -3
- package/dist/{kiro-YDHXY2MA.js → kiro-B6WZNLY4.js} +2 -2
- package/dist/opencode-SZETJ62M.js +17 -0
- package/dist/{roo-L3QTTIPO.js → roo-KLTWVAKE.js} +3 -2
- package/dist/{windsurf-4P6HEUBV.js → windsurf-IZEKUAID.js} +3 -3
- package/package.json +2 -1
- package/dist/chunk-MDYDKNXQ.js +0 -61
- package/dist/chunk-PCATT4UZ.js +0 -78
package/dist/index.cjs
CHANGED
|
@@ -35,12 +35,13 @@ function isToolTarget(target) {
|
|
|
35
35
|
if (!target) return false;
|
|
36
36
|
return ALL_TOOL_TARGETS.some((validTarget) => validTarget === target);
|
|
37
37
|
}
|
|
38
|
-
var
|
|
38
|
+
var import_mini2, ALL_TOOL_TARGETS, ToolTargetSchema, ToolTargetsSchema, WildcardTargetSchema, RulesyncTargetsSchema;
|
|
39
39
|
var init_tool_targets = __esm({
|
|
40
40
|
"src/types/tool-targets.ts"() {
|
|
41
41
|
"use strict";
|
|
42
|
-
|
|
42
|
+
import_mini2 = require("zod/mini");
|
|
43
43
|
ALL_TOOL_TARGETS = [
|
|
44
|
+
"amazonqcli",
|
|
44
45
|
"augmentcode",
|
|
45
46
|
"augmentcode-legacy",
|
|
46
47
|
"copilot",
|
|
@@ -48,16 +49,163 @@ var init_tool_targets = __esm({
|
|
|
48
49
|
"cline",
|
|
49
50
|
"claudecode",
|
|
50
51
|
"codexcli",
|
|
52
|
+
"opencode",
|
|
51
53
|
"roo",
|
|
52
54
|
"geminicli",
|
|
53
55
|
"kiro",
|
|
54
56
|
"junie",
|
|
55
57
|
"windsurf"
|
|
56
58
|
];
|
|
57
|
-
ToolTargetSchema =
|
|
58
|
-
ToolTargetsSchema =
|
|
59
|
-
WildcardTargetSchema =
|
|
60
|
-
RulesyncTargetsSchema =
|
|
59
|
+
ToolTargetSchema = import_mini2.z.enum(ALL_TOOL_TARGETS);
|
|
60
|
+
ToolTargetsSchema = import_mini2.z.array(ToolTargetSchema);
|
|
61
|
+
WildcardTargetSchema = import_mini2.z.tuple([import_mini2.z.literal("*")]);
|
|
62
|
+
RulesyncTargetsSchema = import_mini2.z.union([ToolTargetsSchema, WildcardTargetSchema]);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// src/utils/logger.ts
|
|
67
|
+
var import_consola, Logger, logger;
|
|
68
|
+
var init_logger = __esm({
|
|
69
|
+
"src/utils/logger.ts"() {
|
|
70
|
+
"use strict";
|
|
71
|
+
import_consola = require("consola");
|
|
72
|
+
Logger = class {
|
|
73
|
+
_verbose = false;
|
|
74
|
+
console = import_consola.consola.withDefaults({
|
|
75
|
+
tag: "rulesync"
|
|
76
|
+
});
|
|
77
|
+
setVerbose(verbose) {
|
|
78
|
+
this._verbose = verbose;
|
|
79
|
+
}
|
|
80
|
+
get verbose() {
|
|
81
|
+
return this._verbose;
|
|
82
|
+
}
|
|
83
|
+
// Regular log (always shown, regardless of verbose)
|
|
84
|
+
log(message, ...args) {
|
|
85
|
+
this.console.log(message, ...args);
|
|
86
|
+
}
|
|
87
|
+
// Info level (shown only in verbose mode)
|
|
88
|
+
info(message, ...args) {
|
|
89
|
+
if (this._verbose) {
|
|
90
|
+
this.console.info(message, ...args);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Success (always shown)
|
|
94
|
+
success(message, ...args) {
|
|
95
|
+
this.console.success(message, ...args);
|
|
96
|
+
}
|
|
97
|
+
// Warning (always shown)
|
|
98
|
+
warn(message, ...args) {
|
|
99
|
+
this.console.warn(message, ...args);
|
|
100
|
+
}
|
|
101
|
+
// Error (always shown)
|
|
102
|
+
error(message, ...args) {
|
|
103
|
+
this.console.error(message, ...args);
|
|
104
|
+
}
|
|
105
|
+
// Debug level (shown only in verbose mode)
|
|
106
|
+
debug(message, ...args) {
|
|
107
|
+
if (this._verbose) {
|
|
108
|
+
this.console.debug(message, ...args);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
logger = new Logger();
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// src/utils/file.ts
|
|
117
|
+
async function ensureDir(dirPath) {
|
|
118
|
+
try {
|
|
119
|
+
await (0, import_promises2.stat)(dirPath);
|
|
120
|
+
} catch {
|
|
121
|
+
await (0, import_promises2.mkdir)(dirPath, { recursive: true });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function resolvePath(relativePath, baseDir) {
|
|
125
|
+
if (!baseDir) return relativePath;
|
|
126
|
+
const resolved = (0, import_node_path.resolve)(baseDir, relativePath);
|
|
127
|
+
const rel = (0, import_node_path.relative)(baseDir, resolved);
|
|
128
|
+
if (rel.startsWith("..") || (0, import_node_path.resolve)(resolved) !== resolved) {
|
|
129
|
+
throw new Error(`Path traversal detected: ${relativePath}`);
|
|
130
|
+
}
|
|
131
|
+
return resolved;
|
|
132
|
+
}
|
|
133
|
+
async function readFileContent(filepath) {
|
|
134
|
+
return (0, import_promises2.readFile)(filepath, "utf-8");
|
|
135
|
+
}
|
|
136
|
+
async function writeFileContent(filepath, content) {
|
|
137
|
+
await ensureDir((0, import_node_path.dirname)(filepath));
|
|
138
|
+
await (0, import_promises2.writeFile)(filepath, content, "utf-8");
|
|
139
|
+
}
|
|
140
|
+
async function fileExists(filepath) {
|
|
141
|
+
try {
|
|
142
|
+
await (0, import_promises2.stat)(filepath);
|
|
143
|
+
return true;
|
|
144
|
+
} catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function findFiles(dir, extension = ".md") {
|
|
149
|
+
try {
|
|
150
|
+
const files = await (0, import_promises2.readdir)(dir);
|
|
151
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path.join)(dir, file));
|
|
152
|
+
} catch {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async function findRuleFiles(aiRulesDir) {
|
|
157
|
+
const rulesDir = (0, import_node_path.join)(aiRulesDir, "rules");
|
|
158
|
+
const newLocationFiles = await findFiles(rulesDir, ".md");
|
|
159
|
+
const legacyLocationFiles = await findFiles(aiRulesDir, ".md");
|
|
160
|
+
const newLocationBasenames = new Set(
|
|
161
|
+
newLocationFiles.map((file) => file.split("/").pop()?.replace(/\.md$/, ""))
|
|
162
|
+
);
|
|
163
|
+
const filteredLegacyFiles = legacyLocationFiles.filter((file) => {
|
|
164
|
+
const basename6 = file.split("/").pop()?.replace(/\.md$/, "");
|
|
165
|
+
return !newLocationBasenames.has(basename6);
|
|
166
|
+
});
|
|
167
|
+
return [...newLocationFiles, ...filteredLegacyFiles];
|
|
168
|
+
}
|
|
169
|
+
async function removeDirectory(dirPath) {
|
|
170
|
+
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
171
|
+
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
172
|
+
logger.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
if (await fileExists(dirPath)) {
|
|
177
|
+
await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
logger.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function removeFile(filepath) {
|
|
184
|
+
try {
|
|
185
|
+
if (await fileExists(filepath)) {
|
|
186
|
+
await (0, import_promises2.rm)(filepath);
|
|
187
|
+
}
|
|
188
|
+
} catch (error) {
|
|
189
|
+
logger.warn(`Failed to remove file ${filepath}:`, error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async function removeClaudeGeneratedFiles() {
|
|
193
|
+
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
194
|
+
for (const fileOrDir of filesToRemove) {
|
|
195
|
+
if (fileOrDir.endsWith("/memories")) {
|
|
196
|
+
await removeDirectory(fileOrDir);
|
|
197
|
+
} else {
|
|
198
|
+
await removeFile(fileOrDir);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
var import_promises2, import_node_path;
|
|
203
|
+
var init_file = __esm({
|
|
204
|
+
"src/utils/file.ts"() {
|
|
205
|
+
"use strict";
|
|
206
|
+
import_promises2 = require("fs/promises");
|
|
207
|
+
import_node_path = require("path");
|
|
208
|
+
init_logger();
|
|
61
209
|
}
|
|
62
210
|
});
|
|
63
211
|
|
|
@@ -85,6 +233,75 @@ var init_mcp_helpers = __esm({
|
|
|
85
233
|
}
|
|
86
234
|
});
|
|
87
235
|
|
|
236
|
+
// src/generators/mcp/amazonqcli.ts
|
|
237
|
+
var amazonqcli_exports = {};
|
|
238
|
+
__export(amazonqcli_exports, {
|
|
239
|
+
generateAmazonqcliMcp: () => generateAmazonqcliMcp,
|
|
240
|
+
generateAmazonqcliMcpString: () => generateAmazonqcliMcpString
|
|
241
|
+
});
|
|
242
|
+
async function generateAmazonqcliMcp(mcpServers, config, baseDir) {
|
|
243
|
+
const outputs = [];
|
|
244
|
+
const configPaths = [
|
|
245
|
+
".amazonq/mcp.json"
|
|
246
|
+
// Workspace configuration
|
|
247
|
+
// Note: Global configuration is ~/.aws/amazonq/mcp.json but is user-specific
|
|
248
|
+
// According to precautions.md, we should not create user-level files
|
|
249
|
+
];
|
|
250
|
+
for (const configPath of configPaths) {
|
|
251
|
+
const filepath = resolvePath(configPath, baseDir);
|
|
252
|
+
const content = generateAmazonqcliMcpConfig({ mcpServers });
|
|
253
|
+
outputs.push({
|
|
254
|
+
tool: "amazonqcli",
|
|
255
|
+
filepath,
|
|
256
|
+
content
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return outputs;
|
|
260
|
+
}
|
|
261
|
+
function generateAmazonqcliMcpString(config) {
|
|
262
|
+
return generateAmazonqcliMcpConfig(config);
|
|
263
|
+
}
|
|
264
|
+
function generateAmazonqcliMcpConfig(config) {
|
|
265
|
+
const servers = {};
|
|
266
|
+
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
267
|
+
if (!shouldIncludeServer(server, "amazonqcli")) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
const amazonqServer = {};
|
|
271
|
+
if (server.command) {
|
|
272
|
+
amazonqServer.command = server.command;
|
|
273
|
+
if (server.args) {
|
|
274
|
+
amazonqServer.args = server.args;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (server.env) {
|
|
278
|
+
amazonqServer.env = server.env;
|
|
279
|
+
}
|
|
280
|
+
if (server.timeout !== void 0) {
|
|
281
|
+
amazonqServer.timeout = server.timeout;
|
|
282
|
+
}
|
|
283
|
+
if (server.disabled !== void 0) {
|
|
284
|
+
amazonqServer.disabled = server.disabled;
|
|
285
|
+
}
|
|
286
|
+
if (server.alwaysAllow) {
|
|
287
|
+
amazonqServer.autoApprove = server.alwaysAllow;
|
|
288
|
+
}
|
|
289
|
+
servers[serverName] = amazonqServer;
|
|
290
|
+
}
|
|
291
|
+
const finalConfig = {
|
|
292
|
+
mcpServers: servers
|
|
293
|
+
};
|
|
294
|
+
return `${JSON.stringify(finalConfig, null, 2)}
|
|
295
|
+
`;
|
|
296
|
+
}
|
|
297
|
+
var init_amazonqcli = __esm({
|
|
298
|
+
"src/generators/mcp/amazonqcli.ts"() {
|
|
299
|
+
"use strict";
|
|
300
|
+
init_file();
|
|
301
|
+
init_mcp_helpers();
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
88
305
|
// src/generators/mcp/augmentcode.ts
|
|
89
306
|
var augmentcode_exports = {};
|
|
90
307
|
__export(augmentcode_exports, {
|
|
@@ -104,7 +321,10 @@ function generateAugmentcodeMcp(config) {
|
|
|
104
321
|
name: serverName
|
|
105
322
|
};
|
|
106
323
|
if (server.command) {
|
|
107
|
-
|
|
324
|
+
const command = Array.isArray(server.command) ? server.command[0] : server.command;
|
|
325
|
+
if (command) {
|
|
326
|
+
augmentServer.command = command;
|
|
327
|
+
}
|
|
108
328
|
if (server.args) {
|
|
109
329
|
augmentServer.args = server.args;
|
|
110
330
|
}
|
|
@@ -152,7 +372,10 @@ function generateAugmentcodeMcpConfiguration(mcpServers, baseDir = "") {
|
|
|
152
372
|
const { targets: _, ...serverConfig } = server;
|
|
153
373
|
const augmentServer = {};
|
|
154
374
|
if (serverConfig.command) {
|
|
155
|
-
|
|
375
|
+
const command = Array.isArray(serverConfig.command) ? serverConfig.command[0] : serverConfig.command;
|
|
376
|
+
if (command) {
|
|
377
|
+
augmentServer.command = command;
|
|
378
|
+
}
|
|
156
379
|
if (serverConfig.args) {
|
|
157
380
|
augmentServer.args = serverConfig.args;
|
|
158
381
|
}
|
|
@@ -199,6 +422,17 @@ var init_augmentcode = __esm({
|
|
|
199
422
|
}
|
|
200
423
|
});
|
|
201
424
|
|
|
425
|
+
// src/constants/schemas.ts
|
|
426
|
+
var SCHEMA_URLS;
|
|
427
|
+
var init_schemas = __esm({
|
|
428
|
+
"src/constants/schemas.ts"() {
|
|
429
|
+
"use strict";
|
|
430
|
+
SCHEMA_URLS = {
|
|
431
|
+
OPENCODE: "https://opencode.ai/config.json"
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
202
436
|
// src/generators/mcp/shared-factory.ts
|
|
203
437
|
function generateMcpConfig(config, toolConfig) {
|
|
204
438
|
const servers = {};
|
|
@@ -248,7 +482,7 @@ function generateMcpConfigurationFilesFromRegistry(tool, mcpServers, baseDir = "
|
|
|
248
482
|
if (tool === "junie") {
|
|
249
483
|
return generateJunieMcpConfigurationFiles(mcpServers, baseDir);
|
|
250
484
|
}
|
|
251
|
-
const customTools = ["copilot", "augmentcode", "
|
|
485
|
+
const customTools = ["copilot", "augmentcode", "codexcli", "kiro"];
|
|
252
486
|
if (customTools.includes(tool)) {
|
|
253
487
|
throw new Error(
|
|
254
488
|
`Tool ${tool} uses custom configuration logic - use its specific generator function instead`
|
|
@@ -297,6 +531,7 @@ var serverTransforms, configWrappers, MCP_GENERATOR_REGISTRY, cursorMcpGenerator
|
|
|
297
531
|
var init_shared_factory = __esm({
|
|
298
532
|
"src/generators/mcp/shared-factory.ts"() {
|
|
299
533
|
"use strict";
|
|
534
|
+
init_schemas();
|
|
300
535
|
init_mcp_helpers();
|
|
301
536
|
serverTransforms = {
|
|
302
537
|
/**
|
|
@@ -307,7 +542,8 @@ var init_shared_factory = __esm({
|
|
|
307
542
|
if (server.command) {
|
|
308
543
|
result.command = server.command;
|
|
309
544
|
if (server.args) result.args = server.args;
|
|
310
|
-
}
|
|
545
|
+
}
|
|
546
|
+
if (server.url || server.httpUrl) {
|
|
311
547
|
const url = server.httpUrl || server.url;
|
|
312
548
|
if (url) result.url = url;
|
|
313
549
|
}
|
|
@@ -316,6 +552,25 @@ var init_shared_factory = __esm({
|
|
|
316
552
|
}
|
|
317
553
|
return result;
|
|
318
554
|
},
|
|
555
|
+
/**
|
|
556
|
+
* Roo-specific server transformation (preserves httpUrl, transport, type, etc.)
|
|
557
|
+
*/
|
|
558
|
+
roo: (server) => {
|
|
559
|
+
const result = serverTransforms.extended(server);
|
|
560
|
+
if (server.httpUrl) {
|
|
561
|
+
if (!server.url) {
|
|
562
|
+
result.httpUrl = server.httpUrl;
|
|
563
|
+
delete result.url;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
if (server.transport) {
|
|
567
|
+
result.transport = server.transport;
|
|
568
|
+
}
|
|
569
|
+
if (server.type) {
|
|
570
|
+
result.type = server.type;
|
|
571
|
+
}
|
|
572
|
+
return result;
|
|
573
|
+
},
|
|
319
574
|
/**
|
|
320
575
|
* Extended server transformation (includes disabled, alwaysAllow, etc.)
|
|
321
576
|
*/
|
|
@@ -367,6 +622,12 @@ var init_shared_factory = __esm({
|
|
|
367
622
|
})
|
|
368
623
|
};
|
|
369
624
|
MCP_GENERATOR_REGISTRY = {
|
|
625
|
+
roo: {
|
|
626
|
+
target: "roo",
|
|
627
|
+
configPaths: [".roo/mcp.json"],
|
|
628
|
+
serverTransform: serverTransforms.roo,
|
|
629
|
+
configWrapper: configWrappers.mcpServers
|
|
630
|
+
},
|
|
370
631
|
claudecode: {
|
|
371
632
|
target: "claudecode",
|
|
372
633
|
configPaths: [".mcp.json"],
|
|
@@ -493,6 +754,48 @@ var init_shared_factory = __esm({
|
|
|
493
754
|
configPaths: [".cline/mcp.json"],
|
|
494
755
|
serverTransform: serverTransforms.extended,
|
|
495
756
|
configWrapper: configWrappers.mcpServers
|
|
757
|
+
},
|
|
758
|
+
geminicli: {
|
|
759
|
+
target: "geminicli",
|
|
760
|
+
configPaths: [".gemini/settings.json"],
|
|
761
|
+
serverTransform: (server) => {
|
|
762
|
+
const { targets: _, ...serverConfig } = server;
|
|
763
|
+
const geminiServer = { ...serverConfig };
|
|
764
|
+
if (server.env) {
|
|
765
|
+
geminiServer.env = server.env;
|
|
766
|
+
}
|
|
767
|
+
return geminiServer;
|
|
768
|
+
},
|
|
769
|
+
configWrapper: configWrappers.mcpServers
|
|
770
|
+
},
|
|
771
|
+
opencode: {
|
|
772
|
+
target: "opencode",
|
|
773
|
+
configPaths: ["opencode.json"],
|
|
774
|
+
serverTransform: (server) => {
|
|
775
|
+
const opencodeServer = {};
|
|
776
|
+
if (server.command) {
|
|
777
|
+
opencodeServer.type = "local";
|
|
778
|
+
opencodeServer.command = Array.isArray(server.command) ? server.command : [server.command];
|
|
779
|
+
if (server.args) opencodeServer.args = server.args;
|
|
780
|
+
if (server.env) opencodeServer.environment = server.env;
|
|
781
|
+
if (server.cwd) opencodeServer.cwd = server.cwd;
|
|
782
|
+
} else if (server.url || server.httpUrl) {
|
|
783
|
+
opencodeServer.type = "remote";
|
|
784
|
+
const url = server.httpUrl || server.url;
|
|
785
|
+
if (url) opencodeServer.url = url;
|
|
786
|
+
if (server.headers) opencodeServer.headers = server.headers;
|
|
787
|
+
}
|
|
788
|
+
if (server.disabled !== void 0) {
|
|
789
|
+
opencodeServer.enabled = !server.disabled;
|
|
790
|
+
} else {
|
|
791
|
+
opencodeServer.enabled = true;
|
|
792
|
+
}
|
|
793
|
+
return opencodeServer;
|
|
794
|
+
},
|
|
795
|
+
configWrapper: (servers) => ({
|
|
796
|
+
$schema: SCHEMA_URLS.OPENCODE,
|
|
797
|
+
mcp: servers
|
|
798
|
+
})
|
|
496
799
|
}
|
|
497
800
|
};
|
|
498
801
|
cursorMcpGenerator = createMcpGenerator("cursor");
|
|
@@ -696,7 +999,10 @@ function generateCopilotMcp(config, target) {
|
|
|
696
999
|
if (!shouldIncludeServer(server, "copilot")) continue;
|
|
697
1000
|
const copilotServer = {};
|
|
698
1001
|
if (server.command) {
|
|
699
|
-
|
|
1002
|
+
const command = Array.isArray(server.command) ? server.command[0] : server.command;
|
|
1003
|
+
if (command) {
|
|
1004
|
+
copilotServer.command = command;
|
|
1005
|
+
}
|
|
700
1006
|
if (server.args) copilotServer.args = server.args;
|
|
701
1007
|
} else if (server.url || server.httpUrl) {
|
|
702
1008
|
const url = server.httpUrl || server.url;
|
|
@@ -786,53 +1092,10 @@ __export(geminicli_exports, {
|
|
|
786
1092
|
generateGeminiCliMcpConfiguration: () => generateGeminiCliMcpConfiguration
|
|
787
1093
|
});
|
|
788
1094
|
function generateGeminiCliMcp(config) {
|
|
789
|
-
return
|
|
790
|
-
target: "geminicli",
|
|
791
|
-
configPaths: [".gemini/settings.json"],
|
|
792
|
-
serverTransform: (server) => {
|
|
793
|
-
const geminiServer = {};
|
|
794
|
-
if (server.command) {
|
|
795
|
-
geminiServer.command = server.command;
|
|
796
|
-
if (server.args) geminiServer.args = server.args;
|
|
797
|
-
} else if (server.url || server.httpUrl) {
|
|
798
|
-
if (server.httpUrl) {
|
|
799
|
-
geminiServer.httpUrl = server.httpUrl;
|
|
800
|
-
} else if (server.url) {
|
|
801
|
-
geminiServer.url = server.url;
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
if (server.env) {
|
|
805
|
-
geminiServer.env = server.env;
|
|
806
|
-
}
|
|
807
|
-
if (server.timeout !== void 0) {
|
|
808
|
-
geminiServer.timeout = server.timeout;
|
|
809
|
-
}
|
|
810
|
-
if (server.trust !== void 0) {
|
|
811
|
-
geminiServer.trust = server.trust;
|
|
812
|
-
}
|
|
813
|
-
return geminiServer;
|
|
814
|
-
},
|
|
815
|
-
configWrapper: configWrappers.mcpServers
|
|
816
|
-
});
|
|
1095
|
+
return generateMcpFromRegistry("geminicli", config);
|
|
817
1096
|
}
|
|
818
1097
|
function generateGeminiCliMcpConfiguration(mcpServers, baseDir = "") {
|
|
819
|
-
return
|
|
820
|
-
mcpServers,
|
|
821
|
-
{
|
|
822
|
-
target: "geminicli",
|
|
823
|
-
configPaths: [".gemini/settings.json"],
|
|
824
|
-
serverTransform: (server) => {
|
|
825
|
-
const { targets: _, ...serverConfig } = server;
|
|
826
|
-
const geminiServer = { ...serverConfig };
|
|
827
|
-
if (server.env) {
|
|
828
|
-
geminiServer.env = server.env;
|
|
829
|
-
}
|
|
830
|
-
return geminiServer;
|
|
831
|
-
},
|
|
832
|
-
configWrapper: configWrappers.mcpServers
|
|
833
|
-
},
|
|
834
|
-
baseDir
|
|
835
|
-
);
|
|
1098
|
+
return generateMcpConfigurationFilesFromRegistry("geminicli", mcpServers, baseDir);
|
|
836
1099
|
}
|
|
837
1100
|
var init_geminicli = __esm({
|
|
838
1101
|
"src/generators/mcp/geminicli.ts"() {
|
|
@@ -956,77 +1219,15 @@ __export(roo_exports, {
|
|
|
956
1219
|
generateRooMcpConfiguration: () => generateRooMcpConfiguration
|
|
957
1220
|
});
|
|
958
1221
|
function generateRooMcp(config) {
|
|
959
|
-
|
|
960
|
-
mcpServers: {}
|
|
961
|
-
};
|
|
962
|
-
for (const [serverName, server] of Object.entries(config.mcpServers)) {
|
|
963
|
-
if (!shouldIncludeServer(server, "roo")) continue;
|
|
964
|
-
const rooServer = {};
|
|
965
|
-
if (server.command) {
|
|
966
|
-
rooServer.command = server.command;
|
|
967
|
-
if (server.args) rooServer.args = server.args;
|
|
968
|
-
} else if (server.url || server.httpUrl) {
|
|
969
|
-
const url = server.httpUrl || server.url;
|
|
970
|
-
if (url) {
|
|
971
|
-
rooServer.url = url;
|
|
972
|
-
}
|
|
973
|
-
if (server.httpUrl || server.transport === "http") {
|
|
974
|
-
rooServer.type = "streamable-http";
|
|
975
|
-
} else if (server.transport === "sse" || server.type === "sse") {
|
|
976
|
-
rooServer.type = "sse";
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
if (server.env) {
|
|
980
|
-
rooServer.env = {};
|
|
981
|
-
for (const [key, value] of Object.entries(server.env)) {
|
|
982
|
-
if (value.startsWith("${env:") && value.endsWith("}")) {
|
|
983
|
-
rooServer.env[key] = value;
|
|
984
|
-
} else {
|
|
985
|
-
rooServer.env[key] = `\${env:${value}}`;
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
if (server.disabled !== void 0) {
|
|
990
|
-
rooServer.disabled = server.disabled;
|
|
991
|
-
}
|
|
992
|
-
if (server.alwaysAllow) {
|
|
993
|
-
rooServer.alwaysAllow = server.alwaysAllow;
|
|
994
|
-
}
|
|
995
|
-
if (server.networkTimeout !== void 0) {
|
|
996
|
-
rooServer.networkTimeout = Math.max(3e4, Math.min(3e5, server.networkTimeout));
|
|
997
|
-
}
|
|
998
|
-
rooConfig.mcpServers[serverName] = rooServer;
|
|
999
|
-
}
|
|
1000
|
-
return JSON.stringify(rooConfig, null, 2);
|
|
1222
|
+
return generateMcpFromRegistry("roo", config);
|
|
1001
1223
|
}
|
|
1002
1224
|
function generateRooMcpConfiguration(mcpServers, baseDir = "") {
|
|
1003
|
-
|
|
1004
|
-
const config = {
|
|
1005
|
-
mcpServers: {}
|
|
1006
|
-
};
|
|
1007
|
-
for (const [serverName, server] of Object.entries(mcpServers)) {
|
|
1008
|
-
if (!shouldIncludeServer(server, "roo")) {
|
|
1009
|
-
continue;
|
|
1010
|
-
}
|
|
1011
|
-
const { targets: _targets, ...serverConfig } = server;
|
|
1012
|
-
const rooServer = { ...serverConfig };
|
|
1013
|
-
if (serverConfig.httpUrl !== void 0 && serverConfig.url !== void 0) {
|
|
1014
|
-
rooServer.url = serverConfig.httpUrl;
|
|
1015
|
-
}
|
|
1016
|
-
config.mcpServers[serverName] = rooServer;
|
|
1017
|
-
}
|
|
1018
|
-
return [
|
|
1019
|
-
{
|
|
1020
|
-
filepath,
|
|
1021
|
-
content: `${JSON.stringify(config, null, 2)}
|
|
1022
|
-
`
|
|
1023
|
-
}
|
|
1024
|
-
];
|
|
1225
|
+
return generateMcpConfigurationFilesFromRegistry("roo", mcpServers, baseDir);
|
|
1025
1226
|
}
|
|
1026
1227
|
var init_roo = __esm({
|
|
1027
1228
|
"src/generators/mcp/roo.ts"() {
|
|
1028
1229
|
"use strict";
|
|
1029
|
-
|
|
1230
|
+
init_shared_factory();
|
|
1030
1231
|
}
|
|
1031
1232
|
});
|
|
1032
1233
|
|
|
@@ -1049,6 +1250,25 @@ var init_windsurf = __esm({
|
|
|
1049
1250
|
}
|
|
1050
1251
|
});
|
|
1051
1252
|
|
|
1253
|
+
// src/generators/mcp/opencode.ts
|
|
1254
|
+
var opencode_exports = {};
|
|
1255
|
+
__export(opencode_exports, {
|
|
1256
|
+
generateOpenCodeMcp: () => generateOpenCodeMcp,
|
|
1257
|
+
generateOpenCodeMcpConfiguration: () => generateOpenCodeMcpConfiguration
|
|
1258
|
+
});
|
|
1259
|
+
function generateOpenCodeMcp(config) {
|
|
1260
|
+
return generateMcpFromRegistry("opencode", config);
|
|
1261
|
+
}
|
|
1262
|
+
function generateOpenCodeMcpConfiguration(mcpServers, baseDir = "") {
|
|
1263
|
+
return generateMcpConfigurationFilesFromRegistry("opencode", mcpServers, baseDir);
|
|
1264
|
+
}
|
|
1265
|
+
var init_opencode = __esm({
|
|
1266
|
+
"src/generators/mcp/opencode.ts"() {
|
|
1267
|
+
"use strict";
|
|
1268
|
+
init_shared_factory();
|
|
1269
|
+
}
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1052
1272
|
// src/cli/index.ts
|
|
1053
1273
|
var import_commander = require("commander");
|
|
1054
1274
|
|
|
@@ -1071,12 +1291,21 @@ var ClaudeSettingsSchema = import_mini.z.looseObject({
|
|
|
1071
1291
|
)
|
|
1072
1292
|
});
|
|
1073
1293
|
|
|
1074
|
-
// src/types/
|
|
1075
|
-
var
|
|
1076
|
-
|
|
1077
|
-
|
|
1294
|
+
// src/types/shared.ts
|
|
1295
|
+
var import_mini3 = require("zod/mini");
|
|
1296
|
+
init_tool_targets();
|
|
1297
|
+
var OutputSchema = import_mini3.z.object({
|
|
1298
|
+
tool: ToolTargetSchema,
|
|
1299
|
+
filepath: import_mini3.z.string(),
|
|
1300
|
+
content: import_mini3.z.string()
|
|
1301
|
+
});
|
|
1302
|
+
var BaseFrontmatterSchema = import_mini3.z.object({
|
|
1303
|
+
description: import_mini3.z.optional(import_mini3.z.string())
|
|
1078
1304
|
});
|
|
1079
1305
|
|
|
1306
|
+
// src/types/commands.ts
|
|
1307
|
+
var CommandFrontmatterSchema = BaseFrontmatterSchema;
|
|
1308
|
+
|
|
1080
1309
|
// src/types/config.ts
|
|
1081
1310
|
var import_mini4 = require("zod/mini");
|
|
1082
1311
|
init_tool_targets();
|
|
@@ -1094,6 +1323,7 @@ var ConfigSchema = import_mini4.z.object({
|
|
|
1094
1323
|
var import_mini5 = require("zod/mini");
|
|
1095
1324
|
init_tool_targets();
|
|
1096
1325
|
var OutputPathsSchema = import_mini5.z.object({
|
|
1326
|
+
amazonqcli: import_mini5.z.optional(import_mini5.z.string()),
|
|
1097
1327
|
augmentcode: import_mini5.z.optional(import_mini5.z.string()),
|
|
1098
1328
|
"augmentcode-legacy": import_mini5.z.optional(import_mini5.z.string()),
|
|
1099
1329
|
copilot: import_mini5.z.optional(import_mini5.z.string()),
|
|
@@ -1101,6 +1331,7 @@ var OutputPathsSchema = import_mini5.z.object({
|
|
|
1101
1331
|
cline: import_mini5.z.optional(import_mini5.z.string()),
|
|
1102
1332
|
claudecode: import_mini5.z.optional(import_mini5.z.string()),
|
|
1103
1333
|
codexcli: import_mini5.z.optional(import_mini5.z.string()),
|
|
1334
|
+
opencode: import_mini5.z.optional(import_mini5.z.string()),
|
|
1104
1335
|
roo: import_mini5.z.optional(import_mini5.z.string()),
|
|
1105
1336
|
geminicli: import_mini5.z.optional(import_mini5.z.string()),
|
|
1106
1337
|
kiro: import_mini5.z.optional(import_mini5.z.string()),
|
|
@@ -1152,7 +1383,7 @@ var import_mini6 = require("zod/mini");
|
|
|
1152
1383
|
init_tool_targets();
|
|
1153
1384
|
var McpTransportTypeSchema = import_mini6.z.enum(["stdio", "sse", "http"]);
|
|
1154
1385
|
var McpServerBaseSchema = import_mini6.z.object({
|
|
1155
|
-
command: import_mini6.z.optional(import_mini6.z.string()),
|
|
1386
|
+
command: import_mini6.z.optional(import_mini6.z.union([import_mini6.z.string(), import_mini6.z.array(import_mini6.z.string())])),
|
|
1156
1387
|
args: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string())),
|
|
1157
1388
|
url: import_mini6.z.optional(import_mini6.z.string()),
|
|
1158
1389
|
httpUrl: import_mini6.z.optional(import_mini6.z.string()),
|
|
@@ -1193,11 +1424,6 @@ var RuleFrontmatterSchema = import_mini7.z.object({
|
|
|
1193
1424
|
windsurfOutputFormat: import_mini7.z.optional(import_mini7.z.enum(["single-file", "directory"])),
|
|
1194
1425
|
tags: import_mini7.z.optional(import_mini7.z.array(import_mini7.z.string()))
|
|
1195
1426
|
});
|
|
1196
|
-
var GeneratedOutputSchema = import_mini7.z.object({
|
|
1197
|
-
tool: ToolTargetSchema,
|
|
1198
|
-
filepath: import_mini7.z.string(),
|
|
1199
|
-
content: import_mini7.z.string()
|
|
1200
|
-
});
|
|
1201
1427
|
var GenerateOptionsSchema = import_mini7.z.object({
|
|
1202
1428
|
targetTools: import_mini7.z.optional(ToolTargetsSchema),
|
|
1203
1429
|
outputDir: import_mini7.z.optional(import_mini7.z.string()),
|
|
@@ -1213,6 +1439,7 @@ function getDefaultConfig() {
|
|
|
1213
1439
|
return {
|
|
1214
1440
|
aiRulesDir: ".rulesync",
|
|
1215
1441
|
outputPaths: {
|
|
1442
|
+
amazonqcli: ".amazonq/rules",
|
|
1216
1443
|
augmentcode: ".",
|
|
1217
1444
|
"augmentcode-legacy": ".",
|
|
1218
1445
|
copilot: ".github/instructions",
|
|
@@ -1220,6 +1447,7 @@ function getDefaultConfig() {
|
|
|
1220
1447
|
cline: ".clinerules",
|
|
1221
1448
|
claudecode: ".",
|
|
1222
1449
|
codexcli: ".",
|
|
1450
|
+
opencode: ".",
|
|
1223
1451
|
roo: ".roo/rules",
|
|
1224
1452
|
geminicli: ".gemini/memories",
|
|
1225
1453
|
kiro: ".kiro/steering",
|
|
@@ -1427,6 +1655,7 @@ function mergeWithCliOptions(config, cliOptions) {
|
|
|
1427
1655
|
}
|
|
1428
1656
|
|
|
1429
1657
|
// src/cli/commands/add.ts
|
|
1658
|
+
init_logger();
|
|
1430
1659
|
function sanitizeFilename(filename) {
|
|
1431
1660
|
return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
|
|
1432
1661
|
}
|
|
@@ -1455,11 +1684,11 @@ async function addCommand(filename, options = {}) {
|
|
|
1455
1684
|
await (0, import_promises.mkdir)(rulesDir, { recursive: true });
|
|
1456
1685
|
const template = generateRuleTemplate(sanitizedFilename);
|
|
1457
1686
|
await (0, import_promises.writeFile)(filePath, template, "utf8");
|
|
1458
|
-
|
|
1459
|
-
|
|
1687
|
+
logger.success(`Created rule file: ${filePath}`);
|
|
1688
|
+
logger.log(`\u{1F4DD} Edit the file to customize your rules.`);
|
|
1460
1689
|
} catch (error) {
|
|
1461
|
-
|
|
1462
|
-
|
|
1690
|
+
logger.error(
|
|
1691
|
+
`Failed to create rule file: ${error instanceof Error ? error.message : String(error)}`
|
|
1463
1692
|
);
|
|
1464
1693
|
process.exit(3);
|
|
1465
1694
|
}
|
|
@@ -1470,6 +1699,7 @@ var import_node_fs = require("fs");
|
|
|
1470
1699
|
var import_node_path2 = __toESM(require("path"), 1);
|
|
1471
1700
|
|
|
1472
1701
|
// src/utils/error.ts
|
|
1702
|
+
init_logger();
|
|
1473
1703
|
function getErrorMessage(error) {
|
|
1474
1704
|
return error instanceof Error ? error.message : String(error);
|
|
1475
1705
|
}
|
|
@@ -1499,134 +1729,11 @@ async function safeAsyncOperation(operation, errorContext) {
|
|
|
1499
1729
|
}
|
|
1500
1730
|
}
|
|
1501
1731
|
|
|
1502
|
-
// src/utils/
|
|
1503
|
-
|
|
1504
|
-
var import_node_path = require("path");
|
|
1505
|
-
async function ensureDir(dirPath) {
|
|
1506
|
-
try {
|
|
1507
|
-
await (0, import_promises2.stat)(dirPath);
|
|
1508
|
-
} catch {
|
|
1509
|
-
await (0, import_promises2.mkdir)(dirPath, { recursive: true });
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
function resolvePath(relativePath, baseDir) {
|
|
1513
|
-
return baseDir ? (0, import_node_path.join)(baseDir, relativePath) : relativePath;
|
|
1514
|
-
}
|
|
1515
|
-
async function readFileContent(filepath) {
|
|
1516
|
-
return (0, import_promises2.readFile)(filepath, "utf-8");
|
|
1517
|
-
}
|
|
1518
|
-
async function writeFileContent(filepath, content) {
|
|
1519
|
-
await ensureDir((0, import_node_path.dirname)(filepath));
|
|
1520
|
-
await (0, import_promises2.writeFile)(filepath, content, "utf-8");
|
|
1521
|
-
}
|
|
1522
|
-
async function fileExists(filepath) {
|
|
1523
|
-
try {
|
|
1524
|
-
await (0, import_promises2.stat)(filepath);
|
|
1525
|
-
return true;
|
|
1526
|
-
} catch {
|
|
1527
|
-
return false;
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
async function findFiles(dir, extension = ".md") {
|
|
1531
|
-
try {
|
|
1532
|
-
const files = await (0, import_promises2.readdir)(dir);
|
|
1533
|
-
return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path.join)(dir, file));
|
|
1534
|
-
} catch {
|
|
1535
|
-
return [];
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
async function findRuleFiles(aiRulesDir) {
|
|
1539
|
-
const rulesDir = (0, import_node_path.join)(aiRulesDir, "rules");
|
|
1540
|
-
const newLocationFiles = await findFiles(rulesDir, ".md");
|
|
1541
|
-
const legacyLocationFiles = await findFiles(aiRulesDir, ".md");
|
|
1542
|
-
const newLocationBasenames = new Set(
|
|
1543
|
-
newLocationFiles.map((file) => file.split("/").pop()?.replace(/\.md$/, ""))
|
|
1544
|
-
);
|
|
1545
|
-
const filteredLegacyFiles = legacyLocationFiles.filter((file) => {
|
|
1546
|
-
const basename6 = file.split("/").pop()?.replace(/\.md$/, "");
|
|
1547
|
-
return !newLocationBasenames.has(basename6);
|
|
1548
|
-
});
|
|
1549
|
-
return [...newLocationFiles, ...filteredLegacyFiles];
|
|
1550
|
-
}
|
|
1551
|
-
async function removeDirectory(dirPath) {
|
|
1552
|
-
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
1553
|
-
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
1554
|
-
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
1555
|
-
return;
|
|
1556
|
-
}
|
|
1557
|
-
try {
|
|
1558
|
-
if (await fileExists(dirPath)) {
|
|
1559
|
-
await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
|
|
1560
|
-
}
|
|
1561
|
-
} catch (error) {
|
|
1562
|
-
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
async function removeFile(filepath) {
|
|
1566
|
-
try {
|
|
1567
|
-
if (await fileExists(filepath)) {
|
|
1568
|
-
await (0, import_promises2.rm)(filepath);
|
|
1569
|
-
}
|
|
1570
|
-
} catch (error) {
|
|
1571
|
-
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
1572
|
-
}
|
|
1573
|
-
}
|
|
1574
|
-
async function removeClaudeGeneratedFiles() {
|
|
1575
|
-
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
1576
|
-
for (const fileOrDir of filesToRemove) {
|
|
1577
|
-
if (fileOrDir.endsWith("/memories")) {
|
|
1578
|
-
await removeDirectory(fileOrDir);
|
|
1579
|
-
} else {
|
|
1580
|
-
await removeFile(fileOrDir);
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
// src/utils/logger.ts
|
|
1586
|
-
var import_consola = require("consola");
|
|
1587
|
-
var Logger = class {
|
|
1588
|
-
_verbose = false;
|
|
1589
|
-
console = import_consola.consola.withDefaults({
|
|
1590
|
-
tag: "rulesync"
|
|
1591
|
-
});
|
|
1592
|
-
setVerbose(verbose) {
|
|
1593
|
-
this._verbose = verbose;
|
|
1594
|
-
}
|
|
1595
|
-
get verbose() {
|
|
1596
|
-
return this._verbose;
|
|
1597
|
-
}
|
|
1598
|
-
// Regular log (always shown, regardless of verbose)
|
|
1599
|
-
log(message, ...args) {
|
|
1600
|
-
this.console.log(message, ...args);
|
|
1601
|
-
}
|
|
1602
|
-
// Info level (shown only in verbose mode)
|
|
1603
|
-
info(message, ...args) {
|
|
1604
|
-
if (this._verbose) {
|
|
1605
|
-
this.console.info(message, ...args);
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
// Success (always shown)
|
|
1609
|
-
success(message, ...args) {
|
|
1610
|
-
this.console.success(message, ...args);
|
|
1611
|
-
}
|
|
1612
|
-
// Warning (always shown)
|
|
1613
|
-
warn(message, ...args) {
|
|
1614
|
-
this.console.warn(message, ...args);
|
|
1615
|
-
}
|
|
1616
|
-
// Error (always shown)
|
|
1617
|
-
error(message, ...args) {
|
|
1618
|
-
this.console.error(message, ...args);
|
|
1619
|
-
}
|
|
1620
|
-
// Debug level (shown only in verbose mode)
|
|
1621
|
-
debug(message, ...args) {
|
|
1622
|
-
if (this._verbose) {
|
|
1623
|
-
this.console.debug(message, ...args);
|
|
1624
|
-
}
|
|
1625
|
-
}
|
|
1626
|
-
};
|
|
1627
|
-
var logger = new Logger();
|
|
1732
|
+
// src/utils/index.ts
|
|
1733
|
+
init_file();
|
|
1628
1734
|
|
|
1629
1735
|
// src/cli/commands/config.ts
|
|
1736
|
+
init_logger();
|
|
1630
1737
|
async function configCommand(options = {}) {
|
|
1631
1738
|
if (options.init) {
|
|
1632
1739
|
await initConfig(options);
|
|
@@ -1837,96 +1944,161 @@ export default config;
|
|
|
1837
1944
|
}
|
|
1838
1945
|
|
|
1839
1946
|
// src/cli/commands/generate.ts
|
|
1840
|
-
var
|
|
1947
|
+
var import_node_path13 = require("path");
|
|
1841
1948
|
|
|
1842
1949
|
// src/core/command-generator.ts
|
|
1843
|
-
var
|
|
1950
|
+
var import_node_path5 = require("path");
|
|
1844
1951
|
|
|
1845
|
-
// src/generators
|
|
1952
|
+
// src/utils/command-generators.ts
|
|
1846
1953
|
var import_node_path3 = require("path");
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1954
|
+
function generateYamlFrontmatter(command, options) {
|
|
1955
|
+
const frontmatterLines = ["---"];
|
|
1956
|
+
if (options?.includeDescription && command.frontmatter.description) {
|
|
1957
|
+
frontmatterLines.push(`description: ${command.frontmatter.description}`);
|
|
1958
|
+
}
|
|
1959
|
+
if (options?.additionalFields) {
|
|
1960
|
+
for (const field of options.additionalFields) {
|
|
1961
|
+
frontmatterLines.push(`${field.key}: ${field.value}`);
|
|
1853
1962
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1963
|
+
}
|
|
1964
|
+
frontmatterLines.push("---");
|
|
1965
|
+
return frontmatterLines;
|
|
1966
|
+
}
|
|
1967
|
+
function buildCommandContent(command, frontmatterOptions) {
|
|
1968
|
+
const frontmatter = generateYamlFrontmatter(command, frontmatterOptions);
|
|
1969
|
+
return `${frontmatter.join("\n")}
|
|
1856
1970
|
|
|
1857
1971
|
${command.content.trim()}
|
|
1858
1972
|
`;
|
|
1973
|
+
}
|
|
1974
|
+
function getFlattenedCommandPath(filename, baseDir, subdir) {
|
|
1975
|
+
const flattenedName = filename.replace(/\//g, "-");
|
|
1976
|
+
return (0, import_node_path3.join)(baseDir, subdir, `${flattenedName}.md`);
|
|
1977
|
+
}
|
|
1978
|
+
function getHierarchicalCommandPath(filename, baseDir, subdir, extension = "md") {
|
|
1979
|
+
const nameWithoutExt = filename.replace(/\.[^/.]+$/, "");
|
|
1980
|
+
const fileWithExt = `${nameWithoutExt}.${extension}`;
|
|
1981
|
+
return (0, import_node_path3.join)(baseDir, subdir, fileWithExt);
|
|
1982
|
+
}
|
|
1983
|
+
function escapeTomlString(str) {
|
|
1984
|
+
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
|
|
1985
|
+
}
|
|
1986
|
+
var syntaxConverters = {
|
|
1987
|
+
/**
|
|
1988
|
+
* Convert Claude Code syntax to Gemini CLI syntax
|
|
1989
|
+
*/
|
|
1990
|
+
toGeminiCli(content) {
|
|
1991
|
+
let converted = content;
|
|
1992
|
+
converted = converted.replace(/\$ARGUMENTS/g, "{{args}}");
|
|
1993
|
+
converted = converted.replace(/!`([^`]+)`/g, "!{$1}");
|
|
1994
|
+
return converted.trim();
|
|
1995
|
+
},
|
|
1996
|
+
/**
|
|
1997
|
+
* Convert to Roo Code syntax (currently identical to Claude Code)
|
|
1998
|
+
*/
|
|
1999
|
+
toRooCode(content) {
|
|
2000
|
+
return content.trim();
|
|
2001
|
+
}
|
|
2002
|
+
};
|
|
2003
|
+
|
|
2004
|
+
// src/generators/commands/base.ts
|
|
2005
|
+
var BaseCommandGenerator = class {
|
|
2006
|
+
/**
|
|
2007
|
+
* Generate command output for the specified tool
|
|
2008
|
+
*/
|
|
2009
|
+
generate(command, outputDir) {
|
|
2010
|
+
const filepath = this.getOutputPath(command.filename, outputDir);
|
|
2011
|
+
const content = this.processContent(command);
|
|
1859
2012
|
return {
|
|
1860
|
-
tool:
|
|
2013
|
+
tool: this.getToolName(),
|
|
1861
2014
|
filepath,
|
|
1862
2015
|
content
|
|
1863
2016
|
};
|
|
1864
2017
|
}
|
|
2018
|
+
/**
|
|
2019
|
+
* Get the output path for the command file
|
|
2020
|
+
* Override this method if custom path logic is needed
|
|
2021
|
+
*/
|
|
1865
2022
|
getOutputPath(filename, baseDir) {
|
|
1866
|
-
|
|
1867
|
-
|
|
2023
|
+
if (this.supportsHierarchy()) {
|
|
2024
|
+
return getHierarchicalCommandPath(
|
|
2025
|
+
filename,
|
|
2026
|
+
baseDir,
|
|
2027
|
+
this.getCommandsDirectory(),
|
|
2028
|
+
this.getFileExtension()
|
|
2029
|
+
);
|
|
2030
|
+
} else {
|
|
2031
|
+
return getFlattenedCommandPath(filename, baseDir, this.getCommandsDirectory());
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Whether this tool supports hierarchical directory structure
|
|
2036
|
+
* Override to return true for tools that support nested commands
|
|
2037
|
+
*/
|
|
2038
|
+
supportsHierarchy() {
|
|
2039
|
+
return false;
|
|
2040
|
+
}
|
|
2041
|
+
/**
|
|
2042
|
+
* Get file extension for the target tool
|
|
2043
|
+
* Override if tool uses different extension than .md
|
|
2044
|
+
*/
|
|
2045
|
+
getFileExtension() {
|
|
2046
|
+
return "md";
|
|
2047
|
+
}
|
|
2048
|
+
};
|
|
2049
|
+
|
|
2050
|
+
// src/generators/commands/claudecode.ts
|
|
2051
|
+
var ClaudeCodeCommandGenerator = class extends BaseCommandGenerator {
|
|
2052
|
+
getToolName() {
|
|
2053
|
+
return "claudecode";
|
|
2054
|
+
}
|
|
2055
|
+
getCommandsDirectory() {
|
|
2056
|
+
return ".claude/commands";
|
|
2057
|
+
}
|
|
2058
|
+
processContent(command) {
|
|
2059
|
+
return buildCommandContent(command, { includeDescription: true });
|
|
1868
2060
|
}
|
|
2061
|
+
// Uses flattened structure by default (supportsHierarchy returns false)
|
|
1869
2062
|
};
|
|
1870
2063
|
|
|
1871
2064
|
// src/generators/commands/geminicli.ts
|
|
1872
|
-
var
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
2065
|
+
var GeminiCliCommandGenerator = class extends BaseCommandGenerator {
|
|
2066
|
+
getToolName() {
|
|
2067
|
+
return "geminicli";
|
|
2068
|
+
}
|
|
2069
|
+
getCommandsDirectory() {
|
|
2070
|
+
return ".gemini/commands";
|
|
2071
|
+
}
|
|
2072
|
+
processContent(command) {
|
|
2073
|
+
const convertedContent = syntaxConverters.toGeminiCli(command.content);
|
|
1877
2074
|
const tomlLines = [];
|
|
1878
2075
|
if (command.frontmatter.description) {
|
|
1879
|
-
tomlLines.push(`description = "${
|
|
2076
|
+
tomlLines.push(`description = "${escapeTomlString(command.frontmatter.description)}"`);
|
|
1880
2077
|
tomlLines.push("");
|
|
1881
2078
|
}
|
|
1882
2079
|
tomlLines.push(`prompt = """${convertedContent}"""`);
|
|
1883
|
-
|
|
1884
|
-
return {
|
|
1885
|
-
tool: "geminicli",
|
|
1886
|
-
filepath,
|
|
1887
|
-
content
|
|
1888
|
-
};
|
|
1889
|
-
}
|
|
1890
|
-
getOutputPath(filename, baseDir) {
|
|
1891
|
-
const tomlFilename = filename.replace(/\.md$/, ".toml");
|
|
1892
|
-
const filenameWithExt = tomlFilename.endsWith(".toml") ? tomlFilename : `${tomlFilename}.toml`;
|
|
1893
|
-
return (0, import_node_path4.join)(baseDir, ".gemini", "commands", filenameWithExt);
|
|
2080
|
+
return tomlLines.join("\n") + "\n";
|
|
1894
2081
|
}
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
converted = converted.replace(/\$ARGUMENTS/g, "{{args}}");
|
|
1898
|
-
converted = converted.replace(/!`([^`]+)`/g, "!{$1}");
|
|
1899
|
-
return converted.trim();
|
|
2082
|
+
supportsHierarchy() {
|
|
2083
|
+
return true;
|
|
1900
2084
|
}
|
|
1901
|
-
|
|
1902
|
-
return
|
|
2085
|
+
getFileExtension() {
|
|
2086
|
+
return "toml";
|
|
1903
2087
|
}
|
|
1904
2088
|
};
|
|
1905
2089
|
|
|
1906
2090
|
// src/generators/commands/roo.ts
|
|
1907
|
-
var
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
const filepath = this.getOutputPath(command.filename, outputDir);
|
|
1911
|
-
const frontmatter = ["---"];
|
|
1912
|
-
if (command.frontmatter.description) {
|
|
1913
|
-
frontmatter.push(`description: ${command.frontmatter.description}`);
|
|
1914
|
-
}
|
|
1915
|
-
frontmatter.push("---");
|
|
1916
|
-
const content = `${frontmatter.join("\n")}
|
|
1917
|
-
|
|
1918
|
-
${command.content.trim()}
|
|
1919
|
-
`;
|
|
1920
|
-
return {
|
|
1921
|
-
tool: "roo",
|
|
1922
|
-
filepath,
|
|
1923
|
-
content
|
|
1924
|
-
};
|
|
2091
|
+
var RooCommandGenerator = class extends BaseCommandGenerator {
|
|
2092
|
+
getToolName() {
|
|
2093
|
+
return "roo";
|
|
1925
2094
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
2095
|
+
getCommandsDirectory() {
|
|
2096
|
+
return ".roo/commands";
|
|
2097
|
+
}
|
|
2098
|
+
processContent(command) {
|
|
2099
|
+
return buildCommandContent(command, { includeDescription: true });
|
|
1929
2100
|
}
|
|
2101
|
+
// Uses flattened structure by default (supportsHierarchy returns false)
|
|
1930
2102
|
};
|
|
1931
2103
|
|
|
1932
2104
|
// src/generators/commands/index.ts
|
|
@@ -1939,9 +2111,33 @@ function getCommandGenerator(tool) {
|
|
|
1939
2111
|
return commandGenerators[tool];
|
|
1940
2112
|
}
|
|
1941
2113
|
|
|
2114
|
+
// src/core/command-generator.ts
|
|
2115
|
+
init_file();
|
|
2116
|
+
init_logger();
|
|
2117
|
+
|
|
1942
2118
|
// src/core/command-parser.ts
|
|
1943
|
-
var
|
|
2119
|
+
var import_node_path4 = require("path");
|
|
2120
|
+
|
|
2121
|
+
// src/utils/frontmatter.ts
|
|
1944
2122
|
var import_gray_matter = __toESM(require("gray-matter"), 1);
|
|
2123
|
+
function parseFrontmatter(content, options) {
|
|
2124
|
+
const parsed = (0, import_gray_matter.default)(content, options?.matterOptions);
|
|
2125
|
+
return {
|
|
2126
|
+
content: parsed.content.trim(),
|
|
2127
|
+
data: parsed.data || {}
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
function extractArrayField(data, key, defaultValue = []) {
|
|
2131
|
+
const value = data[key];
|
|
2132
|
+
return Array.isArray(value) ? value : defaultValue;
|
|
2133
|
+
}
|
|
2134
|
+
function extractStringField(data, key, defaultValue) {
|
|
2135
|
+
const value = data[key];
|
|
2136
|
+
return typeof value === "string" ? value : defaultValue;
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
// src/core/command-parser.ts
|
|
2140
|
+
init_logger();
|
|
1945
2141
|
async function parseCommandsFromDirectory(commandsDir) {
|
|
1946
2142
|
const commandFiles = await findFiles(commandsDir, ".md");
|
|
1947
2143
|
const commands = [];
|
|
@@ -1956,17 +2152,17 @@ async function parseCommandsFromDirectory(commandsDir) {
|
|
|
1956
2152
|
}
|
|
1957
2153
|
}
|
|
1958
2154
|
if (errors.length > 0) {
|
|
1959
|
-
|
|
2155
|
+
logger.warn(`Command parsing errors:
|
|
1960
2156
|
${errors.join("\n")}`);
|
|
1961
2157
|
}
|
|
1962
2158
|
return commands;
|
|
1963
2159
|
}
|
|
1964
2160
|
async function parseCommandFile(filepath) {
|
|
1965
2161
|
const content = await readFileContent(filepath);
|
|
1966
|
-
const parsed = (
|
|
2162
|
+
const parsed = parseFrontmatter(content);
|
|
1967
2163
|
try {
|
|
1968
2164
|
const validatedData = CommandFrontmatterSchema.parse(parsed.data);
|
|
1969
|
-
const filename = (0,
|
|
2165
|
+
const filename = (0, import_node_path4.basename)(filepath, ".md");
|
|
1970
2166
|
return {
|
|
1971
2167
|
frontmatter: {
|
|
1972
2168
|
description: validatedData.description
|
|
@@ -1984,7 +2180,7 @@ async function parseCommandFile(filepath) {
|
|
|
1984
2180
|
|
|
1985
2181
|
// src/core/command-generator.ts
|
|
1986
2182
|
async function generateCommands(projectRoot, baseDir, targets) {
|
|
1987
|
-
const commandsDir = (0,
|
|
2183
|
+
const commandsDir = (0, import_node_path5.join)(projectRoot, ".rulesync", "commands");
|
|
1988
2184
|
if (!await fileExists(commandsDir)) {
|
|
1989
2185
|
return [];
|
|
1990
2186
|
}
|
|
@@ -2008,8 +2204,8 @@ async function generateCommands(projectRoot, baseDir, targets) {
|
|
|
2008
2204
|
outputs.push(output);
|
|
2009
2205
|
} catch (error) {
|
|
2010
2206
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2011
|
-
|
|
2012
|
-
|
|
2207
|
+
logger.error(
|
|
2208
|
+
`Failed to generate ${target} command for ${command.filename}: ${errorMessage}`
|
|
2013
2209
|
);
|
|
2014
2210
|
}
|
|
2015
2211
|
}
|
|
@@ -2018,7 +2214,7 @@ async function generateCommands(projectRoot, baseDir, targets) {
|
|
|
2018
2214
|
}
|
|
2019
2215
|
|
|
2020
2216
|
// src/generators/ignore/shared-factory.ts
|
|
2021
|
-
var
|
|
2217
|
+
var import_node_path6 = require("path");
|
|
2022
2218
|
|
|
2023
2219
|
// src/generators/ignore/shared-helpers.ts
|
|
2024
2220
|
function extractIgnorePatternsFromRules(rules) {
|
|
@@ -2141,7 +2337,7 @@ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
|
|
|
2141
2337
|
const outputs = [];
|
|
2142
2338
|
const content = generateIgnoreContent(rules, ignoreConfig);
|
|
2143
2339
|
const outputPath = baseDir || process.cwd();
|
|
2144
|
-
const filepath = (0,
|
|
2340
|
+
const filepath = (0, import_node_path6.join)(outputPath, ignoreConfig.filename);
|
|
2145
2341
|
outputs.push({
|
|
2146
2342
|
tool: ignoreConfig.tool,
|
|
2147
2343
|
filepath,
|
|
@@ -2728,21 +2924,21 @@ function generateWindsurfIgnore(rules, config, baseDir) {
|
|
|
2728
2924
|
return generateIgnoreFile(rules, config, ignoreConfigs.windsurf, baseDir);
|
|
2729
2925
|
}
|
|
2730
2926
|
|
|
2731
|
-
// src/generators/rules/augmentcode.ts
|
|
2732
|
-
var import_node_path11 = require("path");
|
|
2733
|
-
|
|
2734
2927
|
// src/generators/rules/shared-helpers.ts
|
|
2735
|
-
var
|
|
2928
|
+
var import_node_path8 = require("path");
|
|
2929
|
+
init_file();
|
|
2736
2930
|
|
|
2737
2931
|
// src/utils/ignore.ts
|
|
2738
|
-
var
|
|
2932
|
+
var import_node_path7 = require("path");
|
|
2739
2933
|
var import_micromatch = __toESM(require("micromatch"), 1);
|
|
2934
|
+
init_file();
|
|
2935
|
+
init_logger();
|
|
2740
2936
|
var cachedIgnorePatterns = null;
|
|
2741
2937
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
2742
2938
|
if (cachedIgnorePatterns) {
|
|
2743
2939
|
return cachedIgnorePatterns;
|
|
2744
2940
|
}
|
|
2745
|
-
const ignorePath = (0,
|
|
2941
|
+
const ignorePath = (0, import_node_path7.join)(baseDir, ".rulesyncignore");
|
|
2746
2942
|
if (!await fileExists(ignorePath)) {
|
|
2747
2943
|
cachedIgnorePatterns = { patterns: [] };
|
|
2748
2944
|
return cachedIgnorePatterns;
|
|
@@ -2753,7 +2949,7 @@ async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
|
2753
2949
|
cachedIgnorePatterns = { patterns };
|
|
2754
2950
|
return cachedIgnorePatterns;
|
|
2755
2951
|
} catch (error) {
|
|
2756
|
-
|
|
2952
|
+
logger.warn(`Failed to read .rulesyncignore: ${error}`);
|
|
2757
2953
|
cachedIgnorePatterns = { patterns: [] };
|
|
2758
2954
|
return cachedIgnorePatterns;
|
|
2759
2955
|
}
|
|
@@ -2796,7 +2992,7 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
|
|
|
2796
2992
|
const outputDir = resolveOutputDir(config, tool, baseDir);
|
|
2797
2993
|
outputs.push({
|
|
2798
2994
|
tool,
|
|
2799
|
-
filepath: (0,
|
|
2995
|
+
filepath: (0, import_node_path8.join)(outputDir, relativePath),
|
|
2800
2996
|
content
|
|
2801
2997
|
});
|
|
2802
2998
|
}
|
|
@@ -2805,7 +3001,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
|
2805
3001
|
for (const rule of rules) {
|
|
2806
3002
|
const content = generatorConfig.generateContent(rule);
|
|
2807
3003
|
const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
|
|
2808
|
-
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : (0,
|
|
3004
|
+
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : (0, import_node_path8.join)(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
|
|
2809
3005
|
outputs.push({
|
|
2810
3006
|
tool: generatorConfig.tool,
|
|
2811
3007
|
filepath,
|
|
@@ -2833,7 +3029,7 @@ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
|
2833
3029
|
for (const rule of detailRules) {
|
|
2834
3030
|
const content = generatorConfig.generateDetailContent(rule);
|
|
2835
3031
|
const filepath = resolvePath(
|
|
2836
|
-
(0,
|
|
3032
|
+
(0, import_node_path8.join)(generatorConfig.detailSubDir, `${rule.filename}.md`),
|
|
2837
3033
|
baseDir
|
|
2838
3034
|
);
|
|
2839
3035
|
outputs.push({
|
|
@@ -2887,7 +3083,61 @@ function generateIgnoreFile2(patterns, tool) {
|
|
|
2887
3083
|
return lines.join("\n");
|
|
2888
3084
|
}
|
|
2889
3085
|
|
|
3086
|
+
// src/generators/rules/amazonqcli.ts
|
|
3087
|
+
async function generateAmazonqcliConfig(rules, config, baseDir) {
|
|
3088
|
+
const generatorConfig = {
|
|
3089
|
+
tool: "amazonqcli",
|
|
3090
|
+
fileExtension: ".md",
|
|
3091
|
+
generateContent: generateRuleFile,
|
|
3092
|
+
generateRootContent: generateMainRulesFile,
|
|
3093
|
+
rootFilePath: ".amazonq/rules/main.md",
|
|
3094
|
+
generateDetailContent: generateRuleFile,
|
|
3095
|
+
detailSubDir: ".amazonq/rules"
|
|
3096
|
+
};
|
|
3097
|
+
return generateComplexRules(rules, config, generatorConfig, baseDir);
|
|
3098
|
+
}
|
|
3099
|
+
function generateMainRulesFile(rootRule, detailRules) {
|
|
3100
|
+
const lines = [];
|
|
3101
|
+
if (detailRules.length > 0) {
|
|
3102
|
+
lines.push("# Amazon Q Developer CLI Project Rules");
|
|
3103
|
+
lines.push("");
|
|
3104
|
+
lines.push("This file contains the main project rules. See also:");
|
|
3105
|
+
lines.push("");
|
|
3106
|
+
for (const rule of detailRules) {
|
|
3107
|
+
lines.push(`- ${rule.filename}.md: ${rule.frontmatter.description}`);
|
|
3108
|
+
}
|
|
3109
|
+
lines.push("");
|
|
3110
|
+
}
|
|
3111
|
+
if (rootRule) {
|
|
3112
|
+
if (detailRules.length > 0) {
|
|
3113
|
+
lines.push("## Overview");
|
|
3114
|
+
lines.push("");
|
|
3115
|
+
}
|
|
3116
|
+
lines.push(rootRule.content);
|
|
3117
|
+
lines.push("");
|
|
3118
|
+
} else if (detailRules.length === 0) {
|
|
3119
|
+
lines.push("# Amazon Q Developer CLI Project Rules");
|
|
3120
|
+
lines.push("");
|
|
3121
|
+
lines.push("This file contains project-specific rules and context for Amazon Q Developer CLI.");
|
|
3122
|
+
lines.push("");
|
|
3123
|
+
lines.push("## Development Standards");
|
|
3124
|
+
lines.push("");
|
|
3125
|
+
lines.push("Add your project-specific development standards here.");
|
|
3126
|
+
lines.push("");
|
|
3127
|
+
}
|
|
3128
|
+
return lines.join("\n").trim() + "\n";
|
|
3129
|
+
}
|
|
3130
|
+
function generateRuleFile(rule) {
|
|
3131
|
+
const lines = [];
|
|
3132
|
+
lines.push(`# ${rule.frontmatter.description || rule.filename}`);
|
|
3133
|
+
lines.push("");
|
|
3134
|
+
lines.push(rule.content.trim());
|
|
3135
|
+
lines.push("");
|
|
3136
|
+
return lines.join("\n");
|
|
3137
|
+
}
|
|
3138
|
+
|
|
2890
3139
|
// src/generators/rules/augmentcode.ts
|
|
3140
|
+
var import_node_path9 = require("path");
|
|
2891
3141
|
async function generateAugmentcodeConfig(rules, config, baseDir) {
|
|
2892
3142
|
const outputs = createOutputsArray();
|
|
2893
3143
|
rules.forEach((rule) => {
|
|
@@ -2896,13 +3146,13 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
|
|
|
2896
3146
|
"augmentcode",
|
|
2897
3147
|
config,
|
|
2898
3148
|
baseDir,
|
|
2899
|
-
(0,
|
|
2900
|
-
|
|
3149
|
+
(0, import_node_path9.join)(".augment", "rules", `${rule.filename}.md`),
|
|
3150
|
+
generateRuleFile2(rule)
|
|
2901
3151
|
);
|
|
2902
3152
|
});
|
|
2903
3153
|
return outputs;
|
|
2904
3154
|
}
|
|
2905
|
-
function
|
|
3155
|
+
function generateRuleFile2(rule) {
|
|
2906
3156
|
const lines = [];
|
|
2907
3157
|
lines.push("---");
|
|
2908
3158
|
let ruleType = "manual";
|
|
@@ -2949,7 +3199,9 @@ function generateLegacyGuidelinesFile(allRules) {
|
|
|
2949
3199
|
}
|
|
2950
3200
|
|
|
2951
3201
|
// src/generators/rules/claudecode.ts
|
|
2952
|
-
var
|
|
3202
|
+
var import_node_path10 = require("path");
|
|
3203
|
+
init_file();
|
|
3204
|
+
init_logger();
|
|
2953
3205
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
2954
3206
|
const generatorConfig = {
|
|
2955
3207
|
tool: "claudecode",
|
|
@@ -2961,7 +3213,7 @@ async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
|
2961
3213
|
generateDetailContent: generateMemoryFile,
|
|
2962
3214
|
detailSubDir: ".claude/memories",
|
|
2963
3215
|
updateAdditionalConfig: async (ignorePatterns, baseDir2) => {
|
|
2964
|
-
const settingsPath = resolvePath((0,
|
|
3216
|
+
const settingsPath = resolvePath((0, import_node_path10.join)(".claude", "settings.json"), baseDir2);
|
|
2965
3217
|
await updateClaudeSettings(settingsPath, ignorePatterns);
|
|
2966
3218
|
return [];
|
|
2967
3219
|
}
|
|
@@ -2998,7 +3250,7 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
2998
3250
|
const content = await readFileContent(settingsPath);
|
|
2999
3251
|
rawSettings = JSON.parse(content);
|
|
3000
3252
|
} catch {
|
|
3001
|
-
|
|
3253
|
+
logger.warn(`Failed to parse existing ${settingsPath}, creating new settings`);
|
|
3002
3254
|
rawSettings = {};
|
|
3003
3255
|
}
|
|
3004
3256
|
}
|
|
@@ -3021,11 +3273,11 @@ async function updateClaudeSettings(settingsPath, ignorePatterns) {
|
|
|
3021
3273
|
settings.permissions.deny = Array.from(new Set(filteredDeny));
|
|
3022
3274
|
const jsonContent = JSON.stringify(settings, null, 2);
|
|
3023
3275
|
await writeFileContent(settingsPath, jsonContent);
|
|
3024
|
-
|
|
3276
|
+
logger.success(`Updated Claude Code settings: ${settingsPath}`);
|
|
3025
3277
|
}
|
|
3026
3278
|
|
|
3027
3279
|
// src/generators/rules/generator-registry.ts
|
|
3028
|
-
var
|
|
3280
|
+
var import_node_path11 = require("path");
|
|
3029
3281
|
function determineCursorRuleType(frontmatter) {
|
|
3030
3282
|
if (frontmatter.cursorRuleType) {
|
|
3031
3283
|
return frontmatter.cursorRuleType;
|
|
@@ -3049,6 +3301,22 @@ function determineCursorRuleType(frontmatter) {
|
|
|
3049
3301
|
}
|
|
3050
3302
|
var GENERATOR_REGISTRY = {
|
|
3051
3303
|
// Simple generators - generate one file per rule
|
|
3304
|
+
amazonqcli: {
|
|
3305
|
+
type: "complex",
|
|
3306
|
+
tool: "amazonqcli",
|
|
3307
|
+
fileExtension: ".md",
|
|
3308
|
+
// ignoreFileName omitted - Amazon Q CLI doesn't have native ignore file support yet
|
|
3309
|
+
generateContent: (rule) => {
|
|
3310
|
+
const lines = [];
|
|
3311
|
+
if (rule.frontmatter.description) {
|
|
3312
|
+
lines.push(`# ${rule.frontmatter.description}
|
|
3313
|
+
`);
|
|
3314
|
+
}
|
|
3315
|
+
lines.push(rule.content.trim());
|
|
3316
|
+
return lines.join("\n");
|
|
3317
|
+
}
|
|
3318
|
+
// Complex generation handled by existing generator
|
|
3319
|
+
},
|
|
3052
3320
|
cline: {
|
|
3053
3321
|
type: "simple",
|
|
3054
3322
|
tool: "cline",
|
|
@@ -3105,7 +3373,7 @@ var GENERATOR_REGISTRY = {
|
|
|
3105
3373
|
},
|
|
3106
3374
|
pathResolver: (rule, outputDir) => {
|
|
3107
3375
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
3108
|
-
return (0,
|
|
3376
|
+
return (0, import_node_path11.join)(outputDir, `${baseFilename}.instructions.md`);
|
|
3109
3377
|
}
|
|
3110
3378
|
},
|
|
3111
3379
|
cursor: {
|
|
@@ -3145,7 +3413,7 @@ var GENERATOR_REGISTRY = {
|
|
|
3145
3413
|
return lines.join("\n");
|
|
3146
3414
|
},
|
|
3147
3415
|
pathResolver: (rule, outputDir) => {
|
|
3148
|
-
return (0,
|
|
3416
|
+
return (0, import_node_path11.join)(outputDir, `${rule.filename}.mdc`);
|
|
3149
3417
|
}
|
|
3150
3418
|
},
|
|
3151
3419
|
codexcli: {
|
|
@@ -3181,10 +3449,10 @@ var GENERATOR_REGISTRY = {
|
|
|
3181
3449
|
pathResolver: (rule, outputDir) => {
|
|
3182
3450
|
const outputFormat = rule.frontmatter.windsurfOutputFormat || "directory";
|
|
3183
3451
|
if (outputFormat === "single-file") {
|
|
3184
|
-
return (0,
|
|
3452
|
+
return (0, import_node_path11.join)(outputDir, ".windsurf-rules");
|
|
3185
3453
|
} else {
|
|
3186
|
-
const rulesDir = (0,
|
|
3187
|
-
return (0,
|
|
3454
|
+
const rulesDir = (0, import_node_path11.join)(outputDir, ".windsurf", "rules");
|
|
3455
|
+
return (0, import_node_path11.join)(rulesDir, `${rule.filename}.md`);
|
|
3188
3456
|
}
|
|
3189
3457
|
}
|
|
3190
3458
|
},
|
|
@@ -3215,6 +3483,22 @@ var GENERATOR_REGISTRY = {
|
|
|
3215
3483
|
const lines = [];
|
|
3216
3484
|
if (rule.frontmatter.description) {
|
|
3217
3485
|
lines.push(`# ${rule.frontmatter.description}
|
|
3486
|
+
`);
|
|
3487
|
+
}
|
|
3488
|
+
lines.push(rule.content.trim());
|
|
3489
|
+
return lines.join("\n");
|
|
3490
|
+
}
|
|
3491
|
+
// Complex generation handled by existing generator
|
|
3492
|
+
},
|
|
3493
|
+
opencode: {
|
|
3494
|
+
type: "complex",
|
|
3495
|
+
tool: "opencode",
|
|
3496
|
+
fileExtension: ".md",
|
|
3497
|
+
// ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
|
|
3498
|
+
generateContent: (rule) => {
|
|
3499
|
+
const lines = [];
|
|
3500
|
+
if (rule.frontmatter.description) {
|
|
3501
|
+
lines.push(`# ${rule.frontmatter.description}
|
|
3218
3502
|
`);
|
|
3219
3503
|
}
|
|
3220
3504
|
lines.push(rule.content.trim());
|
|
@@ -3257,8 +3541,8 @@ async function generateFromRegistry(tool, rules, config, baseDir) {
|
|
|
3257
3541
|
const enhancedConfig = {
|
|
3258
3542
|
tool: generatorConfig.tool,
|
|
3259
3543
|
fileExtension: generatorConfig.fileExtension,
|
|
3260
|
-
ignoreFileName: generatorConfig.ignoreFileName,
|
|
3261
3544
|
generateContent: generatorConfig.generateContent,
|
|
3545
|
+
...generatorConfig.ignoreFileName && { ignoreFileName: generatorConfig.ignoreFileName },
|
|
3262
3546
|
...generatorConfig.generateRootContent && {
|
|
3263
3547
|
generateRootContent: generatorConfig.generateRootContent
|
|
3264
3548
|
},
|
|
@@ -3289,48 +3573,91 @@ var generateKiroConfig = createSimpleGenerator("kiro");
|
|
|
3289
3573
|
var generateRooConfig = createSimpleGenerator("roo");
|
|
3290
3574
|
|
|
3291
3575
|
// src/generators/rules/codexcli.ts
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
const
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3576
|
+
init_file();
|
|
3577
|
+
|
|
3578
|
+
// src/utils/xml-document-generator.ts
|
|
3579
|
+
var import_fast_xml_parser = require("fast-xml-parser");
|
|
3580
|
+
function generateRootMarkdownWithXmlDocs(rootRule, memoryRules, config) {
|
|
3581
|
+
const lines = [];
|
|
3582
|
+
if (memoryRules.length > 0) {
|
|
3583
|
+
lines.push(
|
|
3584
|
+
"Please also reference the following documents as needed. In this case, `@` stands for the project root directory."
|
|
3585
|
+
);
|
|
3586
|
+
lines.push("");
|
|
3587
|
+
const documentsData = {
|
|
3588
|
+
Documents: {
|
|
3589
|
+
Document: memoryRules.map((rule) => {
|
|
3590
|
+
const relativePath = `@${config.memorySubDir}/${rule.filename}.md`;
|
|
3591
|
+
const document = {
|
|
3592
|
+
Path: relativePath,
|
|
3593
|
+
Description: rule.frontmatter.description
|
|
3594
|
+
};
|
|
3595
|
+
if (rule.frontmatter.globs.length > 0) {
|
|
3596
|
+
document.FilePatterns = rule.frontmatter.globs.join(", ");
|
|
3597
|
+
}
|
|
3598
|
+
return document;
|
|
3599
|
+
})
|
|
3600
|
+
}
|
|
3601
|
+
};
|
|
3602
|
+
const builder = new import_fast_xml_parser.XMLBuilder({
|
|
3603
|
+
format: true,
|
|
3604
|
+
ignoreAttributes: false,
|
|
3605
|
+
suppressEmptyNode: false
|
|
3320
3606
|
});
|
|
3607
|
+
const xmlContent = builder.build(documentsData);
|
|
3608
|
+
lines.push(xmlContent);
|
|
3609
|
+
lines.push("");
|
|
3610
|
+
lines.push("");
|
|
3321
3611
|
}
|
|
3322
|
-
|
|
3612
|
+
if (rootRule) {
|
|
3613
|
+
lines.push(rootRule.content.trim());
|
|
3614
|
+
} else if (memoryRules.length === 0) {
|
|
3615
|
+
lines.push(`# ${config.fallbackTitle}`);
|
|
3616
|
+
lines.push("");
|
|
3617
|
+
lines.push("No configuration rules have been defined yet.");
|
|
3618
|
+
}
|
|
3619
|
+
return lines.join("\n");
|
|
3323
3620
|
}
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3621
|
+
|
|
3622
|
+
// src/generators/rules/codexcli.ts
|
|
3623
|
+
async function generateCodexConfig(rules, config, baseDir) {
|
|
3624
|
+
const outputs = [];
|
|
3625
|
+
const nonEmptyRules = rules.filter((rule) => rule.content.trim().length > 0);
|
|
3626
|
+
if (nonEmptyRules.length > 0) {
|
|
3627
|
+
const generatorConfig = {
|
|
3628
|
+
tool: "codexcli",
|
|
3629
|
+
fileExtension: ".md",
|
|
3630
|
+
ignoreFileName: ".codexignore",
|
|
3631
|
+
generateContent: generateCodexMemoryMarkdown,
|
|
3632
|
+
generateDetailContent: generateCodexMemoryMarkdown,
|
|
3633
|
+
generateRootContent: generateCodexRootMarkdown,
|
|
3634
|
+
rootFilePath: "AGENTS.md",
|
|
3635
|
+
detailSubDir: ".codex/memories"
|
|
3636
|
+
};
|
|
3637
|
+
const ruleOutputs = await generateComplexRules(nonEmptyRules, config, generatorConfig, baseDir);
|
|
3638
|
+
outputs.push(...ruleOutputs);
|
|
3639
|
+
} else {
|
|
3640
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
3641
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
3642
|
+
const ignorePath = resolvePath(".codexignore", baseDir);
|
|
3643
|
+
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, "codexcli");
|
|
3644
|
+
outputs.push({
|
|
3645
|
+
tool: "codexcli",
|
|
3646
|
+
filepath: ignorePath,
|
|
3647
|
+
content: ignoreContent
|
|
3648
|
+
});
|
|
3330
3649
|
}
|
|
3331
|
-
sections.push(content);
|
|
3332
3650
|
}
|
|
3333
|
-
return
|
|
3651
|
+
return outputs;
|
|
3652
|
+
}
|
|
3653
|
+
function generateCodexMemoryMarkdown(rule) {
|
|
3654
|
+
return rule.content.trim();
|
|
3655
|
+
}
|
|
3656
|
+
function generateCodexRootMarkdown(rootRule, memoryRules, _baseDir) {
|
|
3657
|
+
return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
|
|
3658
|
+
memorySubDir: ".codex/memories",
|
|
3659
|
+
fallbackTitle: "OpenAI Codex CLI Configuration"
|
|
3660
|
+
});
|
|
3334
3661
|
}
|
|
3335
3662
|
|
|
3336
3663
|
// src/generators/rules/geminicli.ts
|
|
@@ -3351,28 +3678,10 @@ function generateGeminiMemoryMarkdown(rule) {
|
|
|
3351
3678
|
return rule.content.trim();
|
|
3352
3679
|
}
|
|
3353
3680
|
function generateGeminiRootMarkdown(rootRule, memoryRules, _baseDir) {
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
lines.push("| Document | Description | File Patterns |");
|
|
3359
|
-
lines.push("|----------|-------------|---------------|");
|
|
3360
|
-
for (const rule of memoryRules) {
|
|
3361
|
-
const relativePath = `@.gemini/memories/${rule.filename}.md`;
|
|
3362
|
-
const filePatterns = rule.frontmatter.globs.length > 0 ? rule.frontmatter.globs.join(", ") : "-";
|
|
3363
|
-
lines.push(`| ${relativePath} | ${rule.frontmatter.description} | ${filePatterns} |`);
|
|
3364
|
-
}
|
|
3365
|
-
lines.push("");
|
|
3366
|
-
lines.push("");
|
|
3367
|
-
}
|
|
3368
|
-
if (rootRule) {
|
|
3369
|
-
lines.push(rootRule.content.trim());
|
|
3370
|
-
} else if (memoryRules.length === 0) {
|
|
3371
|
-
lines.push("# Gemini CLI Configuration");
|
|
3372
|
-
lines.push("");
|
|
3373
|
-
lines.push("No configuration rules have been defined yet.");
|
|
3374
|
-
}
|
|
3375
|
-
return lines.join("\n");
|
|
3681
|
+
return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
|
|
3682
|
+
memorySubDir: ".gemini/memories",
|
|
3683
|
+
fallbackTitle: "Gemini CLI Configuration"
|
|
3684
|
+
});
|
|
3376
3685
|
}
|
|
3377
3686
|
|
|
3378
3687
|
// src/generators/rules/junie.ts
|
|
@@ -3402,20 +3711,43 @@ function generateGuidelinesMarkdown(rootRule, detailRules) {
|
|
|
3402
3711
|
return lines.join("\n").trim();
|
|
3403
3712
|
}
|
|
3404
3713
|
|
|
3714
|
+
// src/generators/rules/opencode.ts
|
|
3715
|
+
async function generateOpenCodeConfig(rules, config, baseDir) {
|
|
3716
|
+
const generatorConfig = {
|
|
3717
|
+
tool: "opencode",
|
|
3718
|
+
fileExtension: ".md",
|
|
3719
|
+
// ignoreFileName omitted - OpenCode doesn't use dedicated ignore files
|
|
3720
|
+
generateContent: generateOpenCodeMarkdown,
|
|
3721
|
+
generateDetailContent: generateOpenCodeMarkdown,
|
|
3722
|
+
generateRootContent: generateOpenCodeRootMarkdown,
|
|
3723
|
+
rootFilePath: "AGENTS.md",
|
|
3724
|
+
detailSubDir: ".opencode/memories"
|
|
3725
|
+
};
|
|
3726
|
+
return generateComplexRules(rules, config, generatorConfig, baseDir);
|
|
3727
|
+
}
|
|
3728
|
+
function generateOpenCodeMarkdown(rule) {
|
|
3729
|
+
return rule.content.trim();
|
|
3730
|
+
}
|
|
3731
|
+
function generateOpenCodeRootMarkdown(rootRule, memoryRules, _baseDir) {
|
|
3732
|
+
return generateRootMarkdownWithXmlDocs(rootRule, memoryRules, {
|
|
3733
|
+
memorySubDir: ".opencode/memories",
|
|
3734
|
+
fallbackTitle: "OpenCode Configuration"
|
|
3735
|
+
});
|
|
3736
|
+
}
|
|
3737
|
+
|
|
3405
3738
|
// src/core/generator.ts
|
|
3739
|
+
init_logger();
|
|
3406
3740
|
async function generateConfigurations(rules, config, targetTools, baseDir) {
|
|
3407
3741
|
const outputs = createOutputsArray();
|
|
3408
3742
|
const toolsToGenerate = targetTools || config.defaultTargets;
|
|
3409
3743
|
const rootFiles = rules.filter((rule) => rule.frontmatter.root === true);
|
|
3410
3744
|
if (rootFiles.length === 0) {
|
|
3411
|
-
|
|
3412
|
-
"\u26A0\uFE0F Warning: No files with 'root: true' found. This may result in incomplete configurations."
|
|
3413
|
-
);
|
|
3745
|
+
logger.warn("No files with 'root: true' found. This may result in incomplete configurations.");
|
|
3414
3746
|
}
|
|
3415
3747
|
for (const tool of toolsToGenerate) {
|
|
3416
3748
|
const relevantRules = filterRulesForTool(rules, tool, config);
|
|
3417
3749
|
if (relevantRules.length === 0) {
|
|
3418
|
-
|
|
3750
|
+
logger.warn(`No rules found for tool: ${tool}`);
|
|
3419
3751
|
continue;
|
|
3420
3752
|
}
|
|
3421
3753
|
const toolOutputs = await generateForTool(tool, relevantRules, config, baseDir);
|
|
@@ -3436,6 +3768,8 @@ function filterRulesForTool(rules, tool, config) {
|
|
|
3436
3768
|
}
|
|
3437
3769
|
async function generateForTool(tool, rules, config, baseDir) {
|
|
3438
3770
|
switch (tool) {
|
|
3771
|
+
case "amazonqcli":
|
|
3772
|
+
return await generateAmazonqcliConfig(rules, config, baseDir);
|
|
3439
3773
|
case "augmentcode": {
|
|
3440
3774
|
const augmentRulesOutputs = await generateAugmentcodeConfig(rules, config, baseDir);
|
|
3441
3775
|
const augmentIgnoreOutputs = await generateAugmentCodeIgnoreFiles(rules, config, baseDir);
|
|
@@ -3474,20 +3808,22 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
3474
3808
|
const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
|
|
3475
3809
|
return [...kiroRulesOutputs, ...kiroIgnoreOutputs];
|
|
3476
3810
|
}
|
|
3811
|
+
case "opencode":
|
|
3812
|
+
return generateOpenCodeConfig(rules, config, baseDir);
|
|
3477
3813
|
case "windsurf": {
|
|
3478
3814
|
const windsurfRulesOutputs = await generateWindsurfConfig(rules, config, baseDir);
|
|
3479
3815
|
const windsurfIgnoreOutputs = await generateWindsurfIgnore(rules, config, baseDir);
|
|
3480
3816
|
return [...windsurfRulesOutputs, ...windsurfIgnoreOutputs];
|
|
3481
3817
|
}
|
|
3482
3818
|
default:
|
|
3483
|
-
|
|
3819
|
+
logger.warn(`Unknown tool: ${tool}`);
|
|
3484
3820
|
return null;
|
|
3485
3821
|
}
|
|
3486
3822
|
}
|
|
3487
3823
|
|
|
3488
3824
|
// src/core/parser.ts
|
|
3489
|
-
var
|
|
3490
|
-
|
|
3825
|
+
var import_node_path12 = require("path");
|
|
3826
|
+
init_logger();
|
|
3491
3827
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
3492
3828
|
const ignorePatterns = await loadIgnorePatterns();
|
|
3493
3829
|
const allRuleFiles = await findRuleFiles(aiRulesDir);
|
|
@@ -3495,7 +3831,7 @@ async function parseRulesFromDirectory(aiRulesDir) {
|
|
|
3495
3831
|
const rules = [];
|
|
3496
3832
|
const errors = [];
|
|
3497
3833
|
if (ignorePatterns.patterns.length > 0) {
|
|
3498
|
-
|
|
3834
|
+
logger.info(`Loaded ${ignorePatterns.patterns.length} ignore patterns from .rulesyncignore`);
|
|
3499
3835
|
}
|
|
3500
3836
|
for (const filepath of ruleFiles) {
|
|
3501
3837
|
try {
|
|
@@ -3521,7 +3857,7 @@ ${errors.join("\n")}`);
|
|
|
3521
3857
|
}
|
|
3522
3858
|
async function parseRuleFile(filepath) {
|
|
3523
3859
|
const content = await readFileContent(filepath);
|
|
3524
|
-
const parsed = (
|
|
3860
|
+
const parsed = parseFrontmatter(content);
|
|
3525
3861
|
try {
|
|
3526
3862
|
const validatedData = RuleFrontmatterSchema.parse(parsed.data);
|
|
3527
3863
|
const frontmatter = {
|
|
@@ -3540,7 +3876,7 @@ async function parseRuleFile(filepath) {
|
|
|
3540
3876
|
},
|
|
3541
3877
|
...validatedData.tags !== void 0 && { tags: validatedData.tags }
|
|
3542
3878
|
};
|
|
3543
|
-
const filename = (0,
|
|
3879
|
+
const filename = (0, import_node_path12.basename)(filepath, ".md");
|
|
3544
3880
|
return {
|
|
3545
3881
|
frontmatter,
|
|
3546
3882
|
content: parsed.content,
|
|
@@ -3608,6 +3944,7 @@ async function validateRule(rule) {
|
|
|
3608
3944
|
var path4 = __toESM(require("path"), 1);
|
|
3609
3945
|
|
|
3610
3946
|
// src/generators/mcp/index.ts
|
|
3947
|
+
init_amazonqcli();
|
|
3611
3948
|
init_augmentcode();
|
|
3612
3949
|
init_claudecode();
|
|
3613
3950
|
init_cline();
|
|
@@ -3620,6 +3957,9 @@ init_kiro();
|
|
|
3620
3957
|
init_roo();
|
|
3621
3958
|
init_windsurf();
|
|
3622
3959
|
|
|
3960
|
+
// src/core/mcp-generator.ts
|
|
3961
|
+
init_file();
|
|
3962
|
+
|
|
3623
3963
|
// src/core/mcp-parser.ts
|
|
3624
3964
|
var fs = __toESM(require("fs"), 1);
|
|
3625
3965
|
var path3 = __toESM(require("path"), 1);
|
|
@@ -3651,6 +3991,35 @@ function parseMcpConfig(projectRoot) {
|
|
|
3651
3991
|
async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
|
|
3652
3992
|
const outputs = [];
|
|
3653
3993
|
const toolMap = {
|
|
3994
|
+
amazonqcli: async (servers, dir) => {
|
|
3995
|
+
const config = {
|
|
3996
|
+
aiRulesDir: ".rulesync",
|
|
3997
|
+
outputPaths: {
|
|
3998
|
+
amazonqcli: ".amazonq/rules",
|
|
3999
|
+
augmentcode: ".",
|
|
4000
|
+
"augmentcode-legacy": ".",
|
|
4001
|
+
copilot: ".github/instructions",
|
|
4002
|
+
cursor: ".cursor/rules",
|
|
4003
|
+
cline: ".clinerules",
|
|
4004
|
+
claudecode: ".",
|
|
4005
|
+
codexcli: ".",
|
|
4006
|
+
opencode: ".",
|
|
4007
|
+
roo: ".roo/rules",
|
|
4008
|
+
geminicli: ".gemini/memories",
|
|
4009
|
+
kiro: ".kiro/steering",
|
|
4010
|
+
junie: ".",
|
|
4011
|
+
windsurf: "."
|
|
4012
|
+
},
|
|
4013
|
+
watchEnabled: false,
|
|
4014
|
+
defaultTargets: []
|
|
4015
|
+
};
|
|
4016
|
+
const results = await (await Promise.resolve().then(() => (init_amazonqcli(), amazonqcli_exports))).generateAmazonqcliMcp(
|
|
4017
|
+
servers,
|
|
4018
|
+
config,
|
|
4019
|
+
dir
|
|
4020
|
+
);
|
|
4021
|
+
return results.map((result) => ({ filepath: result.filepath, content: result.content }));
|
|
4022
|
+
},
|
|
3654
4023
|
augmentcode: async (servers, dir) => (await Promise.resolve().then(() => (init_augmentcode(), augmentcode_exports))).generateAugmentcodeMcpConfiguration(
|
|
3655
4024
|
servers,
|
|
3656
4025
|
dir
|
|
@@ -3667,6 +4036,10 @@ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
|
|
|
3667
4036
|
cursor: async (servers, dir) => (await Promise.resolve().then(() => (init_cursor(), cursor_exports))).generateCursorMcpConfiguration(servers, dir),
|
|
3668
4037
|
cline: async (servers, dir) => (await Promise.resolve().then(() => (init_cline(), cline_exports))).generateClineMcpConfiguration(servers, dir),
|
|
3669
4038
|
codexcli: async (servers, dir) => (await Promise.resolve().then(() => (init_codexcli(), codexcli_exports))).generateCodexMcpConfiguration(servers, dir),
|
|
4039
|
+
opencode: async (servers, dir) => (await Promise.resolve().then(() => (init_opencode(), opencode_exports))).generateOpenCodeMcpConfiguration(
|
|
4040
|
+
servers,
|
|
4041
|
+
dir
|
|
4042
|
+
),
|
|
3670
4043
|
roo: async (servers, dir) => (await Promise.resolve().then(() => (init_roo(), roo_exports))).generateRooMcpConfiguration(servers, dir),
|
|
3671
4044
|
geminicli: async (servers, dir) => (await Promise.resolve().then(() => (init_geminicli(), geminicli_exports))).generateGeminiCliMcpConfiguration(
|
|
3672
4045
|
servers,
|
|
@@ -3696,6 +4069,7 @@ async function generateMcpConfigurations(mcpConfig, baseDir, targetTools) {
|
|
|
3696
4069
|
}
|
|
3697
4070
|
|
|
3698
4071
|
// src/cli/commands/generate.ts
|
|
4072
|
+
init_logger();
|
|
3699
4073
|
async function generateCommand(options = {}) {
|
|
3700
4074
|
const configLoaderOptions = {
|
|
3701
4075
|
...options.config !== void 0 && { configPath: options.config },
|
|
@@ -3759,7 +4133,7 @@ async function generateCommand(options = {}) {
|
|
|
3759
4133
|
logger.info("Deleting existing output directories...");
|
|
3760
4134
|
const targetTools = config.defaultTargets;
|
|
3761
4135
|
const deleteTasks = [];
|
|
3762
|
-
const commandsDir = (0,
|
|
4136
|
+
const commandsDir = (0, import_node_path13.join)(config.aiRulesDir, "commands");
|
|
3763
4137
|
const hasCommands = await fileExists(commandsDir);
|
|
3764
4138
|
let hasCommandFiles = false;
|
|
3765
4139
|
if (hasCommands) {
|
|
@@ -3774,12 +4148,12 @@ async function generateCommand(options = {}) {
|
|
|
3774
4148
|
for (const tool of targetTools) {
|
|
3775
4149
|
switch (tool) {
|
|
3776
4150
|
case "augmentcode":
|
|
3777
|
-
deleteTasks.push(removeDirectory((0,
|
|
3778
|
-
deleteTasks.push(removeDirectory((0,
|
|
4151
|
+
deleteTasks.push(removeDirectory((0, import_node_path13.join)(".augment", "rules")));
|
|
4152
|
+
deleteTasks.push(removeDirectory((0, import_node_path13.join)(".augment", "ignore")));
|
|
3779
4153
|
break;
|
|
3780
4154
|
case "augmentcode-legacy":
|
|
3781
4155
|
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
3782
|
-
deleteTasks.push(removeDirectory((0,
|
|
4156
|
+
deleteTasks.push(removeDirectory((0, import_node_path13.join)(".augment", "ignore")));
|
|
3783
4157
|
break;
|
|
3784
4158
|
case "copilot":
|
|
3785
4159
|
deleteTasks.push(removeDirectory(config.outputPaths.copilot));
|
|
@@ -3793,24 +4167,27 @@ async function generateCommand(options = {}) {
|
|
|
3793
4167
|
case "claudecode":
|
|
3794
4168
|
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
3795
4169
|
if (hasCommandFiles) {
|
|
3796
|
-
deleteTasks.push(removeDirectory((0,
|
|
4170
|
+
deleteTasks.push(removeDirectory((0, import_node_path13.join)(".claude", "commands")));
|
|
3797
4171
|
}
|
|
3798
4172
|
break;
|
|
3799
4173
|
case "roo":
|
|
3800
4174
|
deleteTasks.push(removeDirectory(config.outputPaths.roo));
|
|
3801
4175
|
if (hasCommandFiles) {
|
|
3802
|
-
deleteTasks.push(removeDirectory((0,
|
|
4176
|
+
deleteTasks.push(removeDirectory((0, import_node_path13.join)(".roo", "commands")));
|
|
3803
4177
|
}
|
|
3804
4178
|
break;
|
|
3805
4179
|
case "geminicli":
|
|
3806
4180
|
deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
|
|
3807
4181
|
if (hasCommandFiles) {
|
|
3808
|
-
deleteTasks.push(removeDirectory((0,
|
|
4182
|
+
deleteTasks.push(removeDirectory((0, import_node_path13.join)(".gemini", "commands")));
|
|
3809
4183
|
}
|
|
3810
4184
|
break;
|
|
3811
4185
|
case "kiro":
|
|
3812
4186
|
deleteTasks.push(removeDirectory(config.outputPaths.kiro));
|
|
3813
4187
|
break;
|
|
4188
|
+
case "opencode":
|
|
4189
|
+
deleteTasks.push(removeDirectory(config.outputPaths.opencode));
|
|
4190
|
+
break;
|
|
3814
4191
|
case "windsurf":
|
|
3815
4192
|
deleteTasks.push(removeDirectory(config.outputPaths.windsurf));
|
|
3816
4193
|
break;
|
|
@@ -3904,11 +4281,14 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
3904
4281
|
|
|
3905
4282
|
// src/cli/commands/gitignore.ts
|
|
3906
4283
|
var import_node_fs2 = require("fs");
|
|
3907
|
-
var
|
|
4284
|
+
var import_node_path14 = require("path");
|
|
4285
|
+
init_logger();
|
|
3908
4286
|
var gitignoreCommand = async () => {
|
|
3909
|
-
const gitignorePath = (0,
|
|
4287
|
+
const gitignorePath = (0, import_node_path14.join)(process.cwd(), ".gitignore");
|
|
3910
4288
|
const rulesFilesToIgnore = [
|
|
3911
4289
|
"# Generated by rulesync - AI tool configuration files",
|
|
4290
|
+
"**/.amazonq/rules/",
|
|
4291
|
+
"**/.amazonq/mcp.json",
|
|
3912
4292
|
"**/.github/copilot-instructions.md",
|
|
3913
4293
|
"**/.github/instructions/",
|
|
3914
4294
|
"**/.cursor/rules/",
|
|
@@ -3918,7 +4298,7 @@ var gitignoreCommand = async () => {
|
|
|
3918
4298
|
"**/CLAUDE.md",
|
|
3919
4299
|
"**/.claude/memories/",
|
|
3920
4300
|
"**/.claude/commands/",
|
|
3921
|
-
"**/
|
|
4301
|
+
"**/AGENTS.md",
|
|
3922
4302
|
"**/.codexignore",
|
|
3923
4303
|
"**/.roo/rules/",
|
|
3924
4304
|
"**/.rooignore",
|
|
@@ -3934,6 +4314,9 @@ var gitignoreCommand = async () => {
|
|
|
3934
4314
|
"**/.augment-guidelines",
|
|
3935
4315
|
"**/.junie/guidelines.md",
|
|
3936
4316
|
"**/.noai",
|
|
4317
|
+
"**/.opencode/memories/",
|
|
4318
|
+
"**/.opencode/commands/",
|
|
4319
|
+
"**/opencode.json",
|
|
3937
4320
|
"**/.mcp.json",
|
|
3938
4321
|
"!.rulesync/.mcp.json",
|
|
3939
4322
|
"**/.cursor/mcp.json",
|
|
@@ -3954,7 +4337,7 @@ var gitignoreCommand = async () => {
|
|
|
3954
4337
|
}
|
|
3955
4338
|
}
|
|
3956
4339
|
if (linesToAdd.length === 0) {
|
|
3957
|
-
|
|
4340
|
+
logger.success(".gitignore is already up to date");
|
|
3958
4341
|
return;
|
|
3959
4342
|
}
|
|
3960
4343
|
const newContent = gitignoreContent ? `${gitignoreContent.trimEnd()}
|
|
@@ -3963,174 +4346,21 @@ ${linesToAdd.join("\n")}
|
|
|
3963
4346
|
` : `${linesToAdd.join("\n")}
|
|
3964
4347
|
`;
|
|
3965
4348
|
(0, import_node_fs2.writeFileSync)(gitignorePath, newContent);
|
|
3966
|
-
|
|
4349
|
+
logger.success(`Added ${linesToAdd.length} rules to .gitignore:`);
|
|
3967
4350
|
for (const line of linesToAdd) {
|
|
3968
4351
|
if (!line.startsWith("#")) {
|
|
3969
|
-
|
|
4352
|
+
logger.log(` ${line}`);
|
|
3970
4353
|
}
|
|
3971
4354
|
}
|
|
3972
4355
|
};
|
|
3973
4356
|
|
|
3974
4357
|
// src/core/importer.ts
|
|
3975
|
-
var
|
|
3976
|
-
var
|
|
3977
|
-
|
|
3978
|
-
// src/parsers/augmentcode.ts
|
|
3979
|
-
var import_node_path17 = require("path");
|
|
3980
|
-
var import_gray_matter3 = __toESM(require("gray-matter"), 1);
|
|
3981
|
-
|
|
3982
|
-
// src/utils/parser-helpers.ts
|
|
3983
|
-
function createParseResult() {
|
|
3984
|
-
return { rules: [], errors: [] };
|
|
3985
|
-
}
|
|
3986
|
-
function addError(result, error) {
|
|
3987
|
-
result.errors.push(error);
|
|
3988
|
-
}
|
|
3989
|
-
function addRule(result, rule) {
|
|
3990
|
-
if (!result.rules) {
|
|
3991
|
-
result.rules = [];
|
|
3992
|
-
}
|
|
3993
|
-
result.rules.push(rule);
|
|
3994
|
-
}
|
|
3995
|
-
function addRules(result, rules) {
|
|
3996
|
-
if (!result.rules) {
|
|
3997
|
-
result.rules = [];
|
|
3998
|
-
}
|
|
3999
|
-
result.rules.push(...rules);
|
|
4000
|
-
}
|
|
4001
|
-
async function safeReadFile(operation, errorContext) {
|
|
4002
|
-
try {
|
|
4003
|
-
const result = await operation();
|
|
4004
|
-
return createSuccessResult(result);
|
|
4005
|
-
} catch (error) {
|
|
4006
|
-
return createErrorResult(error, errorContext);
|
|
4007
|
-
}
|
|
4008
|
-
}
|
|
4009
|
-
|
|
4010
|
-
// src/parsers/augmentcode.ts
|
|
4011
|
-
async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
|
|
4012
|
-
return parseUnifiedAugmentcode(baseDir, {
|
|
4013
|
-
rulesDir: ".augment/rules",
|
|
4014
|
-
targetName: "augmentcode",
|
|
4015
|
-
filenamePrefix: "augmentcode"
|
|
4016
|
-
});
|
|
4017
|
-
}
|
|
4018
|
-
async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
|
|
4019
|
-
return parseUnifiedAugmentcode(baseDir, {
|
|
4020
|
-
legacyFilePath: ".augment-guidelines",
|
|
4021
|
-
targetName: "augmentcode-legacy",
|
|
4022
|
-
filenamePrefix: "augmentcode-legacy"
|
|
4023
|
-
});
|
|
4024
|
-
}
|
|
4025
|
-
async function parseUnifiedAugmentcode(baseDir, config) {
|
|
4026
|
-
const result = createParseResult();
|
|
4027
|
-
if (config.rulesDir) {
|
|
4028
|
-
const rulesDir = (0, import_node_path17.join)(baseDir, config.rulesDir);
|
|
4029
|
-
if (await fileExists(rulesDir)) {
|
|
4030
|
-
const rulesResult = await parseAugmentRules(rulesDir, config);
|
|
4031
|
-
addRules(result, rulesResult.rules);
|
|
4032
|
-
result.errors.push(...rulesResult.errors);
|
|
4033
|
-
} else {
|
|
4034
|
-
addError(
|
|
4035
|
-
result,
|
|
4036
|
-
`No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
|
|
4037
|
-
);
|
|
4038
|
-
}
|
|
4039
|
-
}
|
|
4040
|
-
if (config.legacyFilePath) {
|
|
4041
|
-
const legacyPath = (0, import_node_path17.join)(baseDir, config.legacyFilePath);
|
|
4042
|
-
if (await fileExists(legacyPath)) {
|
|
4043
|
-
const legacyResult = await parseAugmentGuidelines(legacyPath, config);
|
|
4044
|
-
if (legacyResult.rule) {
|
|
4045
|
-
addRule(result, legacyResult.rule);
|
|
4046
|
-
}
|
|
4047
|
-
result.errors.push(...legacyResult.errors);
|
|
4048
|
-
} else {
|
|
4049
|
-
addError(
|
|
4050
|
-
result,
|
|
4051
|
-
`No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
|
|
4052
|
-
);
|
|
4053
|
-
}
|
|
4054
|
-
}
|
|
4055
|
-
return { rules: result.rules || [], errors: result.errors };
|
|
4056
|
-
}
|
|
4057
|
-
async function parseAugmentRules(rulesDir, config) {
|
|
4058
|
-
const rules = [];
|
|
4059
|
-
const errors = [];
|
|
4060
|
-
try {
|
|
4061
|
-
const { readdir: readdir2 } = await import("fs/promises");
|
|
4062
|
-
const files = await readdir2(rulesDir);
|
|
4063
|
-
for (const file of files) {
|
|
4064
|
-
if (file.endsWith(".md") || file.endsWith(".mdc")) {
|
|
4065
|
-
const filePath = (0, import_node_path17.join)(rulesDir, file);
|
|
4066
|
-
try {
|
|
4067
|
-
const rawContent = await readFileContent(filePath);
|
|
4068
|
-
const parsed = (0, import_gray_matter3.default)(rawContent);
|
|
4069
|
-
const frontmatterData = parsed.data;
|
|
4070
|
-
const ruleType = frontmatterData.type || "manual";
|
|
4071
|
-
const description = frontmatterData.description || "";
|
|
4072
|
-
const tags = Array.isArray(frontmatterData.tags) ? frontmatterData.tags : void 0;
|
|
4073
|
-
const isRoot = ruleType === "always";
|
|
4074
|
-
const filename = (0, import_node_path17.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
|
|
4075
|
-
const frontmatter = {
|
|
4076
|
-
root: isRoot,
|
|
4077
|
-
targets: [config.targetName],
|
|
4078
|
-
description,
|
|
4079
|
-
globs: ["**/*"],
|
|
4080
|
-
// AugmentCode doesn't use specific globs in the same way
|
|
4081
|
-
...tags && { tags }
|
|
4082
|
-
};
|
|
4083
|
-
rules.push({
|
|
4084
|
-
frontmatter,
|
|
4085
|
-
content: parsed.content.trim(),
|
|
4086
|
-
filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
|
|
4087
|
-
filepath: filePath
|
|
4088
|
-
});
|
|
4089
|
-
} catch (error) {
|
|
4090
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4091
|
-
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
4092
|
-
}
|
|
4093
|
-
}
|
|
4094
|
-
}
|
|
4095
|
-
} catch (error) {
|
|
4096
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4097
|
-
errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
|
|
4098
|
-
}
|
|
4099
|
-
return { rules, errors };
|
|
4100
|
-
}
|
|
4101
|
-
async function parseAugmentGuidelines(guidelinesPath, config) {
|
|
4102
|
-
const parseResult = await safeReadFile(
|
|
4103
|
-
async () => {
|
|
4104
|
-
const content = await readFileContent(guidelinesPath);
|
|
4105
|
-
if (content.trim()) {
|
|
4106
|
-
const frontmatter = {
|
|
4107
|
-
root: true,
|
|
4108
|
-
// Legacy guidelines become root rules
|
|
4109
|
-
targets: [config.targetName],
|
|
4110
|
-
description: "Legacy AugmentCode guidelines",
|
|
4111
|
-
globs: ["**/*"]
|
|
4112
|
-
};
|
|
4113
|
-
return {
|
|
4114
|
-
frontmatter,
|
|
4115
|
-
content: content.trim(),
|
|
4116
|
-
filename: `${config.filenamePrefix}-guidelines`,
|
|
4117
|
-
filepath: guidelinesPath
|
|
4118
|
-
};
|
|
4119
|
-
}
|
|
4120
|
-
return null;
|
|
4121
|
-
},
|
|
4122
|
-
`Failed to parse ${config.legacyFilePath || guidelinesPath}`
|
|
4123
|
-
);
|
|
4124
|
-
if (parseResult.success) {
|
|
4125
|
-
return { rule: parseResult.result || null, errors: [] };
|
|
4126
|
-
} else {
|
|
4127
|
-
return { rule: null, errors: [parseResult.error || "Unknown error"] };
|
|
4128
|
-
}
|
|
4129
|
-
}
|
|
4358
|
+
var import_node_path21 = require("path");
|
|
4359
|
+
var import_gray_matter2 = __toESM(require("gray-matter"), 1);
|
|
4130
4360
|
|
|
4131
4361
|
// src/parsers/shared-helpers.ts
|
|
4132
|
-
var
|
|
4133
|
-
|
|
4362
|
+
var import_node_path15 = require("path");
|
|
4363
|
+
init_file();
|
|
4134
4364
|
async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
4135
4365
|
const errors = [];
|
|
4136
4366
|
const rules = [];
|
|
@@ -4143,16 +4373,18 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
4143
4373
|
let content;
|
|
4144
4374
|
let frontmatter;
|
|
4145
4375
|
if (mainFile.useFrontmatter) {
|
|
4146
|
-
const parsed = (
|
|
4147
|
-
content = parsed.content
|
|
4148
|
-
const parsedFrontmatter = parsed.data;
|
|
4376
|
+
const parsed = parseFrontmatter(rawContent);
|
|
4377
|
+
content = parsed.content;
|
|
4149
4378
|
frontmatter = {
|
|
4150
4379
|
root: mainFile.isRoot ?? false,
|
|
4151
4380
|
targets: [config.tool],
|
|
4152
|
-
description:
|
|
4153
|
-
globs:
|
|
4154
|
-
...parsedFrontmatter.tags && { tags: parsedFrontmatter.tags }
|
|
4381
|
+
description: extractStringField(parsed.data, "description", mainFile.description),
|
|
4382
|
+
globs: extractArrayField(parsed.data, "globs", ["**/*"])
|
|
4155
4383
|
};
|
|
4384
|
+
const tags = extractArrayField(parsed.data, "tags");
|
|
4385
|
+
if (tags.length > 0) {
|
|
4386
|
+
frontmatter.tags = tags;
|
|
4387
|
+
}
|
|
4156
4388
|
} else {
|
|
4157
4389
|
content = rawContent.trim();
|
|
4158
4390
|
frontmatter = {
|
|
@@ -4185,23 +4417,29 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
4185
4417
|
const files = await readdir2(dirPath);
|
|
4186
4418
|
for (const file of files) {
|
|
4187
4419
|
if (file.endsWith(dirConfig.filePattern)) {
|
|
4188
|
-
const filePath = (0,
|
|
4420
|
+
const filePath = (0, import_node_path15.join)(dirPath, file);
|
|
4189
4421
|
const fileResult = await safeAsyncOperation(async () => {
|
|
4190
4422
|
const rawContent = await readFileContent(filePath);
|
|
4191
4423
|
let content;
|
|
4192
4424
|
let frontmatter;
|
|
4193
4425
|
const filename = file.replace(new RegExp(`\\${dirConfig.filePattern}$`), "");
|
|
4194
4426
|
if (dirConfig.filePattern === ".instructions.md") {
|
|
4195
|
-
const parsed = (
|
|
4196
|
-
content = parsed.content
|
|
4197
|
-
const parsedFrontmatter = parsed.data;
|
|
4427
|
+
const parsed = parseFrontmatter(rawContent);
|
|
4428
|
+
content = parsed.content;
|
|
4198
4429
|
frontmatter = {
|
|
4199
4430
|
root: false,
|
|
4200
4431
|
targets: [config.tool],
|
|
4201
|
-
description:
|
|
4202
|
-
|
|
4203
|
-
|
|
4432
|
+
description: extractStringField(
|
|
4433
|
+
parsed.data,
|
|
4434
|
+
"description",
|
|
4435
|
+
`${dirConfig.description}: ${filename}`
|
|
4436
|
+
),
|
|
4437
|
+
globs: extractArrayField(parsed.data, "globs", ["**/*"])
|
|
4204
4438
|
};
|
|
4439
|
+
const tags = extractArrayField(parsed.data, "tags");
|
|
4440
|
+
if (tags.length > 0) {
|
|
4441
|
+
frontmatter.tags = tags;
|
|
4442
|
+
}
|
|
4205
4443
|
} else {
|
|
4206
4444
|
content = rawContent.trim();
|
|
4207
4445
|
frontmatter = {
|
|
@@ -4330,10 +4568,10 @@ async function parseMemoryFiles(memoryDir, config) {
|
|
|
4330
4568
|
const files = await readdir2(memoryDir);
|
|
4331
4569
|
for (const file of files) {
|
|
4332
4570
|
if (file.endsWith(".md")) {
|
|
4333
|
-
const filePath = (0,
|
|
4571
|
+
const filePath = (0, import_node_path15.join)(memoryDir, file);
|
|
4334
4572
|
const content = await readFileContent(filePath);
|
|
4335
4573
|
if (content.trim()) {
|
|
4336
|
-
const filename = (0,
|
|
4574
|
+
const filename = (0, import_node_path15.basename)(file, ".md");
|
|
4337
4575
|
const frontmatter = {
|
|
4338
4576
|
root: false,
|
|
4339
4577
|
targets: [config.tool],
|
|
@@ -4360,20 +4598,19 @@ async function parseCommandsFiles(commandsDir, config) {
|
|
|
4360
4598
|
const files = await readdir2(commandsDir);
|
|
4361
4599
|
for (const file of files) {
|
|
4362
4600
|
if (file.endsWith(".md")) {
|
|
4363
|
-
const filePath = (0,
|
|
4601
|
+
const filePath = (0, import_node_path15.join)(commandsDir, file);
|
|
4364
4602
|
const content = await readFileContent(filePath);
|
|
4365
4603
|
if (content.trim()) {
|
|
4366
|
-
const filename = (0,
|
|
4604
|
+
const filename = (0, import_node_path15.basename)(file, ".md");
|
|
4367
4605
|
let frontmatter;
|
|
4368
4606
|
let ruleContent;
|
|
4369
4607
|
try {
|
|
4370
|
-
const parsed = (
|
|
4371
|
-
ruleContent = parsed.content
|
|
4372
|
-
const parsedFrontmatter = parsed.data;
|
|
4608
|
+
const parsed = parseFrontmatter(content);
|
|
4609
|
+
ruleContent = parsed.content;
|
|
4373
4610
|
frontmatter = {
|
|
4374
4611
|
root: false,
|
|
4375
4612
|
targets: [config.tool],
|
|
4376
|
-
description:
|
|
4613
|
+
description: extractStringField(parsed.data, "description", `Command: ${filename}`),
|
|
4377
4614
|
globs: ["**/*"]
|
|
4378
4615
|
};
|
|
4379
4616
|
} catch {
|
|
@@ -4439,6 +4676,170 @@ async function parseSettingsFile(settingsPath, tool) {
|
|
|
4439
4676
|
};
|
|
4440
4677
|
}
|
|
4441
4678
|
|
|
4679
|
+
// src/parsers/amazonqcli.ts
|
|
4680
|
+
async function parseAmazonqcliConfiguration(baseDir = process.cwd()) {
|
|
4681
|
+
return parseMemoryBasedConfiguration(baseDir, {
|
|
4682
|
+
tool: "amazonqcli",
|
|
4683
|
+
mainFileName: ".amazonq/rules/main.md",
|
|
4684
|
+
memoryDirPath: ".amazonq/rules",
|
|
4685
|
+
settingsPath: ".amazonq/mcp.json",
|
|
4686
|
+
mainDescription: "Main Amazon Q Developer CLI configuration",
|
|
4687
|
+
memoryDescription: "Amazon Q rule",
|
|
4688
|
+
filenamePrefix: "amazonq"
|
|
4689
|
+
});
|
|
4690
|
+
}
|
|
4691
|
+
|
|
4692
|
+
// src/parsers/augmentcode.ts
|
|
4693
|
+
var import_node_path16 = require("path");
|
|
4694
|
+
|
|
4695
|
+
// src/utils/parser-helpers.ts
|
|
4696
|
+
function createParseResult() {
|
|
4697
|
+
return { rules: [], errors: [] };
|
|
4698
|
+
}
|
|
4699
|
+
function addError(result, error) {
|
|
4700
|
+
result.errors.push(error);
|
|
4701
|
+
}
|
|
4702
|
+
function addRule(result, rule) {
|
|
4703
|
+
if (!result.rules) {
|
|
4704
|
+
result.rules = [];
|
|
4705
|
+
}
|
|
4706
|
+
result.rules.push(rule);
|
|
4707
|
+
}
|
|
4708
|
+
function addRules(result, rules) {
|
|
4709
|
+
if (!result.rules) {
|
|
4710
|
+
result.rules = [];
|
|
4711
|
+
}
|
|
4712
|
+
result.rules.push(...rules);
|
|
4713
|
+
}
|
|
4714
|
+
async function safeReadFile(operation, errorContext) {
|
|
4715
|
+
try {
|
|
4716
|
+
const result = await operation();
|
|
4717
|
+
return createSuccessResult(result);
|
|
4718
|
+
} catch (error) {
|
|
4719
|
+
return createErrorResult(error, errorContext);
|
|
4720
|
+
}
|
|
4721
|
+
}
|
|
4722
|
+
|
|
4723
|
+
// src/parsers/augmentcode.ts
|
|
4724
|
+
async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
|
|
4725
|
+
return parseUnifiedAugmentcode(baseDir, {
|
|
4726
|
+
rulesDir: ".augment/rules",
|
|
4727
|
+
targetName: "augmentcode",
|
|
4728
|
+
filenamePrefix: "augmentcode"
|
|
4729
|
+
});
|
|
4730
|
+
}
|
|
4731
|
+
async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
|
|
4732
|
+
return parseUnifiedAugmentcode(baseDir, {
|
|
4733
|
+
legacyFilePath: ".augment-guidelines",
|
|
4734
|
+
targetName: "augmentcode-legacy",
|
|
4735
|
+
filenamePrefix: "augmentcode-legacy"
|
|
4736
|
+
});
|
|
4737
|
+
}
|
|
4738
|
+
async function parseUnifiedAugmentcode(baseDir, config) {
|
|
4739
|
+
const result = createParseResult();
|
|
4740
|
+
if (config.rulesDir) {
|
|
4741
|
+
const rulesDir = (0, import_node_path16.join)(baseDir, config.rulesDir);
|
|
4742
|
+
if (await fileExists(rulesDir)) {
|
|
4743
|
+
const rulesResult = await parseAugmentRules(rulesDir, config);
|
|
4744
|
+
addRules(result, rulesResult.rules);
|
|
4745
|
+
result.errors.push(...rulesResult.errors);
|
|
4746
|
+
} else {
|
|
4747
|
+
addError(
|
|
4748
|
+
result,
|
|
4749
|
+
`No AugmentCode configuration found. Expected ${config.rulesDir} directory.`
|
|
4750
|
+
);
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
if (config.legacyFilePath) {
|
|
4754
|
+
const legacyPath = (0, import_node_path16.join)(baseDir, config.legacyFilePath);
|
|
4755
|
+
if (await fileExists(legacyPath)) {
|
|
4756
|
+
const legacyResult = await parseAugmentGuidelines(legacyPath, config);
|
|
4757
|
+
if (legacyResult.rule) {
|
|
4758
|
+
addRule(result, legacyResult.rule);
|
|
4759
|
+
}
|
|
4760
|
+
result.errors.push(...legacyResult.errors);
|
|
4761
|
+
} else {
|
|
4762
|
+
addError(
|
|
4763
|
+
result,
|
|
4764
|
+
`No AugmentCode legacy configuration found. Expected ${config.legacyFilePath} file.`
|
|
4765
|
+
);
|
|
4766
|
+
}
|
|
4767
|
+
}
|
|
4768
|
+
return { rules: result.rules || [], errors: result.errors };
|
|
4769
|
+
}
|
|
4770
|
+
async function parseAugmentRules(rulesDir, config) {
|
|
4771
|
+
const rules = [];
|
|
4772
|
+
const errors = [];
|
|
4773
|
+
try {
|
|
4774
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
4775
|
+
const files = await readdir2(rulesDir);
|
|
4776
|
+
for (const file of files) {
|
|
4777
|
+
if (file.endsWith(".md") || file.endsWith(".mdc")) {
|
|
4778
|
+
const filePath = (0, import_node_path16.join)(rulesDir, file);
|
|
4779
|
+
try {
|
|
4780
|
+
const rawContent = await readFileContent(filePath);
|
|
4781
|
+
const parsed = parseFrontmatter(rawContent);
|
|
4782
|
+
const ruleType = extractStringField(parsed.data, "type", "manual");
|
|
4783
|
+
const description = extractStringField(parsed.data, "description", "");
|
|
4784
|
+
const tags = extractArrayField(parsed.data, "tags");
|
|
4785
|
+
const isRoot = ruleType === "always";
|
|
4786
|
+
const filename = (0, import_node_path16.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
|
|
4787
|
+
const frontmatter = {
|
|
4788
|
+
root: isRoot,
|
|
4789
|
+
targets: [config.targetName],
|
|
4790
|
+
description,
|
|
4791
|
+
globs: ["**/*"],
|
|
4792
|
+
// AugmentCode doesn't use specific globs in the same way
|
|
4793
|
+
...tags.length > 0 && { tags }
|
|
4794
|
+
};
|
|
4795
|
+
rules.push({
|
|
4796
|
+
frontmatter,
|
|
4797
|
+
content: parsed.content.trim(),
|
|
4798
|
+
filename: `${config.filenamePrefix}-${ruleType}-${filename}`,
|
|
4799
|
+
filepath: filePath
|
|
4800
|
+
});
|
|
4801
|
+
} catch (error) {
|
|
4802
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4803
|
+
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4806
|
+
}
|
|
4807
|
+
} catch (error) {
|
|
4808
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4809
|
+
errors.push(`Failed to read ${config.rulesDir || rulesDir} directory: ${errorMessage}`);
|
|
4810
|
+
}
|
|
4811
|
+
return { rules, errors };
|
|
4812
|
+
}
|
|
4813
|
+
async function parseAugmentGuidelines(guidelinesPath, config) {
|
|
4814
|
+
const parseResult = await safeReadFile(
|
|
4815
|
+
async () => {
|
|
4816
|
+
const content = await readFileContent(guidelinesPath);
|
|
4817
|
+
if (content.trim()) {
|
|
4818
|
+
const frontmatter = {
|
|
4819
|
+
root: true,
|
|
4820
|
+
// Legacy guidelines become root rules
|
|
4821
|
+
targets: [config.targetName],
|
|
4822
|
+
description: "Legacy AugmentCode guidelines",
|
|
4823
|
+
globs: ["**/*"]
|
|
4824
|
+
};
|
|
4825
|
+
return {
|
|
4826
|
+
frontmatter,
|
|
4827
|
+
content: content.trim(),
|
|
4828
|
+
filename: `${config.filenamePrefix}-guidelines`,
|
|
4829
|
+
filepath: guidelinesPath
|
|
4830
|
+
};
|
|
4831
|
+
}
|
|
4832
|
+
return null;
|
|
4833
|
+
},
|
|
4834
|
+
`Failed to parse ${config.legacyFilePath || guidelinesPath}`
|
|
4835
|
+
);
|
|
4836
|
+
if (parseResult.success) {
|
|
4837
|
+
return { rule: parseResult.result || null, errors: [] };
|
|
4838
|
+
} else {
|
|
4839
|
+
return { rule: null, errors: [parseResult.error || "Unknown error"] };
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
|
|
4442
4843
|
// src/parsers/claudecode.ts
|
|
4443
4844
|
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
4444
4845
|
return parseMemoryBasedConfiguration(baseDir, {
|
|
@@ -4474,7 +4875,7 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
4474
4875
|
}
|
|
4475
4876
|
|
|
4476
4877
|
// src/parsers/codexcli.ts
|
|
4477
|
-
var
|
|
4878
|
+
var import_node_path17 = require("path");
|
|
4478
4879
|
|
|
4479
4880
|
// src/parsers/copilot.ts
|
|
4480
4881
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
@@ -4497,8 +4898,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
4497
4898
|
}
|
|
4498
4899
|
|
|
4499
4900
|
// src/parsers/cursor.ts
|
|
4500
|
-
var
|
|
4501
|
-
var import_gray_matter5 = __toESM(require("gray-matter"), 1);
|
|
4901
|
+
var import_node_path18 = require("path");
|
|
4502
4902
|
var import_js_yaml = require("js-yaml");
|
|
4503
4903
|
var import_mini8 = require("zod/mini");
|
|
4504
4904
|
var customMatterOptions = {
|
|
@@ -4622,12 +5022,12 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
4622
5022
|
const rules = [];
|
|
4623
5023
|
let ignorePatterns;
|
|
4624
5024
|
let mcpServers;
|
|
4625
|
-
const cursorFilePath = (0,
|
|
5025
|
+
const cursorFilePath = (0, import_node_path18.join)(baseDir, ".cursorrules");
|
|
4626
5026
|
if (await fileExists(cursorFilePath)) {
|
|
4627
5027
|
try {
|
|
4628
5028
|
const rawContent = await readFileContent(cursorFilePath);
|
|
4629
|
-
const parsed = (
|
|
4630
|
-
const content = parsed.content
|
|
5029
|
+
const parsed = parseFrontmatter(rawContent, { matterOptions: customMatterOptions });
|
|
5030
|
+
const content = parsed.content;
|
|
4631
5031
|
if (content) {
|
|
4632
5032
|
const frontmatter = convertCursorMdcFrontmatter(parsed.data, "cursorrules");
|
|
4633
5033
|
frontmatter.targets = ["cursor"];
|
|
@@ -4643,20 +5043,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
4643
5043
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
4644
5044
|
}
|
|
4645
5045
|
}
|
|
4646
|
-
const cursorRulesDir = (0,
|
|
5046
|
+
const cursorRulesDir = (0, import_node_path18.join)(baseDir, ".cursor", "rules");
|
|
4647
5047
|
if (await fileExists(cursorRulesDir)) {
|
|
4648
5048
|
try {
|
|
4649
5049
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
4650
5050
|
const files = await readdir2(cursorRulesDir);
|
|
4651
5051
|
for (const file of files) {
|
|
4652
5052
|
if (file.endsWith(".mdc")) {
|
|
4653
|
-
const filePath = (0,
|
|
5053
|
+
const filePath = (0, import_node_path18.join)(cursorRulesDir, file);
|
|
4654
5054
|
try {
|
|
4655
5055
|
const rawContent = await readFileContent(filePath);
|
|
4656
|
-
const parsed = (
|
|
4657
|
-
const content = parsed.content
|
|
5056
|
+
const parsed = parseFrontmatter(rawContent, { matterOptions: customMatterOptions });
|
|
5057
|
+
const content = parsed.content;
|
|
4658
5058
|
if (content) {
|
|
4659
|
-
const filename = (0,
|
|
5059
|
+
const filename = (0, import_node_path18.basename)(file, ".mdc");
|
|
4660
5060
|
const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
|
|
4661
5061
|
rules.push({
|
|
4662
5062
|
frontmatter,
|
|
@@ -4679,7 +5079,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
4679
5079
|
if (rules.length === 0) {
|
|
4680
5080
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
4681
5081
|
}
|
|
4682
|
-
const cursorIgnorePath = (0,
|
|
5082
|
+
const cursorIgnorePath = (0, import_node_path18.join)(baseDir, ".cursorignore");
|
|
4683
5083
|
if (await fileExists(cursorIgnorePath)) {
|
|
4684
5084
|
try {
|
|
4685
5085
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -4692,7 +5092,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
4692
5092
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
4693
5093
|
}
|
|
4694
5094
|
}
|
|
4695
|
-
const cursorMcpPath = (0,
|
|
5095
|
+
const cursorMcpPath = (0, import_node_path18.join)(baseDir, ".cursor", "mcp.json");
|
|
4696
5096
|
if (await fileExists(cursorMcpPath)) {
|
|
4697
5097
|
try {
|
|
4698
5098
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -4742,11 +5142,11 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
4742
5142
|
}
|
|
4743
5143
|
|
|
4744
5144
|
// src/parsers/junie.ts
|
|
4745
|
-
var
|
|
5145
|
+
var import_node_path19 = require("path");
|
|
4746
5146
|
async function parseJunieConfiguration(baseDir = process.cwd()) {
|
|
4747
5147
|
const errors = [];
|
|
4748
5148
|
const rules = [];
|
|
4749
|
-
const guidelinesPath = (0,
|
|
5149
|
+
const guidelinesPath = (0, import_node_path19.join)(baseDir, ".junie", "guidelines.md");
|
|
4750
5150
|
if (!await fileExists(guidelinesPath)) {
|
|
4751
5151
|
errors.push(".junie/guidelines.md file not found");
|
|
4752
5152
|
return { rules, errors };
|
|
@@ -4777,6 +5177,32 @@ async function parseJunieConfiguration(baseDir = process.cwd()) {
|
|
|
4777
5177
|
return { rules, errors };
|
|
4778
5178
|
}
|
|
4779
5179
|
|
|
5180
|
+
// src/parsers/opencode.ts
|
|
5181
|
+
async function parseOpCodeIgnore(opcodeignorePath) {
|
|
5182
|
+
try {
|
|
5183
|
+
const content = await readFileContent(opcodeignorePath);
|
|
5184
|
+
const patterns = content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
5185
|
+
return patterns;
|
|
5186
|
+
} catch {
|
|
5187
|
+
return [];
|
|
5188
|
+
}
|
|
5189
|
+
}
|
|
5190
|
+
async function parseOpenCodeConfiguration(baseDir = process.cwd()) {
|
|
5191
|
+
return parseMemoryBasedConfiguration(baseDir, {
|
|
5192
|
+
tool: "opencode",
|
|
5193
|
+
mainFileName: "AGENTS.md",
|
|
5194
|
+
memoryDirPath: ".opencode/memories",
|
|
5195
|
+
settingsPath: "opencode.json",
|
|
5196
|
+
mainDescription: "Main OpenCode configuration",
|
|
5197
|
+
memoryDescription: "Memory file",
|
|
5198
|
+
filenamePrefix: "opencode",
|
|
5199
|
+
additionalIgnoreFile: {
|
|
5200
|
+
path: ".opcodeignore",
|
|
5201
|
+
parser: parseOpCodeIgnore
|
|
5202
|
+
}
|
|
5203
|
+
});
|
|
5204
|
+
}
|
|
5205
|
+
|
|
4780
5206
|
// src/parsers/roo.ts
|
|
4781
5207
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
4782
5208
|
return parseConfigurationFiles(baseDir, {
|
|
@@ -4799,10 +5225,11 @@ async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
|
4799
5225
|
|
|
4800
5226
|
// src/parsers/windsurf.ts
|
|
4801
5227
|
var import_promises3 = require("fs/promises");
|
|
4802
|
-
var
|
|
4803
|
-
|
|
5228
|
+
var import_node_path20 = require("path");
|
|
5229
|
+
init_logger();
|
|
4804
5230
|
|
|
4805
5231
|
// src/core/importer.ts
|
|
5232
|
+
init_logger();
|
|
4806
5233
|
async function importConfiguration(options) {
|
|
4807
5234
|
const {
|
|
4808
5235
|
tool,
|
|
@@ -4816,10 +5243,17 @@ async function importConfiguration(options) {
|
|
|
4816
5243
|
let ignorePatterns;
|
|
4817
5244
|
let mcpServers;
|
|
4818
5245
|
if (verbose) {
|
|
4819
|
-
|
|
5246
|
+
logger.log(`Importing ${tool} configuration from ${baseDir}...`);
|
|
4820
5247
|
}
|
|
4821
5248
|
try {
|
|
4822
5249
|
switch (tool) {
|
|
5250
|
+
case "amazonqcli": {
|
|
5251
|
+
const amazonqResult = await parseAmazonqcliConfiguration(baseDir);
|
|
5252
|
+
rules = amazonqResult.rules;
|
|
5253
|
+
errors.push(...amazonqResult.errors);
|
|
5254
|
+
mcpServers = amazonqResult.mcpServers;
|
|
5255
|
+
break;
|
|
5256
|
+
}
|
|
4823
5257
|
case "augmentcode": {
|
|
4824
5258
|
const augmentResult = await parseAugmentcodeConfiguration(baseDir);
|
|
4825
5259
|
rules = augmentResult.rules;
|
|
@@ -4880,6 +5314,14 @@ async function importConfiguration(options) {
|
|
|
4880
5314
|
errors.push(...junieResult.errors);
|
|
4881
5315
|
break;
|
|
4882
5316
|
}
|
|
5317
|
+
case "opencode": {
|
|
5318
|
+
const opencodeResult = await parseOpenCodeConfiguration(baseDir);
|
|
5319
|
+
rules = opencodeResult.rules;
|
|
5320
|
+
errors.push(...opencodeResult.errors);
|
|
5321
|
+
ignorePatterns = opencodeResult.ignorePatterns;
|
|
5322
|
+
mcpServers = opencodeResult.mcpServers;
|
|
5323
|
+
break;
|
|
5324
|
+
}
|
|
4883
5325
|
default:
|
|
4884
5326
|
errors.push(`Unsupported tool: ${tool}`);
|
|
4885
5327
|
return { success: false, rulesCreated: 0, errors };
|
|
@@ -4892,7 +5334,7 @@ async function importConfiguration(options) {
|
|
|
4892
5334
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
4893
5335
|
return { success: false, rulesCreated: 0, errors };
|
|
4894
5336
|
}
|
|
4895
|
-
const rulesDirPath = (0,
|
|
5337
|
+
const rulesDirPath = (0, import_node_path21.join)(baseDir, rulesDir);
|
|
4896
5338
|
try {
|
|
4897
5339
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
4898
5340
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -4907,22 +5349,22 @@ async function importConfiguration(options) {
|
|
|
4907
5349
|
const baseFilename = rule.filename;
|
|
4908
5350
|
let targetDir = rulesDirPath;
|
|
4909
5351
|
if (rule.type === "command") {
|
|
4910
|
-
targetDir = (0,
|
|
5352
|
+
targetDir = (0, import_node_path21.join)(rulesDirPath, "commands");
|
|
4911
5353
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
4912
5354
|
await mkdir3(targetDir, { recursive: true });
|
|
4913
5355
|
} else {
|
|
4914
5356
|
if (!useLegacyLocation) {
|
|
4915
|
-
targetDir = (0,
|
|
5357
|
+
targetDir = (0, import_node_path21.join)(rulesDirPath, "rules");
|
|
4916
5358
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
4917
5359
|
await mkdir3(targetDir, { recursive: true });
|
|
4918
5360
|
}
|
|
4919
5361
|
}
|
|
4920
|
-
const filePath = (0,
|
|
5362
|
+
const filePath = (0, import_node_path21.join)(targetDir, `${baseFilename}.md`);
|
|
4921
5363
|
const content = generateRuleFileContent(rule);
|
|
4922
5364
|
await writeFileContent(filePath, content);
|
|
4923
5365
|
rulesCreated++;
|
|
4924
5366
|
if (verbose) {
|
|
4925
|
-
|
|
5367
|
+
logger.success(`Created rule file: ${filePath}`);
|
|
4926
5368
|
}
|
|
4927
5369
|
} catch (error) {
|
|
4928
5370
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -4932,13 +5374,13 @@ async function importConfiguration(options) {
|
|
|
4932
5374
|
let ignoreFileCreated = false;
|
|
4933
5375
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
4934
5376
|
try {
|
|
4935
|
-
const rulesyncignorePath = (0,
|
|
5377
|
+
const rulesyncignorePath = (0, import_node_path21.join)(baseDir, ".rulesyncignore");
|
|
4936
5378
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
4937
5379
|
`;
|
|
4938
5380
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
4939
5381
|
ignoreFileCreated = true;
|
|
4940
5382
|
if (verbose) {
|
|
4941
|
-
|
|
5383
|
+
logger.success(`Created .rulesyncignore with ${ignorePatterns.length} patterns`);
|
|
4942
5384
|
}
|
|
4943
5385
|
} catch (error) {
|
|
4944
5386
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -4948,13 +5390,13 @@ async function importConfiguration(options) {
|
|
|
4948
5390
|
let mcpFileCreated = false;
|
|
4949
5391
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
4950
5392
|
try {
|
|
4951
|
-
const mcpPath = (0,
|
|
5393
|
+
const mcpPath = (0, import_node_path21.join)(baseDir, rulesDir, ".mcp.json");
|
|
4952
5394
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
4953
5395
|
`;
|
|
4954
5396
|
await writeFileContent(mcpPath, mcpContent);
|
|
4955
5397
|
mcpFileCreated = true;
|
|
4956
5398
|
if (verbose) {
|
|
4957
|
-
|
|
5399
|
+
logger.success(`Created .mcp.json with ${Object.keys(mcpServers).length} servers`);
|
|
4958
5400
|
}
|
|
4959
5401
|
} catch (error) {
|
|
4960
5402
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -4975,17 +5417,19 @@ function generateRuleFileContent(rule) {
|
|
|
4975
5417
|
description: rule.frontmatter.description,
|
|
4976
5418
|
targets: rule.frontmatter.targets
|
|
4977
5419
|
};
|
|
4978
|
-
const frontmatter2 =
|
|
5420
|
+
const frontmatter2 = import_gray_matter2.default.stringify("", simplifiedFrontmatter);
|
|
4979
5421
|
return frontmatter2 + rule.content;
|
|
4980
5422
|
}
|
|
4981
|
-
const frontmatter =
|
|
5423
|
+
const frontmatter = import_gray_matter2.default.stringify("", rule.frontmatter);
|
|
4982
5424
|
return frontmatter + rule.content;
|
|
4983
5425
|
}
|
|
4984
5426
|
|
|
4985
5427
|
// src/cli/commands/import.ts
|
|
5428
|
+
init_logger();
|
|
4986
5429
|
async function importCommand(options = {}) {
|
|
4987
5430
|
logger.setVerbose(options.verbose || false);
|
|
4988
5431
|
const tools = [];
|
|
5432
|
+
if (options.amazonqcli) tools.push("amazonqcli");
|
|
4989
5433
|
if (options.augmentcode) tools.push("augmentcode");
|
|
4990
5434
|
if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
|
|
4991
5435
|
if (options.claudecode) tools.push("claudecode");
|
|
@@ -4994,9 +5438,10 @@ async function importCommand(options = {}) {
|
|
|
4994
5438
|
if (options.cline) tools.push("cline");
|
|
4995
5439
|
if (options.roo) tools.push("roo");
|
|
4996
5440
|
if (options.geminicli) tools.push("geminicli");
|
|
5441
|
+
if (options.opencode) tools.push("opencode");
|
|
4997
5442
|
if (tools.length === 0) {
|
|
4998
5443
|
logger.error(
|
|
4999
|
-
"\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
|
|
5444
|
+
"\u274C Please specify one tool to import from (--amazonqcli, --augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli, --opencode)"
|
|
5000
5445
|
);
|
|
5001
5446
|
process.exit(1);
|
|
5002
5447
|
}
|
|
@@ -5044,23 +5489,24 @@ async function importCommand(options = {}) {
|
|
|
5044
5489
|
}
|
|
5045
5490
|
|
|
5046
5491
|
// src/cli/commands/init.ts
|
|
5047
|
-
var
|
|
5492
|
+
var import_node_path22 = require("path");
|
|
5493
|
+
init_logger();
|
|
5048
5494
|
async function initCommand(options = {}) {
|
|
5049
5495
|
const configResult = await loadConfig();
|
|
5050
5496
|
const config = configResult.config;
|
|
5051
5497
|
const aiRulesDir = config.aiRulesDir;
|
|
5052
|
-
|
|
5498
|
+
logger.log("Initializing rulesync...");
|
|
5053
5499
|
await ensureDir(aiRulesDir);
|
|
5054
5500
|
const useLegacy = options.legacy ?? config.legacy ?? false;
|
|
5055
|
-
const rulesDir = useLegacy ? aiRulesDir : (0,
|
|
5501
|
+
const rulesDir = useLegacy ? aiRulesDir : (0, import_node_path22.join)(aiRulesDir, "rules");
|
|
5056
5502
|
if (!useLegacy) {
|
|
5057
5503
|
await ensureDir(rulesDir);
|
|
5058
5504
|
}
|
|
5059
5505
|
await createSampleFiles(rulesDir);
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5506
|
+
logger.success("rulesync initialized successfully!");
|
|
5507
|
+
logger.log("\nNext steps:");
|
|
5508
|
+
logger.log(`1. Edit rule files in ${rulesDir}/`);
|
|
5509
|
+
logger.log("2. Run 'rulesync generate' to create configuration files");
|
|
5064
5510
|
}
|
|
5065
5511
|
async function createSampleFiles(rulesDir) {
|
|
5066
5512
|
const sampleFile = {
|
|
@@ -5098,36 +5544,37 @@ globs: ["**/*"]
|
|
|
5098
5544
|
- Follow single responsibility principle
|
|
5099
5545
|
`
|
|
5100
5546
|
};
|
|
5101
|
-
const filepath = (0,
|
|
5547
|
+
const filepath = (0, import_node_path22.join)(rulesDir, sampleFile.filename);
|
|
5102
5548
|
if (!await fileExists(filepath)) {
|
|
5103
5549
|
await writeFileContent(filepath, sampleFile.content);
|
|
5104
|
-
|
|
5550
|
+
logger.success(`Created ${filepath}`);
|
|
5105
5551
|
} else {
|
|
5106
|
-
|
|
5552
|
+
logger.log(`Skipped ${filepath} (already exists)`);
|
|
5107
5553
|
}
|
|
5108
5554
|
}
|
|
5109
5555
|
|
|
5110
5556
|
// src/cli/commands/status.ts
|
|
5557
|
+
init_logger();
|
|
5111
5558
|
async function statusCommand() {
|
|
5112
5559
|
const config = getDefaultConfig();
|
|
5113
|
-
|
|
5114
|
-
|
|
5560
|
+
logger.log("rulesync Status");
|
|
5561
|
+
logger.log("===============");
|
|
5115
5562
|
const rulesyncExists = await fileExists(config.aiRulesDir);
|
|
5116
|
-
|
|
5563
|
+
logger.log(`
|
|
5117
5564
|
\u{1F4C1} .rulesync directory: ${rulesyncExists ? "\u2705 Found" : "\u274C Not found"}`);
|
|
5118
5565
|
if (!rulesyncExists) {
|
|
5119
|
-
|
|
5566
|
+
logger.log("\n\u{1F4A1} Run 'rulesync init' to get started");
|
|
5120
5567
|
return;
|
|
5121
5568
|
}
|
|
5122
5569
|
try {
|
|
5123
5570
|
const rules = await parseRulesFromDirectory(config.aiRulesDir);
|
|
5124
|
-
|
|
5571
|
+
logger.log(`
|
|
5125
5572
|
\u{1F4CB} Rules: ${rules.length} total`);
|
|
5126
5573
|
if (rules.length > 0) {
|
|
5127
5574
|
const rootRules = rules.filter((r) => r.frontmatter.root).length;
|
|
5128
5575
|
const nonRootRules = rules.length - rootRules;
|
|
5129
|
-
|
|
5130
|
-
|
|
5576
|
+
logger.log(` - Root rules: ${rootRules}`);
|
|
5577
|
+
logger.log(` - Non-root rules: ${nonRootRules}`);
|
|
5131
5578
|
const targetCounts = { copilot: 0, cursor: 0, cline: 0, claudecode: 0, roo: 0 };
|
|
5132
5579
|
for (const rule of rules) {
|
|
5133
5580
|
const targets = rule.frontmatter.targets[0] === "*" ? config.defaultTargets : rule.frontmatter.targets;
|
|
@@ -5139,73 +5586,75 @@ async function statusCommand() {
|
|
|
5139
5586
|
else if (target === "roo") targetCounts.roo++;
|
|
5140
5587
|
}
|
|
5141
5588
|
}
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
}
|
|
5149
|
-
|
|
5589
|
+
logger.log("\n\u{1F3AF} Target tool coverage:");
|
|
5590
|
+
logger.log(` - Copilot: ${targetCounts.copilot} rules`);
|
|
5591
|
+
logger.log(` - Cursor: ${targetCounts.cursor} rules`);
|
|
5592
|
+
logger.log(` - Cline: ${targetCounts.cline} rules`);
|
|
5593
|
+
logger.log(` - Claude Code: ${targetCounts.claudecode} rules`);
|
|
5594
|
+
logger.log(` - Roo: ${targetCounts.roo} rules`);
|
|
5595
|
+
}
|
|
5596
|
+
logger.log("\n\u{1F4E4} Generated files:");
|
|
5150
5597
|
for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
|
|
5151
5598
|
const outputExists = await fileExists(outputPath);
|
|
5152
|
-
|
|
5599
|
+
logger.log(` - ${tool}: ${outputExists ? "\u2705 Generated" : "\u274C Not found"}`);
|
|
5153
5600
|
}
|
|
5154
5601
|
if (rules.length > 0) {
|
|
5155
|
-
|
|
5602
|
+
logger.log("\n\u{1F4A1} Run 'rulesync generate' to update configuration files");
|
|
5156
5603
|
}
|
|
5157
5604
|
} catch (error) {
|
|
5158
|
-
|
|
5605
|
+
logger.error("\nFailed to get status:", error);
|
|
5159
5606
|
}
|
|
5160
5607
|
}
|
|
5161
5608
|
|
|
5162
5609
|
// src/cli/commands/validate.ts
|
|
5610
|
+
init_logger();
|
|
5163
5611
|
async function validateCommand() {
|
|
5164
5612
|
const config = getDefaultConfig();
|
|
5165
|
-
|
|
5613
|
+
logger.log("Validating rulesync configuration...");
|
|
5166
5614
|
if (!await fileExists(config.aiRulesDir)) {
|
|
5167
|
-
|
|
5615
|
+
logger.error(".rulesync directory not found. Run 'rulesync init' first.");
|
|
5168
5616
|
process.exit(1);
|
|
5169
5617
|
}
|
|
5170
5618
|
try {
|
|
5171
5619
|
const rules = await parseRulesFromDirectory(config.aiRulesDir);
|
|
5172
5620
|
if (rules.length === 0) {
|
|
5173
|
-
|
|
5621
|
+
logger.warn("No rules found in .rulesync directory");
|
|
5174
5622
|
return;
|
|
5175
5623
|
}
|
|
5176
|
-
|
|
5624
|
+
logger.log(`Found ${rules.length} rule(s), validating...`);
|
|
5177
5625
|
const validation = await validateRules(rules);
|
|
5178
5626
|
if (validation.warnings.length > 0) {
|
|
5179
|
-
|
|
5627
|
+
logger.log("\n\u26A0\uFE0F Warnings:");
|
|
5180
5628
|
for (const warning of validation.warnings) {
|
|
5181
|
-
|
|
5629
|
+
logger.log(` - ${warning}`);
|
|
5182
5630
|
}
|
|
5183
5631
|
}
|
|
5184
5632
|
if (validation.errors.length > 0) {
|
|
5185
|
-
|
|
5633
|
+
logger.log("\nErrors:");
|
|
5186
5634
|
for (const error of validation.errors) {
|
|
5187
|
-
|
|
5635
|
+
logger.log(` - ${error}`);
|
|
5188
5636
|
}
|
|
5189
5637
|
}
|
|
5190
5638
|
if (validation.isValid) {
|
|
5191
|
-
|
|
5639
|
+
logger.success("\nAll rules are valid!");
|
|
5192
5640
|
} else {
|
|
5193
|
-
|
|
5194
|
-
|
|
5641
|
+
logger.log(`
|
|
5642
|
+
Validation failed with ${validation.errors.length} error(s)`);
|
|
5195
5643
|
process.exit(1);
|
|
5196
5644
|
}
|
|
5197
5645
|
} catch (error) {
|
|
5198
|
-
|
|
5646
|
+
logger.error("Failed to validate rules:", error);
|
|
5199
5647
|
process.exit(1);
|
|
5200
5648
|
}
|
|
5201
5649
|
}
|
|
5202
5650
|
|
|
5203
5651
|
// src/cli/commands/watch.ts
|
|
5204
5652
|
var import_chokidar = require("chokidar");
|
|
5653
|
+
init_logger();
|
|
5205
5654
|
async function watchCommand() {
|
|
5206
5655
|
const config = getDefaultConfig();
|
|
5207
|
-
|
|
5208
|
-
|
|
5656
|
+
logger.log("\u{1F440} Watching for changes in .rulesync directory...");
|
|
5657
|
+
logger.log("Press Ctrl+C to stop watching");
|
|
5209
5658
|
await generateCommand({ verbose: false });
|
|
5210
5659
|
const watcher = (0, import_chokidar.watch)(`${config.aiRulesDir}/**/*.md`, {
|
|
5211
5660
|
ignoreInitial: true,
|
|
@@ -5215,26 +5664,26 @@ async function watchCommand() {
|
|
|
5215
5664
|
const handleChange = async (path5) => {
|
|
5216
5665
|
if (isGenerating) return;
|
|
5217
5666
|
isGenerating = true;
|
|
5218
|
-
|
|
5667
|
+
logger.log(`
|
|
5219
5668
|
\u{1F4DD} Detected change in ${path5}`);
|
|
5220
5669
|
try {
|
|
5221
5670
|
await generateCommand({ verbose: false });
|
|
5222
|
-
|
|
5671
|
+
logger.success("Regenerated configuration files");
|
|
5223
5672
|
} catch (error) {
|
|
5224
|
-
|
|
5673
|
+
logger.error("Failed to regenerate:", error);
|
|
5225
5674
|
} finally {
|
|
5226
5675
|
isGenerating = false;
|
|
5227
5676
|
}
|
|
5228
5677
|
};
|
|
5229
5678
|
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path5) => {
|
|
5230
|
-
|
|
5679
|
+
logger.log(`
|
|
5231
5680
|
\u{1F5D1}\uFE0F Removed ${path5}`);
|
|
5232
5681
|
handleChange(path5);
|
|
5233
5682
|
}).on("error", (error) => {
|
|
5234
|
-
|
|
5683
|
+
logger.error("Watcher error:", error);
|
|
5235
5684
|
});
|
|
5236
5685
|
process.on("SIGINT", () => {
|
|
5237
|
-
|
|
5686
|
+
logger.log("\n\n\u{1F44B} Stopping watcher...");
|
|
5238
5687
|
watcher.close();
|
|
5239
5688
|
process.exit(0);
|
|
5240
5689
|
});
|
|
@@ -5242,12 +5691,12 @@ async function watchCommand() {
|
|
|
5242
5691
|
|
|
5243
5692
|
// src/cli/index.ts
|
|
5244
5693
|
var program = new import_commander.Command();
|
|
5245
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
5694
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.64.0");
|
|
5246
5695
|
program.command("init").description("Initialize rulesync in current directory").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(initCommand);
|
|
5247
5696
|
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);
|
|
5248
5697
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
5249
|
-
program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "Import from AugmentCode legacy format (.augment-guidelines)").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("-v, --verbose", "Verbose output").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(importCommand);
|
|
5250
|
-
program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "Generate only for AugmentCode legacy format").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--codexcli", "Generate only for OpenAI Codex CLI").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--junie", "Generate only for JetBrains Junie").option("--kiro", "Generate only for Kiro IDE").option("--windsurf", "Generate only for Windsurf").option("--delete", "Delete all existing files in output directories before generating").option(
|
|
5698
|
+
program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "Import from AugmentCode legacy format (.augment-guidelines)").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("--opencode", "Import from OpenCode (AGENTS.md)").option("-v, --verbose", "Verbose output").option("--legacy", "Use legacy file location (.rulesync/*.md instead of .rulesync/rules/*.md)").action(importCommand);
|
|
5699
|
+
program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "Generate only for AugmentCode legacy format").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--codexcli", "Generate only for OpenAI Codex CLI").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--junie", "Generate only for JetBrains Junie").option("--kiro", "Generate only for Kiro IDE").option("--opencode", "Generate only for OpenCode").option("--windsurf", "Generate only for Windsurf").option("--delete", "Delete all existing files in output directories before generating").option(
|
|
5251
5700
|
"-b, --base-dir <paths>",
|
|
5252
5701
|
"Base directories to generate files (comma-separated for multiple paths)"
|
|
5253
5702
|
).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
|
|
@@ -5263,6 +5712,7 @@ program.command("generate").description("Generate configuration files for AI too
|
|
|
5263
5712
|
if (options.geminicli) tools.push("geminicli");
|
|
5264
5713
|
if (options.junie) tools.push("junie");
|
|
5265
5714
|
if (options.kiro) tools.push("kiro");
|
|
5715
|
+
if (options.opencode) tools.push("opencode");
|
|
5266
5716
|
if (options.windsurf) tools.push("windsurf");
|
|
5267
5717
|
const generateOptions = {
|
|
5268
5718
|
verbose: options.verbose,
|