rulesync 0.49.0 → 0.51.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.ja.md +3 -2
- package/README.md +90 -2
- package/dist/{augmentcode-KTXT5GMO.js → augmentcode-ICLQ2NEZ.js} +2 -2
- package/dist/{chunk-YVRWBSCK.js → chunk-4MZXYV5H.js} +1 -1
- package/dist/{chunk-XHNIEO22.js → chunk-AREA26HF.js} +1 -1
- package/dist/{chunk-MCADLVGY.js → chunk-FFW6TGCM.js} +1 -1
- package/dist/{chunk-QNHGYRJT.js → chunk-IXCMY24P.js} +1 -1
- package/dist/{chunk-6TAURCQP.js → chunk-OPZOVKIL.js} +1 -1
- package/dist/{chunk-R5HFWJ5L.js → chunk-QUJMXHNR.js} +1 -1
- package/dist/{chunk-6MFEIYHN.js → chunk-TTHBLXOB.js} +1 -1
- package/dist/{chunk-IBJGN3JQ.js → chunk-USKQYIZ2.js} +11 -3
- package/dist/chunk-Y26DXTAT.js +90 -0
- package/dist/{chunk-G5LLOIO4.js → chunk-Y2XH4E5R.js} +1 -1
- package/dist/{claudecode-OE4TSKPS.js → claudecode-SRXYHIJE.js} +2 -2
- package/dist/{cline-CWLQS5CV.js → cline-IJW27CUU.js} +2 -2
- package/dist/{copilot-PVZDRAU5.js → copilot-ARYIWVJ7.js} +2 -2
- package/dist/{cursor-ZZV7AY6H.js → cursor-FCS74IAH.js} +2 -2
- package/dist/{geminicli-RSXOWFEN.js → geminicli-VOPV6DXZ.js} +2 -2
- package/dist/index.cjs +1374 -549
- package/dist/index.js +1254 -484
- package/dist/junie-A2Y2WZI4.js +9 -0
- package/dist/{kiro-YRKVCDIQ.js → kiro-MHIK4UBV.js} +2 -2
- package/dist/{roo-HCRGELWQ.js → roo-VG4IUNTE.js} +2 -2
- package/package.json +17 -16
package/dist/index.js
CHANGED
|
@@ -1,33 +1,38 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
generateKiroMcp
|
|
4
|
+
} from "./chunk-QUJMXHNR.js";
|
|
2
5
|
import {
|
|
3
6
|
generateRooMcp
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-FFW6TGCM.js";
|
|
5
8
|
import {
|
|
6
9
|
generateAugmentcodeMcp
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-OPZOVKIL.js";
|
|
8
11
|
import {
|
|
9
12
|
generateClaudeMcp
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-4MZXYV5H.js";
|
|
11
14
|
import {
|
|
12
15
|
generateClineMcp
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-AREA26HF.js";
|
|
14
17
|
import {
|
|
15
18
|
generateCopilotMcp
|
|
16
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-Y2XH4E5R.js";
|
|
17
20
|
import {
|
|
18
21
|
generateCursorMcp
|
|
19
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-TTHBLXOB.js";
|
|
20
23
|
import {
|
|
21
24
|
generateGeminiCliMcp
|
|
22
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-IXCMY24P.js";
|
|
23
26
|
import {
|
|
24
|
-
|
|
25
|
-
} from "./chunk-
|
|
27
|
+
generateJunieMcp
|
|
28
|
+
} from "./chunk-Y26DXTAT.js";
|
|
26
29
|
import {
|
|
30
|
+
ALL_TOOL_TARGETS,
|
|
27
31
|
RulesyncTargetsSchema,
|
|
28
32
|
ToolTargetSchema,
|
|
29
|
-
ToolTargetsSchema
|
|
30
|
-
|
|
33
|
+
ToolTargetsSchema,
|
|
34
|
+
isToolTarget
|
|
35
|
+
} from "./chunk-USKQYIZ2.js";
|
|
31
36
|
|
|
32
37
|
// src/cli/index.ts
|
|
33
38
|
import { Command } from "commander";
|
|
@@ -49,19 +54,11 @@ function getDefaultConfig() {
|
|
|
49
54
|
claudecode: ".",
|
|
50
55
|
roo: ".roo/rules",
|
|
51
56
|
geminicli: ".gemini/memories",
|
|
52
|
-
kiro: ".kiro/steering"
|
|
57
|
+
kiro: ".kiro/steering",
|
|
58
|
+
junie: "."
|
|
53
59
|
},
|
|
54
60
|
watchEnabled: false,
|
|
55
|
-
defaultTargets:
|
|
56
|
-
"augmentcode",
|
|
57
|
-
"copilot",
|
|
58
|
-
"cursor",
|
|
59
|
-
"cline",
|
|
60
|
-
"claudecode",
|
|
61
|
-
"roo",
|
|
62
|
-
"geminicli",
|
|
63
|
-
"kiro"
|
|
64
|
-
]
|
|
61
|
+
defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy")
|
|
65
62
|
};
|
|
66
63
|
}
|
|
67
64
|
function resolveTargets(targets, config) {
|
|
@@ -89,32 +86,770 @@ globs: []
|
|
|
89
86
|
Add your rules here.
|
|
90
87
|
`;
|
|
91
88
|
}
|
|
92
|
-
async function addCommand(filename) {
|
|
93
|
-
try {
|
|
94
|
-
const config = getDefaultConfig();
|
|
95
|
-
const sanitizedFilename = sanitizeFilename(filename);
|
|
96
|
-
const rulesDir = config.aiRulesDir;
|
|
97
|
-
const filePath = path.join(rulesDir, `${sanitizedFilename}.md`);
|
|
98
|
-
await mkdir(rulesDir, { recursive: true });
|
|
99
|
-
const template = generateRuleTemplate(sanitizedFilename);
|
|
100
|
-
await writeFile(filePath, template, "utf8");
|
|
101
|
-
console.log(`\u2705 Created rule file: ${filePath}`);
|
|
102
|
-
console.log(`\u{1F4DD} Edit the file to customize your rules.`);
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.error(
|
|
105
|
-
`\u274C Failed to create rule file: ${error instanceof Error ? error.message : String(error)}`
|
|
106
|
-
);
|
|
107
|
-
process.exit(3);
|
|
89
|
+
async function addCommand(filename) {
|
|
90
|
+
try {
|
|
91
|
+
const config = getDefaultConfig();
|
|
92
|
+
const sanitizedFilename = sanitizeFilename(filename);
|
|
93
|
+
const rulesDir = config.aiRulesDir;
|
|
94
|
+
const filePath = path.join(rulesDir, `${sanitizedFilename}.md`);
|
|
95
|
+
await mkdir(rulesDir, { recursive: true });
|
|
96
|
+
const template = generateRuleTemplate(sanitizedFilename);
|
|
97
|
+
await writeFile(filePath, template, "utf8");
|
|
98
|
+
console.log(`\u2705 Created rule file: ${filePath}`);
|
|
99
|
+
console.log(`\u{1F4DD} Edit the file to customize your rules.`);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
console.error(
|
|
102
|
+
`\u274C Failed to create rule file: ${error instanceof Error ? error.message : String(error)}`
|
|
103
|
+
);
|
|
104
|
+
process.exit(3);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/cli/commands/config.ts
|
|
109
|
+
import { writeFileSync } from "fs";
|
|
110
|
+
import path2 from "path";
|
|
111
|
+
|
|
112
|
+
// src/types/claudecode.ts
|
|
113
|
+
import { z } from "zod/mini";
|
|
114
|
+
var ClaudeSettingsSchema = z.looseObject({
|
|
115
|
+
permissions: z._default(
|
|
116
|
+
z.looseObject({
|
|
117
|
+
deny: z._default(z.array(z.string()), [])
|
|
118
|
+
}),
|
|
119
|
+
{ deny: [] }
|
|
120
|
+
)
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// src/types/config.ts
|
|
124
|
+
import { z as z2 } from "zod/mini";
|
|
125
|
+
var ConfigSchema = z2.object({
|
|
126
|
+
aiRulesDir: z2.string(),
|
|
127
|
+
outputPaths: z2.record(ToolTargetSchema, z2.string()),
|
|
128
|
+
watchEnabled: z2.boolean(),
|
|
129
|
+
defaultTargets: ToolTargetsSchema
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// src/types/config-options.ts
|
|
133
|
+
import { z as z3 } from "zod/mini";
|
|
134
|
+
var OutputPathsSchema = z3.object({
|
|
135
|
+
augmentcode: z3.optional(z3.string()),
|
|
136
|
+
"augmentcode-legacy": z3.optional(z3.string()),
|
|
137
|
+
copilot: z3.optional(z3.string()),
|
|
138
|
+
cursor: z3.optional(z3.string()),
|
|
139
|
+
cline: z3.optional(z3.string()),
|
|
140
|
+
claudecode: z3.optional(z3.string()),
|
|
141
|
+
roo: z3.optional(z3.string()),
|
|
142
|
+
geminicli: z3.optional(z3.string()),
|
|
143
|
+
kiro: z3.optional(z3.string()),
|
|
144
|
+
junie: z3.optional(z3.string())
|
|
145
|
+
});
|
|
146
|
+
var ConfigOptionsSchema = z3.object({
|
|
147
|
+
aiRulesDir: z3.optional(z3.string()),
|
|
148
|
+
outputPaths: z3.optional(OutputPathsSchema),
|
|
149
|
+
watchEnabled: z3.optional(z3.boolean()),
|
|
150
|
+
defaultTargets: z3.optional(ToolTargetsSchema),
|
|
151
|
+
targets: z3.optional(z3.array(ToolTargetSchema)),
|
|
152
|
+
exclude: z3.optional(z3.array(ToolTargetSchema)),
|
|
153
|
+
verbose: z3.optional(z3.boolean()),
|
|
154
|
+
delete: z3.optional(z3.boolean()),
|
|
155
|
+
baseDir: z3.optional(z3.union([z3.string(), z3.array(z3.string())])),
|
|
156
|
+
watch: z3.optional(
|
|
157
|
+
z3.object({
|
|
158
|
+
enabled: z3.optional(z3.boolean()),
|
|
159
|
+
interval: z3.optional(z3.number()),
|
|
160
|
+
ignore: z3.optional(z3.array(z3.string()))
|
|
161
|
+
})
|
|
162
|
+
)
|
|
163
|
+
});
|
|
164
|
+
var MergedConfigSchema = z3.object({
|
|
165
|
+
aiRulesDir: z3.string(),
|
|
166
|
+
outputPaths: z3.record(ToolTargetSchema, z3.string()),
|
|
167
|
+
watchEnabled: z3.boolean(),
|
|
168
|
+
defaultTargets: ToolTargetsSchema,
|
|
169
|
+
targets: z3.optional(z3.array(ToolTargetSchema)),
|
|
170
|
+
exclude: z3.optional(z3.array(ToolTargetSchema)),
|
|
171
|
+
verbose: z3.optional(z3.boolean()),
|
|
172
|
+
delete: z3.optional(z3.boolean()),
|
|
173
|
+
baseDir: z3.optional(z3.union([z3.string(), z3.array(z3.string())])),
|
|
174
|
+
configPath: z3.optional(z3.string()),
|
|
175
|
+
watch: z3.optional(
|
|
176
|
+
z3.object({
|
|
177
|
+
enabled: z3.optional(z3.boolean()),
|
|
178
|
+
interval: z3.optional(z3.number()),
|
|
179
|
+
ignore: z3.optional(z3.array(z3.string()))
|
|
180
|
+
})
|
|
181
|
+
)
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// src/types/mcp.ts
|
|
185
|
+
import { z as z4 } from "zod/mini";
|
|
186
|
+
var McpTransportTypeSchema = z4.enum(["stdio", "sse", "http"]);
|
|
187
|
+
var McpServerBaseSchema = z4.object({
|
|
188
|
+
command: z4.optional(z4.string()),
|
|
189
|
+
args: z4.optional(z4.array(z4.string())),
|
|
190
|
+
url: z4.optional(z4.string()),
|
|
191
|
+
httpUrl: z4.optional(z4.string()),
|
|
192
|
+
env: z4.optional(z4.record(z4.string(), z4.string())),
|
|
193
|
+
disabled: z4.optional(z4.boolean()),
|
|
194
|
+
networkTimeout: z4.optional(z4.number()),
|
|
195
|
+
timeout: z4.optional(z4.number()),
|
|
196
|
+
trust: z4.optional(z4.boolean()),
|
|
197
|
+
cwd: z4.optional(z4.string()),
|
|
198
|
+
transport: z4.optional(McpTransportTypeSchema),
|
|
199
|
+
type: z4.optional(z4.enum(["sse", "streamable-http"])),
|
|
200
|
+
alwaysAllow: z4.optional(z4.array(z4.string())),
|
|
201
|
+
tools: z4.optional(z4.array(z4.string())),
|
|
202
|
+
kiroAutoApprove: z4.optional(z4.array(z4.string())),
|
|
203
|
+
kiroAutoBlock: z4.optional(z4.array(z4.string()))
|
|
204
|
+
});
|
|
205
|
+
var RulesyncMcpServerSchema = z4.extend(McpServerBaseSchema, {
|
|
206
|
+
targets: z4.optional(RulesyncTargetsSchema)
|
|
207
|
+
});
|
|
208
|
+
var McpConfigSchema = z4.object({
|
|
209
|
+
mcpServers: z4.record(z4.string(), McpServerBaseSchema)
|
|
210
|
+
});
|
|
211
|
+
var RulesyncMcpConfigSchema = z4.object({
|
|
212
|
+
mcpServers: z4.record(z4.string(), RulesyncMcpServerSchema)
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// src/types/rules.ts
|
|
216
|
+
import { z as z5 } from "zod/mini";
|
|
217
|
+
var RuleFrontmatterSchema = z5.object({
|
|
218
|
+
root: z5.boolean(),
|
|
219
|
+
targets: RulesyncTargetsSchema,
|
|
220
|
+
description: z5.string(),
|
|
221
|
+
globs: z5.array(z5.string()),
|
|
222
|
+
cursorRuleType: z5.optional(z5.enum(["always", "manual", "specificFiles", "intelligently"])),
|
|
223
|
+
tags: z5.optional(z5.array(z5.string()))
|
|
224
|
+
});
|
|
225
|
+
var ParsedRuleSchema = z5.object({
|
|
226
|
+
frontmatter: RuleFrontmatterSchema,
|
|
227
|
+
content: z5.string(),
|
|
228
|
+
filename: z5.string(),
|
|
229
|
+
filepath: z5.string()
|
|
230
|
+
});
|
|
231
|
+
var GeneratedOutputSchema = z5.object({
|
|
232
|
+
tool: ToolTargetSchema,
|
|
233
|
+
filepath: z5.string(),
|
|
234
|
+
content: z5.string()
|
|
235
|
+
});
|
|
236
|
+
var GenerateOptionsSchema = z5.object({
|
|
237
|
+
targetTools: z5.optional(ToolTargetsSchema),
|
|
238
|
+
outputDir: z5.optional(z5.string()),
|
|
239
|
+
watch: z5.optional(z5.boolean())
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// src/utils/config-loader.ts
|
|
243
|
+
import { loadConfig as loadC12Config } from "c12";
|
|
244
|
+
import { $ZodError } from "zod/v4/core";
|
|
245
|
+
var MODULE_NAME = "rulesync";
|
|
246
|
+
async function loadConfig(options = {}) {
|
|
247
|
+
const defaultConfig = getDefaultConfig();
|
|
248
|
+
if (options.noConfig) {
|
|
249
|
+
return {
|
|
250
|
+
config: defaultConfig,
|
|
251
|
+
isEmpty: true
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
const loadOptions = {
|
|
256
|
+
name: MODULE_NAME,
|
|
257
|
+
cwd: options.cwd || process.cwd(),
|
|
258
|
+
rcFile: false,
|
|
259
|
+
// Disable rc file lookup
|
|
260
|
+
configFile: "rulesync",
|
|
261
|
+
// Will look for rulesync.jsonc, rulesync.ts, etc.
|
|
262
|
+
defaults: defaultConfig
|
|
263
|
+
};
|
|
264
|
+
if (options.configPath) {
|
|
265
|
+
loadOptions.configFile = options.configPath;
|
|
266
|
+
}
|
|
267
|
+
const { config, configFile } = await loadC12Config(loadOptions);
|
|
268
|
+
if (!config || Object.keys(config).length === 0) {
|
|
269
|
+
return {
|
|
270
|
+
config: defaultConfig,
|
|
271
|
+
isEmpty: true
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
ConfigOptionsSchema.parse(config);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
if (error instanceof $ZodError) {
|
|
278
|
+
const issues = error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
|
|
279
|
+
throw new Error(`Invalid configuration in ${configFile}:
|
|
280
|
+
${issues}`);
|
|
281
|
+
}
|
|
282
|
+
throw error;
|
|
283
|
+
}
|
|
284
|
+
const processedConfig = postProcessConfig(config);
|
|
285
|
+
const result = {
|
|
286
|
+
config: processedConfig,
|
|
287
|
+
isEmpty: false
|
|
288
|
+
};
|
|
289
|
+
if (configFile) {
|
|
290
|
+
result.filepath = configFile;
|
|
291
|
+
}
|
|
292
|
+
return result;
|
|
293
|
+
} catch (error) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
`Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
function postProcessConfig(config) {
|
|
300
|
+
const processed = { ...config };
|
|
301
|
+
if (processed.baseDir && !Array.isArray(processed.baseDir)) {
|
|
302
|
+
processed.baseDir = [processed.baseDir];
|
|
303
|
+
}
|
|
304
|
+
if (config.targets || config.exclude) {
|
|
305
|
+
const baseTargets = config.targets || processed.defaultTargets;
|
|
306
|
+
if (config.exclude && config.exclude.length > 0) {
|
|
307
|
+
processed.defaultTargets = baseTargets.filter(
|
|
308
|
+
(target) => config.exclude && !config.exclude.includes(target)
|
|
309
|
+
);
|
|
310
|
+
} else {
|
|
311
|
+
processed.defaultTargets = baseTargets;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return processed;
|
|
315
|
+
}
|
|
316
|
+
function generateMinimalConfig(options) {
|
|
317
|
+
if (!options || Object.keys(options).length === 0) {
|
|
318
|
+
return generateSampleConfig();
|
|
319
|
+
}
|
|
320
|
+
const lines = ["{"];
|
|
321
|
+
if (options.targets || options.exclude) {
|
|
322
|
+
lines.push(` // Available tools: ${ALL_TOOL_TARGETS.join(", ")}`);
|
|
323
|
+
}
|
|
324
|
+
if (options.targets) {
|
|
325
|
+
lines.push(` "targets": ${JSON.stringify(options.targets)}`);
|
|
326
|
+
}
|
|
327
|
+
if (options.exclude) {
|
|
328
|
+
const comma = lines.length > 1 ? "," : "";
|
|
329
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
330
|
+
lines.push(` "exclude": ${JSON.stringify(options.exclude)}`);
|
|
331
|
+
}
|
|
332
|
+
if (options.aiRulesDir) {
|
|
333
|
+
const comma = lines.length > 1 ? "," : "";
|
|
334
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
335
|
+
lines.push(` "aiRulesDir": "${options.aiRulesDir}"`);
|
|
336
|
+
}
|
|
337
|
+
if (options.outputPaths) {
|
|
338
|
+
const comma = lines.length > 1 ? "," : "";
|
|
339
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
340
|
+
lines.push(
|
|
341
|
+
` "outputPaths": ${JSON.stringify(options.outputPaths, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n")}`
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
if (options.baseDir) {
|
|
345
|
+
const comma = lines.length > 1 ? "," : "";
|
|
346
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
347
|
+
lines.push(` "baseDir": ${JSON.stringify(options.baseDir)}`);
|
|
348
|
+
}
|
|
349
|
+
if (options.delete !== void 0) {
|
|
350
|
+
const comma = lines.length > 1 ? "," : "";
|
|
351
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
352
|
+
lines.push(` "delete": ${options.delete}`);
|
|
353
|
+
}
|
|
354
|
+
if (options.verbose !== void 0) {
|
|
355
|
+
const comma = lines.length > 1 ? "," : "";
|
|
356
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
357
|
+
lines.push(` "verbose": ${options.verbose}`);
|
|
358
|
+
}
|
|
359
|
+
if (options.watch) {
|
|
360
|
+
const comma = lines.length > 1 ? "," : "";
|
|
361
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
362
|
+
lines.push(
|
|
363
|
+
` "watch": ${JSON.stringify(options.watch, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n")}`
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
lines.push("}");
|
|
367
|
+
return lines.join("\n");
|
|
368
|
+
}
|
|
369
|
+
function generateSampleConfig(options) {
|
|
370
|
+
const targets = options?.targets || ALL_TOOL_TARGETS;
|
|
371
|
+
const excludeValue = options?.exclude ? JSON.stringify(options.exclude) : null;
|
|
372
|
+
const aiRulesDir = options?.aiRulesDir || null;
|
|
373
|
+
const baseDir = options?.baseDir || null;
|
|
374
|
+
const deleteFlag = options?.delete || false;
|
|
375
|
+
const verbose = options?.verbose !== void 0 ? options.verbose : true;
|
|
376
|
+
return `{
|
|
377
|
+
// List of tools to generate configurations for
|
|
378
|
+
// Available: ${ALL_TOOL_TARGETS.join(", ")}
|
|
379
|
+
"targets": ${JSON.stringify(targets)},
|
|
380
|
+
|
|
381
|
+
// Tools to exclude from generation (overrides targets)
|
|
382
|
+
${excludeValue ? `"exclude": ${excludeValue},` : '// "exclude": ["roo"],'}
|
|
383
|
+
${aiRulesDir ? `
|
|
384
|
+
// Directory containing AI rule files
|
|
385
|
+
"aiRulesDir": "${aiRulesDir}",` : ""}
|
|
386
|
+
|
|
387
|
+
// Custom output paths for specific tools
|
|
388
|
+
"outputPaths": {
|
|
389
|
+
"copilot": ".github/copilot-instructions.md"
|
|
390
|
+
},
|
|
391
|
+
${baseDir ? `
|
|
392
|
+
// Base directory for generation
|
|
393
|
+
"baseDir": "${baseDir}",` : `
|
|
394
|
+
// Base directory or directories for generation
|
|
395
|
+
// "baseDir": "./packages",
|
|
396
|
+
// "baseDir": ["./packages/frontend", "./packages/backend"],`}
|
|
397
|
+
|
|
398
|
+
// Delete existing files before generating
|
|
399
|
+
"delete": ${deleteFlag},
|
|
400
|
+
|
|
401
|
+
// Enable verbose output
|
|
402
|
+
"verbose": ${verbose},
|
|
403
|
+
|
|
404
|
+
// Watch configuration
|
|
405
|
+
"watch": {
|
|
406
|
+
"enabled": false,
|
|
407
|
+
"interval": 1000,
|
|
408
|
+
"ignore": ["node_modules/**", "dist/**"]
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
`;
|
|
412
|
+
}
|
|
413
|
+
function mergeWithCliOptions(config, cliOptions) {
|
|
414
|
+
const merged = { ...config };
|
|
415
|
+
if (cliOptions.verbose !== void 0) {
|
|
416
|
+
merged.verbose = cliOptions.verbose;
|
|
417
|
+
}
|
|
418
|
+
if (cliOptions.delete !== void 0) {
|
|
419
|
+
merged.delete = cliOptions.delete;
|
|
420
|
+
}
|
|
421
|
+
if (cliOptions.baseDirs && cliOptions.baseDirs.length > 0) {
|
|
422
|
+
merged.baseDir = cliOptions.baseDirs;
|
|
423
|
+
}
|
|
424
|
+
if (cliOptions.tools && cliOptions.tools.length > 0) {
|
|
425
|
+
merged.defaultTargets = cliOptions.tools;
|
|
426
|
+
merged.exclude = void 0;
|
|
427
|
+
}
|
|
428
|
+
return merged;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/utils/file.ts
|
|
432
|
+
import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
|
|
433
|
+
import { dirname, join as join2 } from "path";
|
|
434
|
+
async function ensureDir(dirPath) {
|
|
435
|
+
try {
|
|
436
|
+
await stat(dirPath);
|
|
437
|
+
} catch {
|
|
438
|
+
await mkdir2(dirPath, { recursive: true });
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
async function readFileContent(filepath) {
|
|
442
|
+
return readFile(filepath, "utf-8");
|
|
443
|
+
}
|
|
444
|
+
async function writeFileContent(filepath, content) {
|
|
445
|
+
await ensureDir(dirname(filepath));
|
|
446
|
+
await writeFile2(filepath, content, "utf-8");
|
|
447
|
+
}
|
|
448
|
+
async function fileExists(filepath) {
|
|
449
|
+
try {
|
|
450
|
+
await stat(filepath);
|
|
451
|
+
return true;
|
|
452
|
+
} catch {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
async function findFiles(dir, extension = ".md") {
|
|
457
|
+
try {
|
|
458
|
+
const files = await readdir(dir);
|
|
459
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => join2(dir, file));
|
|
460
|
+
} catch {
|
|
461
|
+
return [];
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
async function removeDirectory(dirPath) {
|
|
465
|
+
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
466
|
+
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
467
|
+
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
try {
|
|
471
|
+
if (await fileExists(dirPath)) {
|
|
472
|
+
await rm(dirPath, { recursive: true, force: true });
|
|
473
|
+
}
|
|
474
|
+
} catch (error) {
|
|
475
|
+
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
async function removeFile(filepath) {
|
|
479
|
+
try {
|
|
480
|
+
if (await fileExists(filepath)) {
|
|
481
|
+
await rm(filepath);
|
|
482
|
+
}
|
|
483
|
+
} catch (error) {
|
|
484
|
+
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
async function removeClaudeGeneratedFiles() {
|
|
488
|
+
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
489
|
+
for (const fileOrDir of filesToRemove) {
|
|
490
|
+
if (fileOrDir.endsWith("/memories")) {
|
|
491
|
+
await removeDirectory(fileOrDir);
|
|
492
|
+
} else {
|
|
493
|
+
await removeFile(fileOrDir);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// src/utils/rules.ts
|
|
499
|
+
function isToolSpecificRule(rule, targetTool) {
|
|
500
|
+
const filename = rule.filename;
|
|
501
|
+
const toolPatterns = {
|
|
502
|
+
"augmentcode-legacy": /^specification-augmentcode-legacy-/i,
|
|
503
|
+
augmentcode: /^specification-augmentcode-/i,
|
|
504
|
+
copilot: /^specification-copilot-/i,
|
|
505
|
+
cursor: /^specification-cursor-/i,
|
|
506
|
+
cline: /^specification-cline-/i,
|
|
507
|
+
claudecode: /^specification-claudecode-/i,
|
|
508
|
+
roo: /^specification-roo-/i,
|
|
509
|
+
geminicli: /^specification-geminicli-/i,
|
|
510
|
+
kiro: /^specification-kiro-/i
|
|
511
|
+
};
|
|
512
|
+
for (const [tool, pattern] of Object.entries(toolPatterns)) {
|
|
513
|
+
if (pattern.test(filename)) {
|
|
514
|
+
return tool === targetTool;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return true;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// src/cli/commands/config.ts
|
|
521
|
+
async function configCommand(options = {}) {
|
|
522
|
+
if (options.init) {
|
|
523
|
+
await initConfig(options);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
await showConfig();
|
|
527
|
+
}
|
|
528
|
+
async function showConfig() {
|
|
529
|
+
console.log("Loading configuration...\n");
|
|
530
|
+
try {
|
|
531
|
+
const result = await loadConfig();
|
|
532
|
+
if (result.isEmpty) {
|
|
533
|
+
console.log("No configuration file found. Using default configuration.\n");
|
|
534
|
+
} else {
|
|
535
|
+
console.log(`Configuration loaded from: ${result.filepath}
|
|
536
|
+
`);
|
|
537
|
+
}
|
|
538
|
+
console.log("Current configuration:");
|
|
539
|
+
console.log("=====================");
|
|
540
|
+
const config = result.config;
|
|
541
|
+
console.log(`
|
|
542
|
+
AI Rules Directory: ${config.aiRulesDir}`);
|
|
543
|
+
console.log(`
|
|
544
|
+
Default Targets: ${config.defaultTargets.join(", ")}`);
|
|
545
|
+
if (config.exclude && config.exclude.length > 0) {
|
|
546
|
+
console.log(`Excluded Targets: ${config.exclude.join(", ")}`);
|
|
547
|
+
}
|
|
548
|
+
console.log("\nOutput Paths:");
|
|
549
|
+
for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
|
|
550
|
+
console.log(` ${tool}: ${outputPath}`);
|
|
551
|
+
}
|
|
552
|
+
if (config.baseDir) {
|
|
553
|
+
const dirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
|
|
554
|
+
console.log(`
|
|
555
|
+
Base Directories: ${dirs.join(", ")}`);
|
|
556
|
+
}
|
|
557
|
+
console.log(`
|
|
558
|
+
Verbose: ${config.verbose || false}`);
|
|
559
|
+
console.log(`Delete before generate: ${config.delete || false}`);
|
|
560
|
+
if (config.watch) {
|
|
561
|
+
console.log("\nWatch Configuration:");
|
|
562
|
+
console.log(` Enabled: ${config.watch.enabled || false}`);
|
|
563
|
+
if (config.watch.interval) {
|
|
564
|
+
console.log(` Interval: ${config.watch.interval}ms`);
|
|
565
|
+
}
|
|
566
|
+
if (config.watch.ignore && config.watch.ignore.length > 0) {
|
|
567
|
+
console.log(` Ignore patterns: ${config.watch.ignore.join(", ")}`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
console.log("\nTip: Use 'rulesync config init' to create a configuration file.");
|
|
571
|
+
} catch (error) {
|
|
572
|
+
console.error(
|
|
573
|
+
"\u274C Failed to load configuration:",
|
|
574
|
+
error instanceof Error ? error.message : String(error)
|
|
575
|
+
);
|
|
576
|
+
process.exit(1);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
var FORMAT_CONFIG = {
|
|
580
|
+
jsonc: {
|
|
581
|
+
filename: "rulesync.jsonc",
|
|
582
|
+
generator: generateJsoncConfig
|
|
583
|
+
},
|
|
584
|
+
ts: {
|
|
585
|
+
filename: "rulesync.ts",
|
|
586
|
+
generator: generateTsConfig
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
async function initConfig(options) {
|
|
590
|
+
const validFormats = Object.keys(FORMAT_CONFIG);
|
|
591
|
+
const selectedFormat = options.format || "jsonc";
|
|
592
|
+
if (!validFormats.includes(selectedFormat)) {
|
|
593
|
+
console.error(
|
|
594
|
+
`\u274C Invalid format: ${selectedFormat}. Valid formats are: ${validFormats.join(", ")}`
|
|
595
|
+
);
|
|
596
|
+
process.exit(1);
|
|
597
|
+
}
|
|
598
|
+
const formatConfig = FORMAT_CONFIG[selectedFormat];
|
|
599
|
+
const filename = formatConfig.filename;
|
|
600
|
+
const configOptions = {};
|
|
601
|
+
if (options.targets) {
|
|
602
|
+
const targets = options.targets.split(",").map((t) => t.trim());
|
|
603
|
+
const validTargets = [];
|
|
604
|
+
for (const target of targets) {
|
|
605
|
+
const result = ToolTargetSchema.safeParse(target);
|
|
606
|
+
if (result.success) {
|
|
607
|
+
validTargets.push(result.data);
|
|
608
|
+
} else {
|
|
609
|
+
console.error(`\u274C Invalid target: ${target}`);
|
|
610
|
+
process.exit(1);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
configOptions.targets = validTargets;
|
|
614
|
+
}
|
|
615
|
+
if (options.exclude) {
|
|
616
|
+
const excludes = options.exclude.split(",").map((t) => t.trim());
|
|
617
|
+
const validExcludes = [];
|
|
618
|
+
for (const exclude of excludes) {
|
|
619
|
+
const result = ToolTargetSchema.safeParse(exclude);
|
|
620
|
+
if (result.success) {
|
|
621
|
+
validExcludes.push(result.data);
|
|
622
|
+
} else {
|
|
623
|
+
console.error(`\u274C Invalid exclude target: ${exclude}`);
|
|
624
|
+
process.exit(1);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
configOptions.exclude = validExcludes;
|
|
628
|
+
}
|
|
629
|
+
if (options.aiRulesDir) {
|
|
630
|
+
configOptions.aiRulesDir = options.aiRulesDir;
|
|
631
|
+
}
|
|
632
|
+
if (options.baseDir) {
|
|
633
|
+
configOptions.baseDir = options.baseDir;
|
|
634
|
+
}
|
|
635
|
+
if (options.verbose !== void 0) {
|
|
636
|
+
configOptions.verbose = options.verbose;
|
|
637
|
+
}
|
|
638
|
+
if (options.delete !== void 0) {
|
|
639
|
+
configOptions.delete = options.delete;
|
|
640
|
+
}
|
|
641
|
+
const content = formatConfig.generator(configOptions);
|
|
642
|
+
const filepath = path2.join(process.cwd(), filename);
|
|
643
|
+
try {
|
|
644
|
+
const fs2 = await import("fs/promises");
|
|
645
|
+
await fs2.access(filepath);
|
|
646
|
+
console.error(`\u274C Configuration file already exists: ${filepath}`);
|
|
647
|
+
console.log("Remove the existing file or choose a different format.");
|
|
648
|
+
process.exit(1);
|
|
649
|
+
} catch {
|
|
650
|
+
}
|
|
651
|
+
try {
|
|
652
|
+
writeFileSync(filepath, content, "utf-8");
|
|
653
|
+
console.log(`\u2705 Created configuration file: ${filepath}`);
|
|
654
|
+
console.log("\nYou can now customize the configuration to fit your needs.");
|
|
655
|
+
console.log("Run 'rulesync generate' to use the new configuration.");
|
|
656
|
+
} catch (error) {
|
|
657
|
+
console.error(
|
|
658
|
+
`\u274C Failed to create configuration file: ${error instanceof Error ? error.message : String(error)}`
|
|
659
|
+
);
|
|
660
|
+
process.exit(1);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function generateJsoncConfig(options) {
|
|
664
|
+
if (options && Object.keys(options).length > 0) {
|
|
665
|
+
return generateMinimalConfig(options);
|
|
666
|
+
}
|
|
667
|
+
return generateSampleConfig(options);
|
|
668
|
+
}
|
|
669
|
+
function generateTsConfig(options) {
|
|
670
|
+
if (!options || Object.keys(options).length === 0) {
|
|
671
|
+
return `import type { ConfigOptions } from "rulesync";
|
|
672
|
+
|
|
673
|
+
const config: ConfigOptions = {
|
|
674
|
+
// List of tools to generate configurations for
|
|
675
|
+
// Available: ${ALL_TOOL_TARGETS.join(", ")}
|
|
676
|
+
targets: ${JSON.stringify(ALL_TOOL_TARGETS)},
|
|
677
|
+
|
|
678
|
+
// Custom output paths for specific tools
|
|
679
|
+
// outputPaths: {
|
|
680
|
+
// copilot: ".github/copilot-instructions.md",
|
|
681
|
+
// },
|
|
682
|
+
|
|
683
|
+
// Delete existing files before generating
|
|
684
|
+
// delete: false,
|
|
685
|
+
|
|
686
|
+
// Enable verbose output
|
|
687
|
+
verbose: true,
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
export default config;`;
|
|
691
|
+
}
|
|
692
|
+
const configLines = [];
|
|
693
|
+
if (options.targets) {
|
|
694
|
+
configLines.push(` targets: ${JSON.stringify(options.targets)}`);
|
|
695
|
+
}
|
|
696
|
+
if (options.exclude) {
|
|
697
|
+
configLines.push(` exclude: ${JSON.stringify(options.exclude)}`);
|
|
698
|
+
}
|
|
699
|
+
if (options.aiRulesDir) {
|
|
700
|
+
configLines.push(` aiRulesDir: "${options.aiRulesDir}"`);
|
|
701
|
+
}
|
|
702
|
+
if (options.outputPaths) {
|
|
703
|
+
const pathsStr = JSON.stringify(options.outputPaths, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
|
|
704
|
+
configLines.push(` outputPaths: ${pathsStr}`);
|
|
705
|
+
}
|
|
706
|
+
if (options.baseDir) {
|
|
707
|
+
configLines.push(` baseDir: ${JSON.stringify(options.baseDir)}`);
|
|
708
|
+
}
|
|
709
|
+
if (options.delete !== void 0) {
|
|
710
|
+
configLines.push(` delete: ${options.delete}`);
|
|
711
|
+
}
|
|
712
|
+
if (options.verbose !== void 0) {
|
|
713
|
+
configLines.push(` verbose: ${options.verbose}`);
|
|
714
|
+
}
|
|
715
|
+
if (options.watch) {
|
|
716
|
+
const watchStr = JSON.stringify(options.watch, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
|
|
717
|
+
configLines.push(` watch: ${watchStr}`);
|
|
718
|
+
}
|
|
719
|
+
const configContent = `import type { ConfigOptions } from "rulesync";
|
|
720
|
+
|
|
721
|
+
const config: ConfigOptions = {
|
|
722
|
+
${configLines.join(",\n")},
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
export default config;
|
|
726
|
+
`;
|
|
727
|
+
return configContent;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// src/cli/commands/generate.ts
|
|
731
|
+
import { join as join15 } from "path";
|
|
732
|
+
|
|
733
|
+
// src/generators/ignore/augmentcode.ts
|
|
734
|
+
import { join as join3 } from "path";
|
|
735
|
+
|
|
736
|
+
// src/generators/ignore/shared-helpers.ts
|
|
737
|
+
function extractIgnorePatternsFromRules(rules) {
|
|
738
|
+
const patterns = [];
|
|
739
|
+
for (const rule of rules) {
|
|
740
|
+
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
741
|
+
for (const glob of rule.frontmatter.globs) {
|
|
742
|
+
if (shouldExcludeFromAI(glob)) {
|
|
743
|
+
patterns.push(`# Exclude: ${rule.frontmatter.description}`);
|
|
744
|
+
patterns.push(glob);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
const contentPatterns = extractIgnorePatternsFromContent(rule.content);
|
|
749
|
+
patterns.push(...contentPatterns);
|
|
750
|
+
}
|
|
751
|
+
return patterns;
|
|
752
|
+
}
|
|
753
|
+
function shouldExcludeFromAI(glob) {
|
|
754
|
+
const excludePatterns = [
|
|
755
|
+
// Large generated files that slow indexing
|
|
756
|
+
"**/assets/generated/**",
|
|
757
|
+
"**/public/build/**",
|
|
758
|
+
// Test fixtures with potentially sensitive data
|
|
759
|
+
"**/tests/fixtures/**",
|
|
760
|
+
"**/test/fixtures/**",
|
|
761
|
+
"**/*.fixture.*",
|
|
762
|
+
// Build outputs that provide little value for AI context
|
|
763
|
+
"**/dist/**",
|
|
764
|
+
"**/build/**",
|
|
765
|
+
"**/coverage/**",
|
|
766
|
+
// Configuration that might contain sensitive data
|
|
767
|
+
"**/config/production/**",
|
|
768
|
+
"**/config/secrets/**",
|
|
769
|
+
"**/config/prod/**",
|
|
770
|
+
"**/deploy/prod/**",
|
|
771
|
+
"**/*.prod.*",
|
|
772
|
+
// Internal documentation that might be sensitive
|
|
773
|
+
"**/internal/**",
|
|
774
|
+
"**/internal-docs/**",
|
|
775
|
+
"**/proprietary/**",
|
|
776
|
+
"**/personal-notes/**",
|
|
777
|
+
"**/private/**",
|
|
778
|
+
"**/confidential/**"
|
|
779
|
+
];
|
|
780
|
+
return excludePatterns.some((pattern) => {
|
|
781
|
+
const regex = new RegExp(pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*"));
|
|
782
|
+
return regex.test(glob);
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
function extractIgnorePatternsFromContent(content) {
|
|
786
|
+
const patterns = [];
|
|
787
|
+
const lines = content.split("\n");
|
|
788
|
+
for (const line of lines) {
|
|
789
|
+
const trimmed = line.trim();
|
|
790
|
+
if (trimmed.startsWith("# IGNORE:") || trimmed.startsWith("# aiignore:")) {
|
|
791
|
+
const pattern = trimmed.replace(/^# (IGNORE|aiignore):\s*/, "").trim();
|
|
792
|
+
if (pattern) {
|
|
793
|
+
patterns.push(pattern);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
if (trimmed.startsWith("# AUGMENT_IGNORE:") || trimmed.startsWith("# augmentignore:")) {
|
|
797
|
+
const pattern = trimmed.replace(/^# (AUGMENT_IGNORE|augmentignore):\s*/, "").trim();
|
|
798
|
+
if (pattern) {
|
|
799
|
+
patterns.push(pattern);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
if (trimmed.startsWith("# AUGMENT_INCLUDE:") || trimmed.startsWith("# augmentinclude:")) {
|
|
803
|
+
const pattern = trimmed.replace(/^# (AUGMENT_INCLUDE|augmentinclude):\s*/, "").trim();
|
|
804
|
+
if (pattern) {
|
|
805
|
+
patterns.push(`!${pattern}`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
if (trimmed.includes("exclude") || trimmed.includes("ignore")) {
|
|
809
|
+
const matches = trimmed.match(/['"`]([^'"`]+\.(log|tmp|cache|temp))['"`]/g);
|
|
810
|
+
if (matches) {
|
|
811
|
+
patterns.push(...matches.map((m) => m.replace(/['"`]/g, "")));
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
return patterns;
|
|
816
|
+
}
|
|
817
|
+
function extractAugmentCodeIgnorePatternsFromContent(content) {
|
|
818
|
+
const patterns = [];
|
|
819
|
+
const lines = content.split("\n");
|
|
820
|
+
for (const line of lines) {
|
|
821
|
+
const trimmed = line.trim();
|
|
822
|
+
if (trimmed.startsWith("# AUGMENT_IGNORE:") || trimmed.startsWith("# augmentignore:")) {
|
|
823
|
+
const pattern = trimmed.replace(/^# (AUGMENT_IGNORE|augmentignore):\s*/, "").trim();
|
|
824
|
+
if (pattern) {
|
|
825
|
+
patterns.push(pattern);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
if (trimmed.startsWith("# AUGMENT_INCLUDE:") || trimmed.startsWith("# augmentinclude:")) {
|
|
829
|
+
const pattern = trimmed.replace(/^# (AUGMENT_INCLUDE|augmentinclude):\s*/, "").trim();
|
|
830
|
+
if (pattern) {
|
|
831
|
+
patterns.push(`!${pattern}`);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
if (trimmed.includes("large file") || trimmed.includes("binary") || trimmed.includes("media")) {
|
|
835
|
+
const regex = /['"`]([^'"`]+\.(mp4|avi|zip|tar\.gz|rar|pdf|doc|xlsx))['"`]/g;
|
|
836
|
+
let match;
|
|
837
|
+
while ((match = regex.exec(trimmed)) !== null) {
|
|
838
|
+
if (match[1]) {
|
|
839
|
+
patterns.push(match[1]);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
108
843
|
}
|
|
844
|
+
return patterns;
|
|
109
845
|
}
|
|
110
846
|
|
|
111
847
|
// src/generators/ignore/augmentcode.ts
|
|
112
|
-
import { join as join2 } from "path";
|
|
113
848
|
async function generateAugmentCodeIgnoreFiles(rules, config, baseDir) {
|
|
114
849
|
const outputs = [];
|
|
115
850
|
const augmentignoreContent = generateAugmentignoreContent(rules);
|
|
116
851
|
const outputPath = baseDir || process.cwd();
|
|
117
|
-
const filepath =
|
|
852
|
+
const filepath = join3(outputPath, ".augmentignore");
|
|
118
853
|
outputs.push({
|
|
119
854
|
tool: "augmentcode",
|
|
120
855
|
filepath,
|
|
@@ -217,9 +952,14 @@ function generateAugmentignoreContent(rules) {
|
|
|
217
952
|
""
|
|
218
953
|
);
|
|
219
954
|
const rulePatterns = extractIgnorePatternsFromRules(rules);
|
|
220
|
-
|
|
955
|
+
const augmentPatterns = [];
|
|
956
|
+
for (const rule of rules) {
|
|
957
|
+
augmentPatterns.push(...extractAugmentCodeIgnorePatternsFromContent(rule.content));
|
|
958
|
+
}
|
|
959
|
+
const allPatterns = [...rulePatterns, ...augmentPatterns];
|
|
960
|
+
if (allPatterns.length > 0) {
|
|
221
961
|
lines.push("# Project-specific patterns from rulesync rules");
|
|
222
|
-
lines.push(...
|
|
962
|
+
lines.push(...allPatterns);
|
|
223
963
|
lines.push("");
|
|
224
964
|
}
|
|
225
965
|
lines.push(
|
|
@@ -242,85 +982,162 @@ function generateAugmentignoreContent(rules) {
|
|
|
242
982
|
);
|
|
243
983
|
return lines.join("\n");
|
|
244
984
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
patterns.push(...contentPatterns);
|
|
258
|
-
}
|
|
259
|
-
return patterns;
|
|
260
|
-
}
|
|
261
|
-
function shouldExcludeFromAugmentCode(glob) {
|
|
262
|
-
const excludePatterns = [
|
|
263
|
-
// Large generated files that slow indexing
|
|
264
|
-
"**/assets/generated/**",
|
|
265
|
-
"**/public/build/**",
|
|
266
|
-
// Test fixtures with potentially sensitive data
|
|
267
|
-
"**/tests/fixtures/**",
|
|
268
|
-
"**/test/fixtures/**",
|
|
269
|
-
"**/*.fixture.*",
|
|
270
|
-
// Build outputs that provide little value for AI context
|
|
271
|
-
"**/dist/**",
|
|
272
|
-
"**/build/**",
|
|
273
|
-
"**/coverage/**",
|
|
274
|
-
// Configuration that might contain sensitive data
|
|
275
|
-
"**/config/production/**",
|
|
276
|
-
"**/config/secrets/**",
|
|
277
|
-
"**/deploy/prod/**",
|
|
278
|
-
// Internal documentation
|
|
279
|
-
"**/internal-docs/**",
|
|
280
|
-
"**/proprietary/**",
|
|
281
|
-
"**/personal-notes/**",
|
|
282
|
-
"**/private/**",
|
|
283
|
-
"**/confidential/**"
|
|
284
|
-
];
|
|
285
|
-
return excludePatterns.some((pattern) => {
|
|
286
|
-
const regex = new RegExp(pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*"));
|
|
287
|
-
return regex.test(glob);
|
|
985
|
+
|
|
986
|
+
// src/generators/ignore/junie.ts
|
|
987
|
+
import { join as join4 } from "path";
|
|
988
|
+
async function generateJunieIgnoreFiles(rules, config, baseDir) {
|
|
989
|
+
const outputs = [];
|
|
990
|
+
const aiignoreContent = generateAiignoreContent(rules);
|
|
991
|
+
const outputPath = baseDir || process.cwd();
|
|
992
|
+
const filepath = join4(outputPath, ".aiignore");
|
|
993
|
+
outputs.push({
|
|
994
|
+
tool: "junie",
|
|
995
|
+
filepath,
|
|
996
|
+
content: aiignoreContent
|
|
288
997
|
});
|
|
998
|
+
return outputs;
|
|
289
999
|
}
|
|
290
|
-
function
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
1000
|
+
function generateAiignoreContent(rules) {
|
|
1001
|
+
const lines = [
|
|
1002
|
+
"# Generated by rulesync - JetBrains Junie AI ignore file",
|
|
1003
|
+
"# This file controls which files the AI can access automatically",
|
|
1004
|
+
"# AI must ask before reading or editing matched files/directories",
|
|
1005
|
+
"",
|
|
1006
|
+
"# \u2500\u2500\u2500\u2500\u2500 Source Control Metadata \u2500\u2500\u2500\u2500\u2500",
|
|
1007
|
+
".git/",
|
|
1008
|
+
".svn/",
|
|
1009
|
+
".hg/",
|
|
1010
|
+
".idea/",
|
|
1011
|
+
"*.iml",
|
|
1012
|
+
".vscode/settings.json",
|
|
1013
|
+
"",
|
|
1014
|
+
"# \u2500\u2500\u2500\u2500\u2500 Build Artifacts \u2500\u2500\u2500\u2500\u2500",
|
|
1015
|
+
"/out/",
|
|
1016
|
+
"/dist/",
|
|
1017
|
+
"/target/",
|
|
1018
|
+
"/build/",
|
|
1019
|
+
"*.class",
|
|
1020
|
+
"*.jar",
|
|
1021
|
+
"*.war",
|
|
1022
|
+
"",
|
|
1023
|
+
"# \u2500\u2500\u2500\u2500\u2500 Secrets & Credentials \u2500\u2500\u2500\u2500\u2500",
|
|
1024
|
+
"# Environment files",
|
|
1025
|
+
".env",
|
|
1026
|
+
".env.*",
|
|
1027
|
+
"!.env.example",
|
|
1028
|
+
"",
|
|
1029
|
+
"# Key material",
|
|
1030
|
+
"*.pem",
|
|
1031
|
+
"*.key",
|
|
1032
|
+
"*.crt",
|
|
1033
|
+
"*.p12",
|
|
1034
|
+
"*.pfx",
|
|
1035
|
+
"*.der",
|
|
1036
|
+
"id_rsa*",
|
|
1037
|
+
"id_dsa*",
|
|
1038
|
+
"*.ppk",
|
|
1039
|
+
"",
|
|
1040
|
+
"# Cloud and service configs",
|
|
1041
|
+
"aws-credentials.json",
|
|
1042
|
+
"gcp-service-account*.json",
|
|
1043
|
+
"azure-credentials.json",
|
|
1044
|
+
"secrets/**",
|
|
1045
|
+
"config/secrets/",
|
|
1046
|
+
"**/secrets/",
|
|
1047
|
+
"",
|
|
1048
|
+
"# Database credentials",
|
|
1049
|
+
"database.yml",
|
|
1050
|
+
"**/database/config.*",
|
|
1051
|
+
"",
|
|
1052
|
+
"# API keys and tokens",
|
|
1053
|
+
"**/apikeys/",
|
|
1054
|
+
"**/*_token*",
|
|
1055
|
+
"**/*_secret*",
|
|
1056
|
+
"**/*api_key*",
|
|
1057
|
+
"",
|
|
1058
|
+
"# \u2500\u2500\u2500\u2500\u2500 Infrastructure & Deployment \u2500\u2500\u2500\u2500\u2500",
|
|
1059
|
+
"# Terraform state",
|
|
1060
|
+
"*.tfstate",
|
|
1061
|
+
"*.tfstate.*",
|
|
1062
|
+
".terraform/",
|
|
1063
|
+
"",
|
|
1064
|
+
"# Kubernetes secrets",
|
|
1065
|
+
"**/k8s/**/secret*.yaml",
|
|
1066
|
+
"**/kubernetes/**/secret*.yaml",
|
|
1067
|
+
"",
|
|
1068
|
+
"# Docker secrets",
|
|
1069
|
+
"docker-compose.override.yml",
|
|
1070
|
+
"**/docker/secrets/",
|
|
1071
|
+
"",
|
|
1072
|
+
"# \u2500\u2500\u2500\u2500\u2500 Logs & Runtime Data \u2500\u2500\u2500\u2500\u2500",
|
|
1073
|
+
"*.log",
|
|
1074
|
+
"*.tmp",
|
|
1075
|
+
"*.cache",
|
|
1076
|
+
"logs/",
|
|
1077
|
+
"/var/log/",
|
|
1078
|
+
"coverage/",
|
|
1079
|
+
".nyc_output/",
|
|
1080
|
+
"",
|
|
1081
|
+
"# \u2500\u2500\u2500\u2500\u2500 Large Data Files \u2500\u2500\u2500\u2500\u2500",
|
|
1082
|
+
"*.csv",
|
|
1083
|
+
"*.xlsx",
|
|
1084
|
+
"*.sqlite",
|
|
1085
|
+
"*.db",
|
|
1086
|
+
"*.dump",
|
|
1087
|
+
"data/",
|
|
1088
|
+
"datasets/",
|
|
1089
|
+
"",
|
|
1090
|
+
"# \u2500\u2500\u2500\u2500\u2500 Node.js Specific \u2500\u2500\u2500\u2500\u2500",
|
|
1091
|
+
"node_modules/",
|
|
1092
|
+
".pnpm-store/",
|
|
1093
|
+
".yarn/",
|
|
1094
|
+
".next/",
|
|
1095
|
+
".nuxt/",
|
|
1096
|
+
".cache/",
|
|
1097
|
+
".parcel-cache/",
|
|
1098
|
+
"",
|
|
1099
|
+
"# \u2500\u2500\u2500\u2500\u2500 Python Specific \u2500\u2500\u2500\u2500\u2500",
|
|
1100
|
+
"__pycache__/",
|
|
1101
|
+
"*.pyc",
|
|
1102
|
+
"*.pyo",
|
|
1103
|
+
"*.pyd",
|
|
1104
|
+
".Python",
|
|
1105
|
+
"venv/",
|
|
1106
|
+
".venv/",
|
|
1107
|
+
"env/",
|
|
1108
|
+
".env/",
|
|
1109
|
+
"",
|
|
1110
|
+
"# \u2500\u2500\u2500\u2500\u2500 Java Specific \u2500\u2500\u2500\u2500\u2500",
|
|
1111
|
+
"*.class",
|
|
1112
|
+
"*.jar",
|
|
1113
|
+
"*.war",
|
|
1114
|
+
"target/",
|
|
1115
|
+
""
|
|
1116
|
+
];
|
|
1117
|
+
const rulePatterns = extractIgnorePatternsFromRules(rules);
|
|
1118
|
+
if (rulePatterns.length > 0) {
|
|
1119
|
+
lines.push("# \u2500\u2500\u2500\u2500\u2500 Project-specific exclusions from rulesync rules \u2500\u2500\u2500\u2500\u2500");
|
|
1120
|
+
lines.push(...rulePatterns);
|
|
1121
|
+
lines.push("");
|
|
313
1122
|
}
|
|
314
|
-
|
|
1123
|
+
lines.push(
|
|
1124
|
+
"# \u2500\u2500\u2500\u2500\u2500 Allow specific source files (uncomment as needed) \u2500\u2500\u2500\u2500\u2500",
|
|
1125
|
+
"# !src/**/*.ts",
|
|
1126
|
+
"# !src/**/*.js",
|
|
1127
|
+
"# !lib/**/*.py",
|
|
1128
|
+
"# !src/main/**/*.java",
|
|
1129
|
+
""
|
|
1130
|
+
);
|
|
1131
|
+
return lines.join("\n");
|
|
315
1132
|
}
|
|
316
1133
|
|
|
317
1134
|
// src/generators/ignore/kiro.ts
|
|
318
|
-
import { join as
|
|
1135
|
+
import { join as join5 } from "path";
|
|
319
1136
|
async function generateKiroIgnoreFiles(rules, config, baseDir) {
|
|
320
1137
|
const outputs = [];
|
|
321
|
-
const aiignoreContent =
|
|
1138
|
+
const aiignoreContent = generateAiignoreContent2(rules);
|
|
322
1139
|
const outputPath = baseDir || process.cwd();
|
|
323
|
-
const filepath =
|
|
1140
|
+
const filepath = join5(outputPath, ".aiignore");
|
|
324
1141
|
outputs.push({
|
|
325
1142
|
tool: "kiro",
|
|
326
1143
|
filepath,
|
|
@@ -328,7 +1145,7 @@ async function generateKiroIgnoreFiles(rules, config, baseDir) {
|
|
|
328
1145
|
});
|
|
329
1146
|
return outputs;
|
|
330
1147
|
}
|
|
331
|
-
function
|
|
1148
|
+
function generateAiignoreContent2(rules) {
|
|
332
1149
|
const lines = [
|
|
333
1150
|
"# Generated by rulesync - Kiro AI-specific exclusions",
|
|
334
1151
|
"# This file excludes files that can be in Git but shouldn't be read by the AI",
|
|
@@ -341,178 +1158,49 @@ function generateAiignoreContent(rules) {
|
|
|
341
1158
|
"*.sqlite",
|
|
342
1159
|
"*.db",
|
|
343
1160
|
"",
|
|
344
|
-
"# Large binary files",
|
|
345
|
-
"*.zip",
|
|
346
|
-
"*.tar.gz",
|
|
347
|
-
"*.rar",
|
|
348
|
-
"",
|
|
349
|
-
"# Sensitive documentation",
|
|
350
|
-
"internal-docs/",
|
|
351
|
-
"confidential/",
|
|
352
|
-
"",
|
|
353
|
-
"# Test data that might confuse AI",
|
|
354
|
-
"test/fixtures/large-*.json",
|
|
355
|
-
"benchmark-results/",
|
|
356
|
-
"",
|
|
357
|
-
"# Reinforce critical exclusions from .gitignore",
|
|
358
|
-
"*.pem",
|
|
359
|
-
"*.key",
|
|
360
|
-
".env*",
|
|
361
|
-
""
|
|
362
|
-
);
|
|
363
|
-
const rulePatterns =
|
|
364
|
-
if (rulePatterns.length > 0) {
|
|
365
|
-
lines.push("# Project-specific exclusions from rulesync rules");
|
|
366
|
-
lines.push(...rulePatterns);
|
|
367
|
-
lines.push("");
|
|
368
|
-
}
|
|
369
|
-
return lines.join("\n");
|
|
370
|
-
}
|
|
371
|
-
function extractIgnorePatternsFromRules2(rules) {
|
|
372
|
-
const patterns = [];
|
|
373
|
-
for (const rule of rules) {
|
|
374
|
-
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
375
|
-
for (const glob of rule.frontmatter.globs) {
|
|
376
|
-
if (shouldExcludeFromAI(glob)) {
|
|
377
|
-
patterns.push(`# Exclude: ${rule.frontmatter.description}`);
|
|
378
|
-
patterns.push(glob);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
const contentPatterns = extractIgnorePatternsFromContent(rule.content);
|
|
383
|
-
patterns.push(...contentPatterns);
|
|
384
|
-
}
|
|
385
|
-
return patterns;
|
|
386
|
-
}
|
|
387
|
-
function shouldExcludeFromAI(glob) {
|
|
388
|
-
const excludePatterns = [
|
|
389
|
-
// Test and fixture files that might be large or confusing
|
|
390
|
-
"**/test/fixtures/**",
|
|
391
|
-
"**/tests/fixtures/**",
|
|
392
|
-
"**/*.fixture.*",
|
|
393
|
-
// Build and generated files
|
|
394
|
-
"**/dist/**",
|
|
395
|
-
"**/build/**",
|
|
396
|
-
"**/coverage/**",
|
|
397
|
-
// Configuration that might contain sensitive data
|
|
398
|
-
"**/config/production/**",
|
|
399
|
-
"**/config/prod/**",
|
|
400
|
-
"**/*.prod.*",
|
|
401
|
-
// Documentation that might be sensitive
|
|
402
|
-
"**/internal/**",
|
|
403
|
-
"**/private/**",
|
|
404
|
-
"**/confidential/**"
|
|
405
|
-
];
|
|
406
|
-
return excludePatterns.some((pattern) => {
|
|
407
|
-
const regex = new RegExp(pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*"));
|
|
408
|
-
return regex.test(glob);
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
function extractIgnorePatternsFromContent(content) {
|
|
412
|
-
const patterns = [];
|
|
413
|
-
const lines = content.split("\n");
|
|
414
|
-
for (const line of lines) {
|
|
415
|
-
const trimmed = line.trim();
|
|
416
|
-
if (trimmed.startsWith("# IGNORE:") || trimmed.startsWith("# aiignore:")) {
|
|
417
|
-
const pattern = trimmed.replace(/^# (IGNORE|aiignore):\s*/, "").trim();
|
|
418
|
-
if (pattern) {
|
|
419
|
-
patterns.push(pattern);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
if (trimmed.includes("exclude") || trimmed.includes("ignore")) {
|
|
423
|
-
const matches = trimmed.match(/['"`]([^'"`]+\.(log|tmp|cache|temp))['"`]/g);
|
|
424
|
-
if (matches) {
|
|
425
|
-
patterns.push(...matches.map((m) => m.replace(/['"`]/g, "")));
|
|
426
|
-
}
|
|
427
|
-
}
|
|
1161
|
+
"# Large binary files",
|
|
1162
|
+
"*.zip",
|
|
1163
|
+
"*.tar.gz",
|
|
1164
|
+
"*.rar",
|
|
1165
|
+
"",
|
|
1166
|
+
"# Sensitive documentation",
|
|
1167
|
+
"internal-docs/",
|
|
1168
|
+
"confidential/",
|
|
1169
|
+
"",
|
|
1170
|
+
"# Test data that might confuse AI",
|
|
1171
|
+
"test/fixtures/large-*.json",
|
|
1172
|
+
"benchmark-results/",
|
|
1173
|
+
"",
|
|
1174
|
+
"# Reinforce critical exclusions from .gitignore",
|
|
1175
|
+
"*.pem",
|
|
1176
|
+
"*.key",
|
|
1177
|
+
".env*",
|
|
1178
|
+
""
|
|
1179
|
+
);
|
|
1180
|
+
const rulePatterns = extractIgnorePatternsFromRules(rules);
|
|
1181
|
+
if (rulePatterns.length > 0) {
|
|
1182
|
+
lines.push("# Project-specific exclusions from rulesync rules");
|
|
1183
|
+
lines.push(...rulePatterns);
|
|
1184
|
+
lines.push("");
|
|
428
1185
|
}
|
|
429
|
-
return
|
|
1186
|
+
return lines.join("\n");
|
|
430
1187
|
}
|
|
431
1188
|
|
|
432
1189
|
// src/generators/rules/augmentcode.ts
|
|
433
|
-
import { join as
|
|
1190
|
+
import { join as join8 } from "path";
|
|
434
1191
|
|
|
435
1192
|
// src/generators/rules/shared-helpers.ts
|
|
436
|
-
import { join as
|
|
1193
|
+
import { join as join7 } from "path";
|
|
437
1194
|
|
|
438
1195
|
// src/utils/ignore.ts
|
|
439
|
-
import { join as
|
|
1196
|
+
import { join as join6 } from "path";
|
|
440
1197
|
import micromatch from "micromatch";
|
|
441
|
-
|
|
442
|
-
// src/utils/file.ts
|
|
443
|
-
import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
|
|
444
|
-
import { dirname, join as join4 } from "path";
|
|
445
|
-
async function ensureDir(dirPath) {
|
|
446
|
-
try {
|
|
447
|
-
await stat(dirPath);
|
|
448
|
-
} catch {
|
|
449
|
-
await mkdir2(dirPath, { recursive: true });
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
async function readFileContent(filepath) {
|
|
453
|
-
return readFile(filepath, "utf-8");
|
|
454
|
-
}
|
|
455
|
-
async function writeFileContent(filepath, content) {
|
|
456
|
-
await ensureDir(dirname(filepath));
|
|
457
|
-
await writeFile2(filepath, content, "utf-8");
|
|
458
|
-
}
|
|
459
|
-
async function fileExists(filepath) {
|
|
460
|
-
try {
|
|
461
|
-
await stat(filepath);
|
|
462
|
-
return true;
|
|
463
|
-
} catch {
|
|
464
|
-
return false;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
async function findFiles(dir, extension = ".md") {
|
|
468
|
-
try {
|
|
469
|
-
const files = await readdir(dir);
|
|
470
|
-
return files.filter((file) => file.endsWith(extension)).map((file) => join4(dir, file));
|
|
471
|
-
} catch {
|
|
472
|
-
return [];
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
async function removeDirectory(dirPath) {
|
|
476
|
-
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
477
|
-
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
478
|
-
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
479
|
-
return;
|
|
480
|
-
}
|
|
481
|
-
try {
|
|
482
|
-
if (await fileExists(dirPath)) {
|
|
483
|
-
await rm(dirPath, { recursive: true, force: true });
|
|
484
|
-
}
|
|
485
|
-
} catch (error) {
|
|
486
|
-
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
async function removeFile(filepath) {
|
|
490
|
-
try {
|
|
491
|
-
if (await fileExists(filepath)) {
|
|
492
|
-
await rm(filepath);
|
|
493
|
-
}
|
|
494
|
-
} catch (error) {
|
|
495
|
-
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
async function removeClaudeGeneratedFiles() {
|
|
499
|
-
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
500
|
-
for (const fileOrDir of filesToRemove) {
|
|
501
|
-
if (fileOrDir.endsWith("/memories")) {
|
|
502
|
-
await removeDirectory(fileOrDir);
|
|
503
|
-
} else {
|
|
504
|
-
await removeFile(fileOrDir);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// src/utils/ignore.ts
|
|
510
1198
|
var cachedIgnorePatterns = null;
|
|
511
1199
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
512
1200
|
if (cachedIgnorePatterns) {
|
|
513
1201
|
return cachedIgnorePatterns;
|
|
514
1202
|
}
|
|
515
|
-
const ignorePath =
|
|
1203
|
+
const ignorePath = join6(baseDir, ".rulesyncignore");
|
|
516
1204
|
if (!await fileExists(ignorePath)) {
|
|
517
1205
|
cachedIgnorePatterns = { patterns: [] };
|
|
518
1206
|
return cachedIgnorePatterns;
|
|
@@ -557,7 +1245,7 @@ function filterIgnoredFiles(files, ignorePatterns) {
|
|
|
557
1245
|
|
|
558
1246
|
// src/generators/rules/shared-helpers.ts
|
|
559
1247
|
function resolveOutputDir(config, tool, baseDir) {
|
|
560
|
-
return baseDir ?
|
|
1248
|
+
return baseDir ? join7(baseDir, config.outputPaths[tool]) : config.outputPaths[tool];
|
|
561
1249
|
}
|
|
562
1250
|
function createOutputsArray() {
|
|
563
1251
|
return [];
|
|
@@ -566,7 +1254,7 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
|
|
|
566
1254
|
const outputDir = resolveOutputDir(config, tool, baseDir);
|
|
567
1255
|
outputs.push({
|
|
568
1256
|
tool,
|
|
569
|
-
filepath:
|
|
1257
|
+
filepath: join7(outputDir, relativePath),
|
|
570
1258
|
content
|
|
571
1259
|
});
|
|
572
1260
|
}
|
|
@@ -575,7 +1263,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
|
575
1263
|
for (const rule of rules) {
|
|
576
1264
|
const content = generatorConfig.generateContent(rule);
|
|
577
1265
|
const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
|
|
578
|
-
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) :
|
|
1266
|
+
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join7(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
|
|
579
1267
|
outputs.push({
|
|
580
1268
|
tool: generatorConfig.tool,
|
|
581
1269
|
filepath,
|
|
@@ -584,13 +1272,57 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
|
584
1272
|
}
|
|
585
1273
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
586
1274
|
if (ignorePatterns.patterns.length > 0) {
|
|
587
|
-
const ignorePath = baseDir ?
|
|
1275
|
+
const ignorePath = baseDir ? join7(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
|
|
1276
|
+
const ignoreContent = generateIgnoreFile(ignorePatterns.patterns, generatorConfig.tool);
|
|
1277
|
+
outputs.push({
|
|
1278
|
+
tool: generatorConfig.tool,
|
|
1279
|
+
filepath: ignorePath,
|
|
1280
|
+
content: ignoreContent
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
return outputs;
|
|
1284
|
+
}
|
|
1285
|
+
async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
1286
|
+
const outputs = [];
|
|
1287
|
+
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
1288
|
+
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
1289
|
+
const rootRule = rootRules[0];
|
|
1290
|
+
if (generatorConfig.generateDetailContent && generatorConfig.detailSubDir) {
|
|
1291
|
+
for (const rule of detailRules) {
|
|
1292
|
+
const content = generatorConfig.generateDetailContent(rule);
|
|
1293
|
+
const filepath = baseDir ? join7(baseDir, generatorConfig.detailSubDir, `${rule.filename}.md`) : join7(generatorConfig.detailSubDir, `${rule.filename}.md`);
|
|
1294
|
+
outputs.push({
|
|
1295
|
+
tool: generatorConfig.tool,
|
|
1296
|
+
filepath,
|
|
1297
|
+
content
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
if (generatorConfig.generateRootContent && generatorConfig.rootFilePath) {
|
|
1302
|
+
const rootContent = generatorConfig.generateRootContent(rootRule, detailRules, baseDir);
|
|
1303
|
+
const rootFilepath = baseDir ? join7(baseDir, generatorConfig.rootFilePath) : generatorConfig.rootFilePath;
|
|
1304
|
+
outputs.push({
|
|
1305
|
+
tool: generatorConfig.tool,
|
|
1306
|
+
filepath: rootFilepath,
|
|
1307
|
+
content: rootContent
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
1311
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
1312
|
+
const ignorePath = baseDir ? join7(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
|
|
588
1313
|
const ignoreContent = generateIgnoreFile(ignorePatterns.patterns, generatorConfig.tool);
|
|
589
1314
|
outputs.push({
|
|
590
1315
|
tool: generatorConfig.tool,
|
|
591
1316
|
filepath: ignorePath,
|
|
592
1317
|
content: ignoreContent
|
|
593
1318
|
});
|
|
1319
|
+
if (generatorConfig.updateAdditionalConfig) {
|
|
1320
|
+
const additionalOutputs = await generatorConfig.updateAdditionalConfig(
|
|
1321
|
+
ignorePatterns.patterns,
|
|
1322
|
+
baseDir
|
|
1323
|
+
);
|
|
1324
|
+
outputs.push(...additionalOutputs);
|
|
1325
|
+
}
|
|
594
1326
|
}
|
|
595
1327
|
return outputs;
|
|
596
1328
|
}
|
|
@@ -627,7 +1359,7 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
|
|
|
627
1359
|
"augmentcode",
|
|
628
1360
|
config,
|
|
629
1361
|
baseDir,
|
|
630
|
-
|
|
1362
|
+
join8(".augment", "rules", `${rule.filename}.md`),
|
|
631
1363
|
generateRuleFile(rule)
|
|
632
1364
|
);
|
|
633
1365
|
});
|
|
@@ -680,42 +1412,29 @@ function generateLegacyGuidelinesFile(allRules) {
|
|
|
680
1412
|
}
|
|
681
1413
|
|
|
682
1414
|
// src/generators/rules/claudecode.ts
|
|
683
|
-
import { join as
|
|
684
|
-
|
|
685
|
-
// src/types/claudecode.ts
|
|
686
|
-
import { z } from "zod/mini";
|
|
687
|
-
var ClaudeSettingsSchema = z.looseObject({
|
|
688
|
-
permissions: z._default(
|
|
689
|
-
z.looseObject({
|
|
690
|
-
deny: z._default(z.array(z.string()), [])
|
|
691
|
-
}),
|
|
692
|
-
{ deny: [] }
|
|
693
|
-
)
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
// src/generators/rules/claudecode.ts
|
|
1415
|
+
import { join as join9 } from "path";
|
|
697
1416
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
698
1417
|
const outputs = [];
|
|
699
1418
|
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
700
1419
|
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
701
1420
|
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
702
|
-
const claudeOutputDir = baseDir ?
|
|
1421
|
+
const claudeOutputDir = baseDir ? join9(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
|
|
703
1422
|
outputs.push({
|
|
704
1423
|
tool: "claudecode",
|
|
705
|
-
filepath:
|
|
1424
|
+
filepath: join9(claudeOutputDir, "CLAUDE.md"),
|
|
706
1425
|
content: claudeMdContent
|
|
707
1426
|
});
|
|
708
1427
|
for (const rule of detailRules) {
|
|
709
1428
|
const memoryContent = generateMemoryFile(rule);
|
|
710
1429
|
outputs.push({
|
|
711
1430
|
tool: "claudecode",
|
|
712
|
-
filepath:
|
|
1431
|
+
filepath: join9(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
|
|
713
1432
|
content: memoryContent
|
|
714
1433
|
});
|
|
715
1434
|
}
|
|
716
1435
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
717
1436
|
if (ignorePatterns.patterns.length > 0) {
|
|
718
|
-
const settingsPath = baseDir ?
|
|
1437
|
+
const settingsPath = baseDir ? join9(baseDir, ".claude", "settings.json") : join9(".claude", "settings.json");
|
|
719
1438
|
await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
|
|
720
1439
|
}
|
|
721
1440
|
return outputs;
|
|
@@ -794,7 +1513,7 @@ async function generateClineConfig(rules, config, baseDir) {
|
|
|
794
1513
|
}
|
|
795
1514
|
|
|
796
1515
|
// src/generators/rules/copilot.ts
|
|
797
|
-
import { join as
|
|
1516
|
+
import { join as join10 } from "path";
|
|
798
1517
|
async function generateCopilotConfig(rules, config, baseDir) {
|
|
799
1518
|
return generateComplexRulesConfig(
|
|
800
1519
|
rules,
|
|
@@ -806,7 +1525,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
|
|
|
806
1525
|
generateContent: generateCopilotMarkdown,
|
|
807
1526
|
getOutputPath: (rule, outputDir) => {
|
|
808
1527
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
809
|
-
return
|
|
1528
|
+
return join10(outputDir, `${baseFilename}.instructions.md`);
|
|
810
1529
|
}
|
|
811
1530
|
},
|
|
812
1531
|
baseDir
|
|
@@ -827,7 +1546,7 @@ function generateCopilotMarkdown(rule) {
|
|
|
827
1546
|
}
|
|
828
1547
|
|
|
829
1548
|
// src/generators/rules/cursor.ts
|
|
830
|
-
import { join as
|
|
1549
|
+
import { join as join11 } from "path";
|
|
831
1550
|
async function generateCursorConfig(rules, config, baseDir) {
|
|
832
1551
|
return generateComplexRulesConfig(
|
|
833
1552
|
rules,
|
|
@@ -838,7 +1557,7 @@ async function generateCursorConfig(rules, config, baseDir) {
|
|
|
838
1557
|
ignoreFileName: ".cursorignore",
|
|
839
1558
|
generateContent: generateCursorMarkdown,
|
|
840
1559
|
getOutputPath: (rule, outputDir) => {
|
|
841
|
-
return
|
|
1560
|
+
return join11(outputDir, `${rule.filename}.mdc`);
|
|
842
1561
|
}
|
|
843
1562
|
},
|
|
844
1563
|
baseDir
|
|
@@ -898,39 +1617,18 @@ function determineCursorRuleType(frontmatter) {
|
|
|
898
1617
|
}
|
|
899
1618
|
|
|
900
1619
|
// src/generators/rules/geminicli.ts
|
|
901
|
-
import { join as join11 } from "path";
|
|
902
1620
|
async function generateGeminiConfig(rules, config, baseDir) {
|
|
903
|
-
const
|
|
904
|
-
const rootRule = rules.find((rule) => rule.frontmatter.root === true);
|
|
905
|
-
const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
|
|
906
|
-
for (const rule of memoryRules) {
|
|
907
|
-
const content = generateGeminiMemoryMarkdown(rule);
|
|
908
|
-
const outputDir = baseDir ? join11(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
|
|
909
|
-
const filepath = join11(outputDir, `${rule.filename}.md`);
|
|
910
|
-
outputs.push({
|
|
911
|
-
tool: "geminicli",
|
|
912
|
-
filepath,
|
|
913
|
-
content
|
|
914
|
-
});
|
|
915
|
-
}
|
|
916
|
-
const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
|
|
917
|
-
const rootFilepath = baseDir ? join11(baseDir, "GEMINI.md") : "GEMINI.md";
|
|
918
|
-
outputs.push({
|
|
1621
|
+
const generatorConfig = {
|
|
919
1622
|
tool: "geminicli",
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
filepath: aiexcludePath,
|
|
930
|
-
content: aiexcludeContent
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
return outputs;
|
|
1623
|
+
fileExtension: ".md",
|
|
1624
|
+
ignoreFileName: ".aiexclude",
|
|
1625
|
+
generateContent: generateGeminiMemoryMarkdown,
|
|
1626
|
+
generateDetailContent: generateGeminiMemoryMarkdown,
|
|
1627
|
+
generateRootContent: generateGeminiRootMarkdown,
|
|
1628
|
+
rootFilePath: "GEMINI.md",
|
|
1629
|
+
detailSubDir: ".gemini/memories"
|
|
1630
|
+
};
|
|
1631
|
+
return generateComplexRules(rules, config, generatorConfig, baseDir);
|
|
934
1632
|
}
|
|
935
1633
|
function generateGeminiMemoryMarkdown(rule) {
|
|
936
1634
|
return rule.content.trim();
|
|
@@ -959,14 +1657,32 @@ function generateGeminiRootMarkdown(rootRule, memoryRules, _baseDir) {
|
|
|
959
1657
|
}
|
|
960
1658
|
return lines.join("\n");
|
|
961
1659
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
"",
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1660
|
+
|
|
1661
|
+
// src/generators/rules/junie.ts
|
|
1662
|
+
async function generateJunieConfig(rules, config, baseDir) {
|
|
1663
|
+
const generatorConfig = {
|
|
1664
|
+
tool: "junie",
|
|
1665
|
+
fileExtension: ".md",
|
|
1666
|
+
ignoreFileName: ".aiignore",
|
|
1667
|
+
generateContent: (rule) => rule.content.trim(),
|
|
1668
|
+
generateRootContent: generateGuidelinesMarkdown,
|
|
1669
|
+
rootFilePath: ".junie/guidelines.md"
|
|
1670
|
+
};
|
|
1671
|
+
return generateComplexRules(rules, config, generatorConfig, baseDir);
|
|
1672
|
+
}
|
|
1673
|
+
function generateGuidelinesMarkdown(rootRule, detailRules) {
|
|
1674
|
+
const lines = [];
|
|
1675
|
+
if (rootRule) {
|
|
1676
|
+
lines.push(rootRule.content);
|
|
1677
|
+
lines.push("");
|
|
1678
|
+
}
|
|
1679
|
+
if (detailRules.length > 0) {
|
|
1680
|
+
for (const rule of detailRules) {
|
|
1681
|
+
lines.push(rule.content);
|
|
1682
|
+
lines.push("");
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
return lines.join("\n").trim();
|
|
970
1686
|
}
|
|
971
1687
|
|
|
972
1688
|
// src/generators/rules/kiro.ts
|
|
@@ -1030,7 +1746,10 @@ async function generateConfigurations(rules, config, targetTools, baseDir) {
|
|
|
1030
1746
|
function filterRulesForTool(rules, tool, config) {
|
|
1031
1747
|
return rules.filter((rule) => {
|
|
1032
1748
|
const targets = resolveTargets(rule.frontmatter.targets, config);
|
|
1033
|
-
|
|
1749
|
+
if (!targets.includes(tool)) {
|
|
1750
|
+
return false;
|
|
1751
|
+
}
|
|
1752
|
+
return isToolSpecificRule(rule, tool);
|
|
1034
1753
|
});
|
|
1035
1754
|
}
|
|
1036
1755
|
async function generateForTool(tool, rules, config, baseDir) {
|
|
@@ -1061,6 +1780,11 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
1061
1780
|
return generateRooConfig(rules, config, baseDir);
|
|
1062
1781
|
case "geminicli":
|
|
1063
1782
|
return generateGeminiConfig(rules, config, baseDir);
|
|
1783
|
+
case "junie": {
|
|
1784
|
+
const junieRulesOutputs = await generateJunieConfig(rules, config, baseDir);
|
|
1785
|
+
const junieIgnoreOutputs = await generateJunieIgnoreFiles(rules, config, baseDir);
|
|
1786
|
+
return [...junieRulesOutputs, ...junieIgnoreOutputs];
|
|
1787
|
+
}
|
|
1064
1788
|
case "kiro": {
|
|
1065
1789
|
const kiroRulesOutputs = await generateKiroConfig(rules, config, baseDir);
|
|
1066
1790
|
const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
|
|
@@ -1075,75 +1799,6 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
1075
1799
|
// src/core/parser.ts
|
|
1076
1800
|
import { basename } from "path";
|
|
1077
1801
|
import matter from "gray-matter";
|
|
1078
|
-
|
|
1079
|
-
// src/types/config.ts
|
|
1080
|
-
import { z as z2 } from "zod/mini";
|
|
1081
|
-
var ConfigSchema = z2.object({
|
|
1082
|
-
aiRulesDir: z2.string(),
|
|
1083
|
-
outputPaths: z2.record(ToolTargetSchema, z2.string()),
|
|
1084
|
-
watchEnabled: z2.boolean(),
|
|
1085
|
-
defaultTargets: ToolTargetsSchema
|
|
1086
|
-
});
|
|
1087
|
-
|
|
1088
|
-
// src/types/mcp.ts
|
|
1089
|
-
import { z as z3 } from "zod/mini";
|
|
1090
|
-
var McpTransportTypeSchema = z3.enum(["stdio", "sse", "http"]);
|
|
1091
|
-
var McpServerBaseSchema = z3.object({
|
|
1092
|
-
command: z3.optional(z3.string()),
|
|
1093
|
-
args: z3.optional(z3.array(z3.string())),
|
|
1094
|
-
url: z3.optional(z3.string()),
|
|
1095
|
-
httpUrl: z3.optional(z3.string()),
|
|
1096
|
-
env: z3.optional(z3.record(z3.string(), z3.string())),
|
|
1097
|
-
disabled: z3.optional(z3.boolean()),
|
|
1098
|
-
networkTimeout: z3.optional(z3.number()),
|
|
1099
|
-
timeout: z3.optional(z3.number()),
|
|
1100
|
-
trust: z3.optional(z3.boolean()),
|
|
1101
|
-
cwd: z3.optional(z3.string()),
|
|
1102
|
-
transport: z3.optional(McpTransportTypeSchema),
|
|
1103
|
-
type: z3.optional(z3.enum(["sse", "streamable-http"])),
|
|
1104
|
-
alwaysAllow: z3.optional(z3.array(z3.string())),
|
|
1105
|
-
tools: z3.optional(z3.array(z3.string())),
|
|
1106
|
-
kiroAutoApprove: z3.optional(z3.array(z3.string())),
|
|
1107
|
-
kiroAutoBlock: z3.optional(z3.array(z3.string()))
|
|
1108
|
-
});
|
|
1109
|
-
var RulesyncMcpServerSchema = z3.extend(McpServerBaseSchema, {
|
|
1110
|
-
targets: z3.optional(RulesyncTargetsSchema)
|
|
1111
|
-
});
|
|
1112
|
-
var McpConfigSchema = z3.object({
|
|
1113
|
-
mcpServers: z3.record(z3.string(), McpServerBaseSchema)
|
|
1114
|
-
});
|
|
1115
|
-
var RulesyncMcpConfigSchema = z3.object({
|
|
1116
|
-
mcpServers: z3.record(z3.string(), RulesyncMcpServerSchema)
|
|
1117
|
-
});
|
|
1118
|
-
|
|
1119
|
-
// src/types/rules.ts
|
|
1120
|
-
import { z as z4 } from "zod/mini";
|
|
1121
|
-
var RuleFrontmatterSchema = z4.object({
|
|
1122
|
-
root: z4.boolean(),
|
|
1123
|
-
targets: RulesyncTargetsSchema,
|
|
1124
|
-
description: z4.string(),
|
|
1125
|
-
globs: z4.array(z4.string()),
|
|
1126
|
-
cursorRuleType: z4.optional(z4.enum(["always", "manual", "specificFiles", "intelligently"])),
|
|
1127
|
-
tags: z4.optional(z4.array(z4.string()))
|
|
1128
|
-
});
|
|
1129
|
-
var ParsedRuleSchema = z4.object({
|
|
1130
|
-
frontmatter: RuleFrontmatterSchema,
|
|
1131
|
-
content: z4.string(),
|
|
1132
|
-
filename: z4.string(),
|
|
1133
|
-
filepath: z4.string()
|
|
1134
|
-
});
|
|
1135
|
-
var GeneratedOutputSchema = z4.object({
|
|
1136
|
-
tool: ToolTargetSchema,
|
|
1137
|
-
filepath: z4.string(),
|
|
1138
|
-
content: z4.string()
|
|
1139
|
-
});
|
|
1140
|
-
var GenerateOptionsSchema = z4.object({
|
|
1141
|
-
targetTools: z4.optional(ToolTargetsSchema),
|
|
1142
|
-
outputDir: z4.optional(z4.string()),
|
|
1143
|
-
watch: z4.optional(z4.boolean())
|
|
1144
|
-
});
|
|
1145
|
-
|
|
1146
|
-
// src/core/parser.ts
|
|
1147
1802
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
1148
1803
|
const ignorePatterns = await loadIgnorePatterns();
|
|
1149
1804
|
const allRuleFiles = await findFiles(aiRulesDir, ".md");
|
|
@@ -1245,13 +1900,13 @@ async function validateRule(rule) {
|
|
|
1245
1900
|
}
|
|
1246
1901
|
|
|
1247
1902
|
// src/core/mcp-generator.ts
|
|
1248
|
-
import * as
|
|
1903
|
+
import * as path4 from "path";
|
|
1249
1904
|
|
|
1250
1905
|
// src/core/mcp-parser.ts
|
|
1251
1906
|
import * as fs from "fs";
|
|
1252
|
-
import * as
|
|
1907
|
+
import * as path3 from "path";
|
|
1253
1908
|
function parseMcpConfig(projectRoot) {
|
|
1254
|
-
const mcpPath =
|
|
1909
|
+
const mcpPath = path3.join(projectRoot, ".rulesync", ".mcp.json");
|
|
1255
1910
|
if (!fs.existsSync(mcpPath)) {
|
|
1256
1911
|
return null;
|
|
1257
1912
|
}
|
|
@@ -1275,7 +1930,7 @@ function parseMcpConfig(projectRoot) {
|
|
|
1275
1930
|
}
|
|
1276
1931
|
|
|
1277
1932
|
// src/core/mcp-generator.ts
|
|
1278
|
-
async function generateMcpConfigs(projectRoot, baseDir) {
|
|
1933
|
+
async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
|
|
1279
1934
|
const results = [];
|
|
1280
1935
|
const targetRoot = baseDir || projectRoot;
|
|
1281
1936
|
const config = parseMcpConfig(projectRoot);
|
|
@@ -1285,55 +1940,70 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1285
1940
|
const generators = [
|
|
1286
1941
|
{
|
|
1287
1942
|
tool: "augmentcode-project",
|
|
1288
|
-
path:
|
|
1943
|
+
path: path4.join(targetRoot, ".mcp.json"),
|
|
1289
1944
|
generate: () => generateAugmentcodeMcp(config)
|
|
1290
1945
|
},
|
|
1291
1946
|
{
|
|
1292
1947
|
tool: "augmentcode-legacy-project",
|
|
1293
|
-
path:
|
|
1948
|
+
path: path4.join(targetRoot, ".mcp.json"),
|
|
1294
1949
|
generate: () => generateAugmentcodeMcp(config)
|
|
1295
1950
|
},
|
|
1296
1951
|
{
|
|
1297
1952
|
tool: "claude-project",
|
|
1298
|
-
path:
|
|
1953
|
+
path: path4.join(targetRoot, ".mcp.json"),
|
|
1299
1954
|
generate: () => generateClaudeMcp(config)
|
|
1300
1955
|
},
|
|
1301
1956
|
{
|
|
1302
1957
|
tool: "copilot-editor",
|
|
1303
|
-
path:
|
|
1958
|
+
path: path4.join(targetRoot, ".vscode", "mcp.json"),
|
|
1304
1959
|
generate: () => generateCopilotMcp(config, "editor")
|
|
1305
1960
|
},
|
|
1306
1961
|
{
|
|
1307
1962
|
tool: "cursor-project",
|
|
1308
|
-
path:
|
|
1963
|
+
path: path4.join(targetRoot, ".cursor", "mcp.json"),
|
|
1309
1964
|
generate: () => generateCursorMcp(config)
|
|
1310
1965
|
},
|
|
1311
1966
|
{
|
|
1312
1967
|
tool: "cline-project",
|
|
1313
|
-
path:
|
|
1968
|
+
path: path4.join(targetRoot, ".cline", "mcp.json"),
|
|
1314
1969
|
generate: () => generateClineMcp(config)
|
|
1315
1970
|
},
|
|
1316
1971
|
{
|
|
1317
1972
|
tool: "gemini-project",
|
|
1318
|
-
path:
|
|
1973
|
+
path: path4.join(targetRoot, ".gemini", "settings.json"),
|
|
1319
1974
|
generate: () => generateGeminiCliMcp(config)
|
|
1320
1975
|
},
|
|
1976
|
+
{
|
|
1977
|
+
tool: "junie-project",
|
|
1978
|
+
path: path4.join(targetRoot, ".junie", "mcp-config.json"),
|
|
1979
|
+
generate: () => generateJunieMcp(config)
|
|
1980
|
+
},
|
|
1321
1981
|
{
|
|
1322
1982
|
tool: "kiro-project",
|
|
1323
|
-
path:
|
|
1983
|
+
path: path4.join(targetRoot, ".kiro", "mcp.json"),
|
|
1324
1984
|
generate: () => generateKiroMcp(config)
|
|
1325
1985
|
},
|
|
1326
1986
|
{
|
|
1327
1987
|
tool: "roo-project",
|
|
1328
|
-
path:
|
|
1988
|
+
path: path4.join(targetRoot, ".roo", "mcp.json"),
|
|
1329
1989
|
generate: () => generateRooMcp(config)
|
|
1330
1990
|
}
|
|
1331
1991
|
];
|
|
1332
|
-
|
|
1992
|
+
const filteredGenerators = targetTools ? generators.filter((g) => {
|
|
1993
|
+
const baseTool = g.tool.split("-")[0];
|
|
1994
|
+
if (!isToolTarget(baseTool)) {
|
|
1995
|
+
return false;
|
|
1996
|
+
}
|
|
1997
|
+
if (baseTool === "augmentcode") {
|
|
1998
|
+
return targetTools.includes("augmentcode") || targetTools.includes("augmentcode-legacy");
|
|
1999
|
+
}
|
|
2000
|
+
return targetTools.includes(baseTool);
|
|
2001
|
+
}) : generators;
|
|
2002
|
+
for (const generator of filteredGenerators) {
|
|
1333
2003
|
try {
|
|
1334
2004
|
const content = generator.generate();
|
|
1335
2005
|
const parsed = JSON.parse(content);
|
|
1336
|
-
if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
|
|
2006
|
+
if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("junie") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
|
|
1337
2007
|
if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
|
|
1338
2008
|
results.push({
|
|
1339
2009
|
tool: generator.tool,
|
|
@@ -1373,15 +2043,58 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1373
2043
|
|
|
1374
2044
|
// src/cli/commands/generate.ts
|
|
1375
2045
|
async function generateCommand(options = {}) {
|
|
1376
|
-
const
|
|
1377
|
-
|
|
2046
|
+
const configLoaderOptions = {
|
|
2047
|
+
...options.config !== void 0 && { configPath: options.config },
|
|
2048
|
+
...options.noConfig !== void 0 && { noConfig: options.noConfig }
|
|
2049
|
+
};
|
|
2050
|
+
const configResult = await loadConfig(configLoaderOptions);
|
|
2051
|
+
const cliOptions = {
|
|
2052
|
+
...options.tools !== void 0 && { tools: options.tools },
|
|
2053
|
+
...options.verbose !== void 0 && { verbose: options.verbose },
|
|
2054
|
+
...options.delete !== void 0 && { delete: options.delete },
|
|
2055
|
+
...options.baseDirs !== void 0 && { baseDirs: options.baseDirs }
|
|
2056
|
+
};
|
|
2057
|
+
const config = mergeWithCliOptions(configResult.config, cliOptions);
|
|
2058
|
+
if (options.tools && options.tools.length > 0) {
|
|
2059
|
+
const configTargets = config.defaultTargets;
|
|
2060
|
+
const cliTools = options.tools;
|
|
2061
|
+
const cliToolsSet = new Set(cliTools);
|
|
2062
|
+
const configTargetsSet = new Set(configTargets);
|
|
2063
|
+
const notInConfig = cliTools.filter((tool) => !configTargetsSet.has(tool));
|
|
2064
|
+
const notInCli = configTargets.filter((tool) => !cliToolsSet.has(tool));
|
|
2065
|
+
if (notInConfig.length > 0 || notInCli.length > 0) {
|
|
2066
|
+
console.warn("\u26A0\uFE0F Warning: CLI tool selection differs from configuration!");
|
|
2067
|
+
console.warn(` Config targets: ${configTargets.join(", ")}`);
|
|
2068
|
+
console.warn(` CLI specified: ${cliTools.join(", ")}`);
|
|
2069
|
+
if (notInConfig.length > 0) {
|
|
2070
|
+
console.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
|
|
2071
|
+
}
|
|
2072
|
+
if (notInCli.length > 0) {
|
|
2073
|
+
console.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
|
|
2074
|
+
}
|
|
2075
|
+
console.warn("\n The configuration file targets will be used.");
|
|
2076
|
+
console.warn(" To change targets, update your rulesync config file.");
|
|
2077
|
+
console.warn("");
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
let baseDirs;
|
|
2081
|
+
if (config.baseDir) {
|
|
2082
|
+
baseDirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
|
|
2083
|
+
} else if (options.baseDirs) {
|
|
2084
|
+
baseDirs = options.baseDirs;
|
|
2085
|
+
} else {
|
|
2086
|
+
baseDirs = [process.cwd()];
|
|
2087
|
+
}
|
|
2088
|
+
if (config.verbose && configResult.filepath) {
|
|
2089
|
+
console.log(`Loaded configuration from: ${configResult.filepath}`);
|
|
2090
|
+
}
|
|
1378
2091
|
console.log("Generating configuration files...");
|
|
1379
2092
|
if (!await fileExists(config.aiRulesDir)) {
|
|
1380
2093
|
console.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
|
|
1381
2094
|
process.exit(1);
|
|
1382
2095
|
}
|
|
1383
2096
|
try {
|
|
1384
|
-
if (
|
|
2097
|
+
if (config.verbose) {
|
|
1385
2098
|
console.log(`Parsing rules from ${config.aiRulesDir}...`);
|
|
1386
2099
|
}
|
|
1387
2100
|
const rules = await parseRulesFromDirectory(config.aiRulesDir);
|
|
@@ -1389,18 +2102,26 @@ async function generateCommand(options = {}) {
|
|
|
1389
2102
|
console.warn("\u26A0\uFE0F No rules found in .rulesync directory");
|
|
1390
2103
|
return;
|
|
1391
2104
|
}
|
|
1392
|
-
if (
|
|
2105
|
+
if (config.verbose) {
|
|
1393
2106
|
console.log(`Found ${rules.length} rule(s)`);
|
|
1394
2107
|
console.log(`Base directories: ${baseDirs.join(", ")}`);
|
|
1395
2108
|
}
|
|
1396
|
-
if (
|
|
1397
|
-
if (
|
|
2109
|
+
if (config.delete) {
|
|
2110
|
+
if (config.verbose) {
|
|
1398
2111
|
console.log("Deleting existing output directories...");
|
|
1399
2112
|
}
|
|
1400
|
-
const targetTools =
|
|
2113
|
+
const targetTools = config.defaultTargets;
|
|
1401
2114
|
const deleteTasks = [];
|
|
1402
2115
|
for (const tool of targetTools) {
|
|
1403
2116
|
switch (tool) {
|
|
2117
|
+
case "augmentcode":
|
|
2118
|
+
deleteTasks.push(removeDirectory(join15(".augment", "rules")));
|
|
2119
|
+
deleteTasks.push(removeDirectory(join15(".augment", "ignore")));
|
|
2120
|
+
break;
|
|
2121
|
+
case "augmentcode-legacy":
|
|
2122
|
+
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
2123
|
+
deleteTasks.push(removeDirectory(join15(".augment", "ignore")));
|
|
2124
|
+
break;
|
|
1404
2125
|
case "copilot":
|
|
1405
2126
|
deleteTasks.push(removeDirectory(config.outputPaths.copilot));
|
|
1406
2127
|
break;
|
|
@@ -1425,19 +2146,19 @@ async function generateCommand(options = {}) {
|
|
|
1425
2146
|
}
|
|
1426
2147
|
}
|
|
1427
2148
|
await Promise.all(deleteTasks);
|
|
1428
|
-
if (
|
|
2149
|
+
if (config.verbose) {
|
|
1429
2150
|
console.log("Deleted existing output directories");
|
|
1430
2151
|
}
|
|
1431
2152
|
}
|
|
1432
2153
|
let totalOutputs = 0;
|
|
1433
2154
|
for (const baseDir of baseDirs) {
|
|
1434
|
-
if (
|
|
2155
|
+
if (config.verbose) {
|
|
1435
2156
|
console.log(`
|
|
1436
2157
|
Generating configurations for base directory: ${baseDir}`);
|
|
1437
2158
|
}
|
|
1438
|
-
const outputs = await generateConfigurations(rules, config,
|
|
2159
|
+
const outputs = await generateConfigurations(rules, config, config.defaultTargets, baseDir);
|
|
1439
2160
|
if (outputs.length === 0) {
|
|
1440
|
-
if (
|
|
2161
|
+
if (config.verbose) {
|
|
1441
2162
|
console.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
|
|
1442
2163
|
}
|
|
1443
2164
|
continue;
|
|
@@ -1452,17 +2173,18 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1452
2173
|
console.warn("\u26A0\uFE0F No configurations generated");
|
|
1453
2174
|
return;
|
|
1454
2175
|
}
|
|
1455
|
-
if (
|
|
2176
|
+
if (config.verbose) {
|
|
1456
2177
|
console.log("\nGenerating MCP configurations...");
|
|
1457
2178
|
}
|
|
1458
2179
|
let totalMcpOutputs = 0;
|
|
1459
2180
|
for (const baseDir of baseDirs) {
|
|
1460
2181
|
const mcpResults = await generateMcpConfigs(
|
|
1461
2182
|
process.cwd(),
|
|
1462
|
-
baseDir === process.cwd() ? void 0 : baseDir
|
|
2183
|
+
baseDir === process.cwd() ? void 0 : baseDir,
|
|
2184
|
+
config.defaultTargets
|
|
1463
2185
|
);
|
|
1464
2186
|
if (mcpResults.length === 0) {
|
|
1465
|
-
if (
|
|
2187
|
+
if (config.verbose) {
|
|
1466
2188
|
console.log(`No MCP configuration found for ${baseDir}`);
|
|
1467
2189
|
}
|
|
1468
2190
|
continue;
|
|
@@ -1473,7 +2195,7 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1473
2195
|
totalMcpOutputs++;
|
|
1474
2196
|
} else if (result.status === "error") {
|
|
1475
2197
|
console.error(`\u274C Failed to generate ${result.tool} MCP configuration: ${result.error}`);
|
|
1476
|
-
} else if (
|
|
2198
|
+
} else if (config.verbose && result.status === "skipped") {
|
|
1477
2199
|
console.log(`\u23ED\uFE0F Skipped ${result.tool} MCP configuration (no servers configured)`);
|
|
1478
2200
|
}
|
|
1479
2201
|
}
|
|
@@ -1492,10 +2214,10 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1492
2214
|
}
|
|
1493
2215
|
|
|
1494
2216
|
// src/cli/commands/gitignore.ts
|
|
1495
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1496
|
-
import { join as
|
|
2217
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2218
|
+
import { join as join16 } from "path";
|
|
1497
2219
|
var gitignoreCommand = async () => {
|
|
1498
|
-
const gitignorePath =
|
|
2220
|
+
const gitignorePath = join16(process.cwd(), ".gitignore");
|
|
1499
2221
|
const rulesFilesToIgnore = [
|
|
1500
2222
|
"# Generated by rulesync - AI tool configuration files",
|
|
1501
2223
|
"**/.github/copilot-instructions.md",
|
|
@@ -1517,6 +2239,8 @@ var gitignoreCommand = async () => {
|
|
|
1517
2239
|
"**/.kiro/steering/",
|
|
1518
2240
|
"**/.augment/rules/",
|
|
1519
2241
|
"**/.augment-guidelines",
|
|
2242
|
+
"**/.junie/guidelines.md",
|
|
2243
|
+
"**/.noai",
|
|
1520
2244
|
"**/.mcp.json",
|
|
1521
2245
|
"!.rulesync/.mcp.json",
|
|
1522
2246
|
"**/.cursor/mcp.json",
|
|
@@ -1544,7 +2268,7 @@ var gitignoreCommand = async () => {
|
|
|
1544
2268
|
${linesToAdd.join("\n")}
|
|
1545
2269
|
` : `${linesToAdd.join("\n")}
|
|
1546
2270
|
`;
|
|
1547
|
-
|
|
2271
|
+
writeFileSync2(gitignorePath, newContent);
|
|
1548
2272
|
console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
|
|
1549
2273
|
for (const line of linesToAdd) {
|
|
1550
2274
|
if (!line.startsWith("#")) {
|
|
@@ -1554,11 +2278,11 @@ ${linesToAdd.join("\n")}
|
|
|
1554
2278
|
};
|
|
1555
2279
|
|
|
1556
2280
|
// src/core/importer.ts
|
|
1557
|
-
import { join as
|
|
2281
|
+
import { join as join22 } from "path";
|
|
1558
2282
|
import matter5 from "gray-matter";
|
|
1559
2283
|
|
|
1560
2284
|
// src/parsers/augmentcode.ts
|
|
1561
|
-
import { basename as basename2, join as
|
|
2285
|
+
import { basename as basename2, join as join17 } from "path";
|
|
1562
2286
|
import matter2 from "gray-matter";
|
|
1563
2287
|
|
|
1564
2288
|
// src/utils/parser-helpers.ts
|
|
@@ -1599,7 +2323,7 @@ async function safeReadFile(operation, errorContext) {
|
|
|
1599
2323
|
// src/parsers/augmentcode.ts
|
|
1600
2324
|
async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
|
|
1601
2325
|
const result = createParseResult();
|
|
1602
|
-
const rulesDir =
|
|
2326
|
+
const rulesDir = join17(baseDir, ".augment", "rules");
|
|
1603
2327
|
if (await fileExists(rulesDir)) {
|
|
1604
2328
|
const rulesResult = await parseAugmentRules(rulesDir);
|
|
1605
2329
|
addRules(result, rulesResult.rules);
|
|
@@ -1617,7 +2341,7 @@ async function parseAugmentRules(rulesDir) {
|
|
|
1617
2341
|
const files = await readdir2(rulesDir);
|
|
1618
2342
|
for (const file of files) {
|
|
1619
2343
|
if (file.endsWith(".md") || file.endsWith(".mdc")) {
|
|
1620
|
-
const filePath =
|
|
2344
|
+
const filePath = join17(rulesDir, file);
|
|
1621
2345
|
try {
|
|
1622
2346
|
const rawContent = await readFileContent(filePath);
|
|
1623
2347
|
const parsed = matter2(rawContent);
|
|
@@ -1655,10 +2379,10 @@ async function parseAugmentRules(rulesDir) {
|
|
|
1655
2379
|
}
|
|
1656
2380
|
|
|
1657
2381
|
// src/parsers/augmentcode-legacy.ts
|
|
1658
|
-
import { join as
|
|
2382
|
+
import { join as join18 } from "path";
|
|
1659
2383
|
async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
|
|
1660
2384
|
const result = createParseResult();
|
|
1661
|
-
const guidelinesPath =
|
|
2385
|
+
const guidelinesPath = join18(baseDir, ".augment-guidelines");
|
|
1662
2386
|
if (await fileExists(guidelinesPath)) {
|
|
1663
2387
|
const guidelinesResult = await parseAugmentGuidelines(guidelinesPath);
|
|
1664
2388
|
if (guidelinesResult.rule) {
|
|
@@ -1701,13 +2425,13 @@ async function parseAugmentGuidelines(guidelinesPath) {
|
|
|
1701
2425
|
}
|
|
1702
2426
|
|
|
1703
2427
|
// src/parsers/shared-helpers.ts
|
|
1704
|
-
import { basename as basename3, join as
|
|
2428
|
+
import { basename as basename3, join as join19 } from "path";
|
|
1705
2429
|
import matter3 from "gray-matter";
|
|
1706
2430
|
async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
1707
2431
|
const errors = [];
|
|
1708
2432
|
const rules = [];
|
|
1709
2433
|
if (config.mainFile) {
|
|
1710
|
-
const mainFilePath =
|
|
2434
|
+
const mainFilePath = join19(baseDir, config.mainFile.path);
|
|
1711
2435
|
if (await fileExists(mainFilePath)) {
|
|
1712
2436
|
try {
|
|
1713
2437
|
const rawContent = await readFileContent(mainFilePath);
|
|
@@ -1747,14 +2471,14 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
1747
2471
|
}
|
|
1748
2472
|
if (config.directories) {
|
|
1749
2473
|
for (const dirConfig of config.directories) {
|
|
1750
|
-
const dirPath =
|
|
2474
|
+
const dirPath = join19(baseDir, dirConfig.directory);
|
|
1751
2475
|
if (await fileExists(dirPath)) {
|
|
1752
2476
|
try {
|
|
1753
2477
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1754
2478
|
const files = await readdir2(dirPath);
|
|
1755
2479
|
for (const file of files) {
|
|
1756
2480
|
if (file.endsWith(dirConfig.filePattern)) {
|
|
1757
|
-
const filePath =
|
|
2481
|
+
const filePath = join19(dirPath, file);
|
|
1758
2482
|
try {
|
|
1759
2483
|
const rawContent = await readFileContent(filePath);
|
|
1760
2484
|
let content;
|
|
@@ -1802,7 +2526,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
1802
2526
|
const rules = [];
|
|
1803
2527
|
let ignorePatterns;
|
|
1804
2528
|
let mcpServers;
|
|
1805
|
-
const mainFilePath =
|
|
2529
|
+
const mainFilePath = join19(baseDir, config.mainFileName);
|
|
1806
2530
|
if (!await fileExists(mainFilePath)) {
|
|
1807
2531
|
errors.push(`${config.mainFileName} file not found`);
|
|
1808
2532
|
return { rules, errors };
|
|
@@ -1813,12 +2537,12 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
1813
2537
|
if (mainRule) {
|
|
1814
2538
|
rules.push(mainRule);
|
|
1815
2539
|
}
|
|
1816
|
-
const memoryDir =
|
|
2540
|
+
const memoryDir = join19(baseDir, config.memoryDirPath);
|
|
1817
2541
|
if (await fileExists(memoryDir)) {
|
|
1818
2542
|
const memoryRules = await parseMemoryFiles(memoryDir, config);
|
|
1819
2543
|
rules.push(...memoryRules);
|
|
1820
2544
|
}
|
|
1821
|
-
const settingsPath =
|
|
2545
|
+
const settingsPath = join19(baseDir, config.settingsPath);
|
|
1822
2546
|
if (await fileExists(settingsPath)) {
|
|
1823
2547
|
const settingsResult = await parseSettingsFile(settingsPath, config.tool);
|
|
1824
2548
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1830,7 +2554,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
1830
2554
|
errors.push(...settingsResult.errors);
|
|
1831
2555
|
}
|
|
1832
2556
|
if (config.additionalIgnoreFile) {
|
|
1833
|
-
const additionalIgnorePath =
|
|
2557
|
+
const additionalIgnorePath = join19(baseDir, config.additionalIgnoreFile.path);
|
|
1834
2558
|
if (await fileExists(additionalIgnorePath)) {
|
|
1835
2559
|
const additionalPatterns = await config.additionalIgnoreFile.parser(additionalIgnorePath);
|
|
1836
2560
|
if (additionalPatterns.length > 0) {
|
|
@@ -1884,7 +2608,7 @@ async function parseMemoryFiles(memoryDir, config) {
|
|
|
1884
2608
|
const files = await readdir2(memoryDir);
|
|
1885
2609
|
for (const file of files) {
|
|
1886
2610
|
if (file.endsWith(".md")) {
|
|
1887
|
-
const filePath =
|
|
2611
|
+
const filePath = join19(memoryDir, file);
|
|
1888
2612
|
const content = await readFileContent(filePath);
|
|
1889
2613
|
if (content.trim()) {
|
|
1890
2614
|
const filename = basename3(file, ".md");
|
|
@@ -2000,10 +2724,10 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
2000
2724
|
}
|
|
2001
2725
|
|
|
2002
2726
|
// src/parsers/cursor.ts
|
|
2003
|
-
import { basename as basename4, join as
|
|
2727
|
+
import { basename as basename4, join as join20 } from "path";
|
|
2004
2728
|
import matter4 from "gray-matter";
|
|
2005
2729
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
2006
|
-
import { z as
|
|
2730
|
+
import { z as z6 } from "zod/mini";
|
|
2007
2731
|
var customMatterOptions = {
|
|
2008
2732
|
engines: {
|
|
2009
2733
|
yaml: {
|
|
@@ -2031,7 +2755,7 @@ var customMatterOptions = {
|
|
|
2031
2755
|
}
|
|
2032
2756
|
};
|
|
2033
2757
|
function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
2034
|
-
const FrontmatterSchema =
|
|
2758
|
+
const FrontmatterSchema = z6.record(z6.string(), z6.unknown());
|
|
2035
2759
|
const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
|
|
2036
2760
|
if (!parseResult.success) {
|
|
2037
2761
|
return {
|
|
@@ -2125,7 +2849,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
2125
2849
|
const rules = [];
|
|
2126
2850
|
let ignorePatterns;
|
|
2127
2851
|
let mcpServers;
|
|
2128
|
-
const cursorFilePath =
|
|
2852
|
+
const cursorFilePath = join20(baseDir, ".cursorrules");
|
|
2129
2853
|
if (await fileExists(cursorFilePath)) {
|
|
2130
2854
|
try {
|
|
2131
2855
|
const rawContent = await readFileContent(cursorFilePath);
|
|
@@ -2146,14 +2870,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
2146
2870
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
2147
2871
|
}
|
|
2148
2872
|
}
|
|
2149
|
-
const cursorRulesDir =
|
|
2873
|
+
const cursorRulesDir = join20(baseDir, ".cursor", "rules");
|
|
2150
2874
|
if (await fileExists(cursorRulesDir)) {
|
|
2151
2875
|
try {
|
|
2152
2876
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
2153
2877
|
const files = await readdir2(cursorRulesDir);
|
|
2154
2878
|
for (const file of files) {
|
|
2155
2879
|
if (file.endsWith(".mdc")) {
|
|
2156
|
-
const filePath =
|
|
2880
|
+
const filePath = join20(cursorRulesDir, file);
|
|
2157
2881
|
try {
|
|
2158
2882
|
const rawContent = await readFileContent(filePath);
|
|
2159
2883
|
const parsed = matter4(rawContent, customMatterOptions);
|
|
@@ -2182,7 +2906,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
2182
2906
|
if (rules.length === 0) {
|
|
2183
2907
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
2184
2908
|
}
|
|
2185
|
-
const cursorIgnorePath =
|
|
2909
|
+
const cursorIgnorePath = join20(baseDir, ".cursorignore");
|
|
2186
2910
|
if (await fileExists(cursorIgnorePath)) {
|
|
2187
2911
|
try {
|
|
2188
2912
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -2195,7 +2919,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
2195
2919
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
2196
2920
|
}
|
|
2197
2921
|
}
|
|
2198
|
-
const cursorMcpPath =
|
|
2922
|
+
const cursorMcpPath = join20(baseDir, ".cursor", "mcp.json");
|
|
2199
2923
|
if (await fileExists(cursorMcpPath)) {
|
|
2200
2924
|
try {
|
|
2201
2925
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -2243,6 +2967,42 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
2243
2967
|
});
|
|
2244
2968
|
}
|
|
2245
2969
|
|
|
2970
|
+
// src/parsers/junie.ts
|
|
2971
|
+
import { join as join21 } from "path";
|
|
2972
|
+
async function parseJunieConfiguration(baseDir = process.cwd()) {
|
|
2973
|
+
const errors = [];
|
|
2974
|
+
const rules = [];
|
|
2975
|
+
const guidelinesPath = join21(baseDir, ".junie", "guidelines.md");
|
|
2976
|
+
if (!await fileExists(guidelinesPath)) {
|
|
2977
|
+
errors.push(".junie/guidelines.md file not found");
|
|
2978
|
+
return { rules, errors };
|
|
2979
|
+
}
|
|
2980
|
+
try {
|
|
2981
|
+
const content = await readFileContent(guidelinesPath);
|
|
2982
|
+
if (content.trim()) {
|
|
2983
|
+
const frontmatter = {
|
|
2984
|
+
root: false,
|
|
2985
|
+
targets: ["junie"],
|
|
2986
|
+
description: "Junie project guidelines",
|
|
2987
|
+
globs: ["**/*"]
|
|
2988
|
+
};
|
|
2989
|
+
rules.push({
|
|
2990
|
+
frontmatter,
|
|
2991
|
+
content: content.trim(),
|
|
2992
|
+
filename: "junie-guidelines",
|
|
2993
|
+
filepath: guidelinesPath
|
|
2994
|
+
});
|
|
2995
|
+
}
|
|
2996
|
+
} catch (error) {
|
|
2997
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2998
|
+
errors.push(`Failed to parse .junie/guidelines.md: ${errorMessage}`);
|
|
2999
|
+
}
|
|
3000
|
+
if (rules.length === 0) {
|
|
3001
|
+
errors.push("No valid Junie configuration found");
|
|
3002
|
+
}
|
|
3003
|
+
return { rules, errors };
|
|
3004
|
+
}
|
|
3005
|
+
|
|
2246
3006
|
// src/parsers/roo.ts
|
|
2247
3007
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
2248
3008
|
return parseConfigurationFiles(baseDir, {
|
|
@@ -2329,6 +3089,12 @@ async function importConfiguration(options) {
|
|
|
2329
3089
|
mcpServers = geminiResult.mcpServers;
|
|
2330
3090
|
break;
|
|
2331
3091
|
}
|
|
3092
|
+
case "junie": {
|
|
3093
|
+
const junieResult = await parseJunieConfiguration(baseDir);
|
|
3094
|
+
rules = junieResult.rules;
|
|
3095
|
+
errors.push(...junieResult.errors);
|
|
3096
|
+
break;
|
|
3097
|
+
}
|
|
2332
3098
|
default:
|
|
2333
3099
|
errors.push(`Unsupported tool: ${tool}`);
|
|
2334
3100
|
return { success: false, rulesCreated: 0, errors };
|
|
@@ -2341,7 +3107,7 @@ async function importConfiguration(options) {
|
|
|
2341
3107
|
if (rules.length === 0 && !ignorePatterns && !mcpServers) {
|
|
2342
3108
|
return { success: false, rulesCreated: 0, errors };
|
|
2343
3109
|
}
|
|
2344
|
-
const rulesDirPath =
|
|
3110
|
+
const rulesDirPath = join22(baseDir, rulesDir);
|
|
2345
3111
|
try {
|
|
2346
3112
|
const { mkdir: mkdir3 } = await import("fs/promises");
|
|
2347
3113
|
await mkdir3(rulesDirPath, { recursive: true });
|
|
@@ -2355,7 +3121,7 @@ async function importConfiguration(options) {
|
|
|
2355
3121
|
try {
|
|
2356
3122
|
const baseFilename = `${tool}__${rule.filename}`;
|
|
2357
3123
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
2358
|
-
const filePath =
|
|
3124
|
+
const filePath = join22(rulesDirPath, `${filename}.md`);
|
|
2359
3125
|
const content = generateRuleFileContent(rule);
|
|
2360
3126
|
await writeFileContent(filePath, content);
|
|
2361
3127
|
rulesCreated++;
|
|
@@ -2370,7 +3136,7 @@ async function importConfiguration(options) {
|
|
|
2370
3136
|
let ignoreFileCreated = false;
|
|
2371
3137
|
if (ignorePatterns && ignorePatterns.length > 0) {
|
|
2372
3138
|
try {
|
|
2373
|
-
const rulesyncignorePath =
|
|
3139
|
+
const rulesyncignorePath = join22(baseDir, ".rulesyncignore");
|
|
2374
3140
|
const ignoreContent = `${ignorePatterns.join("\n")}
|
|
2375
3141
|
`;
|
|
2376
3142
|
await writeFileContent(rulesyncignorePath, ignoreContent);
|
|
@@ -2386,7 +3152,7 @@ async function importConfiguration(options) {
|
|
|
2386
3152
|
let mcpFileCreated = false;
|
|
2387
3153
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
2388
3154
|
try {
|
|
2389
|
-
const mcpPath =
|
|
3155
|
+
const mcpPath = join22(baseDir, rulesDir, ".mcp.json");
|
|
2390
3156
|
const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
|
|
2391
3157
|
`;
|
|
2392
3158
|
await writeFileContent(mcpPath, mcpContent);
|
|
@@ -2414,7 +3180,7 @@ function generateRuleFileContent(rule) {
|
|
|
2414
3180
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
2415
3181
|
let filename = baseFilename;
|
|
2416
3182
|
let counter = 1;
|
|
2417
|
-
while (await fileExists(
|
|
3183
|
+
while (await fileExists(join22(rulesDir, `${filename}.md`))) {
|
|
2418
3184
|
filename = `${baseFilename}-${counter}`;
|
|
2419
3185
|
counter++;
|
|
2420
3186
|
}
|
|
@@ -2425,7 +3191,7 @@ async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
|
2425
3191
|
async function importCommand(options = {}) {
|
|
2426
3192
|
const tools = [];
|
|
2427
3193
|
if (options.augmentcode) tools.push("augmentcode");
|
|
2428
|
-
if (options
|
|
3194
|
+
if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
|
|
2429
3195
|
if (options.claudecode) tools.push("claudecode");
|
|
2430
3196
|
if (options.cursor) tools.push("cursor");
|
|
2431
3197
|
if (options.copilot) tools.push("copilot");
|
|
@@ -2434,7 +3200,7 @@ async function importCommand(options = {}) {
|
|
|
2434
3200
|
if (options.geminicli) tools.push("geminicli");
|
|
2435
3201
|
if (tools.length === 0) {
|
|
2436
3202
|
console.error(
|
|
2437
|
-
"\u274C Please specify one tool to import from (--augmentcode, --
|
|
3203
|
+
"\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
|
|
2438
3204
|
);
|
|
2439
3205
|
process.exit(1);
|
|
2440
3206
|
}
|
|
@@ -2481,7 +3247,7 @@ async function importCommand(options = {}) {
|
|
|
2481
3247
|
}
|
|
2482
3248
|
|
|
2483
3249
|
// src/cli/commands/init.ts
|
|
2484
|
-
import { join as
|
|
3250
|
+
import { join as join23 } from "path";
|
|
2485
3251
|
async function initCommand() {
|
|
2486
3252
|
const aiRulesDir = ".rulesync";
|
|
2487
3253
|
console.log("Initializing rulesync...");
|
|
@@ -2528,7 +3294,7 @@ globs: ["**/*"]
|
|
|
2528
3294
|
- Follow single responsibility principle
|
|
2529
3295
|
`
|
|
2530
3296
|
};
|
|
2531
|
-
const filepath =
|
|
3297
|
+
const filepath = join23(aiRulesDir, sampleFile.filename);
|
|
2532
3298
|
if (!await fileExists(filepath)) {
|
|
2533
3299
|
await writeFileContent(filepath, sampleFile.content);
|
|
2534
3300
|
console.log(`Created ${filepath}`);
|
|
@@ -2642,11 +3408,11 @@ async function watchCommand() {
|
|
|
2642
3408
|
persistent: true
|
|
2643
3409
|
});
|
|
2644
3410
|
let isGenerating = false;
|
|
2645
|
-
const handleChange = async (
|
|
3411
|
+
const handleChange = async (path5) => {
|
|
2646
3412
|
if (isGenerating) return;
|
|
2647
3413
|
isGenerating = true;
|
|
2648
3414
|
console.log(`
|
|
2649
|
-
\u{1F4DD} Detected change in ${
|
|
3415
|
+
\u{1F4DD} Detected change in ${path5}`);
|
|
2650
3416
|
try {
|
|
2651
3417
|
await generateCommand({ verbose: false });
|
|
2652
3418
|
console.log("\u2705 Regenerated configuration files");
|
|
@@ -2656,10 +3422,10 @@ async function watchCommand() {
|
|
|
2656
3422
|
isGenerating = false;
|
|
2657
3423
|
}
|
|
2658
3424
|
};
|
|
2659
|
-
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (
|
|
3425
|
+
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path5) => {
|
|
2660
3426
|
console.log(`
|
|
2661
|
-
\u{1F5D1}\uFE0F Removed ${
|
|
2662
|
-
handleChange(
|
|
3427
|
+
\u{1F5D1}\uFE0F Removed ${path5}`);
|
|
3428
|
+
handleChange(path5);
|
|
2663
3429
|
}).on("error", (error) => {
|
|
2664
3430
|
console.error("\u274C Watcher error:", error);
|
|
2665
3431
|
});
|
|
@@ -2672,28 +3438,31 @@ async function watchCommand() {
|
|
|
2672
3438
|
|
|
2673
3439
|
// src/cli/index.ts
|
|
2674
3440
|
var program = new Command();
|
|
2675
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
3441
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.51.0");
|
|
2676
3442
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2677
3443
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2678
3444
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
2679
|
-
program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--
|
|
2680
|
-
program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--
|
|
3445
|
+
program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "Import from AugmentCode legacy format (.augment-guidelines)").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("-v, --verbose", "Verbose output").action(importCommand);
|
|
3446
|
+
program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "Generate only for AugmentCode legacy format").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--geminicli", "Generate only for Gemini CLI").option("--junie", "Generate only for JetBrains Junie").option("--kiro", "Generate only for Kiro IDE").option("--delete", "Delete all existing files in output directories before generating").option(
|
|
2681
3447
|
"-b, --base-dir <paths>",
|
|
2682
3448
|
"Base directories to generate files (comma-separated for multiple paths)"
|
|
2683
|
-
).option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
3449
|
+
).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
|
|
2684
3450
|
const tools = [];
|
|
2685
3451
|
if (options.augmentcode) tools.push("augmentcode");
|
|
2686
|
-
if (options
|
|
3452
|
+
if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
|
|
2687
3453
|
if (options.copilot) tools.push("copilot");
|
|
2688
3454
|
if (options.cursor) tools.push("cursor");
|
|
2689
3455
|
if (options.cline) tools.push("cline");
|
|
2690
3456
|
if (options.claudecode) tools.push("claudecode");
|
|
2691
3457
|
if (options.roo) tools.push("roo");
|
|
2692
3458
|
if (options.geminicli) tools.push("geminicli");
|
|
3459
|
+
if (options.junie) tools.push("junie");
|
|
2693
3460
|
if (options.kiro) tools.push("kiro");
|
|
2694
3461
|
const generateOptions = {
|
|
2695
3462
|
verbose: options.verbose,
|
|
2696
|
-
delete: options.delete
|
|
3463
|
+
delete: options.delete,
|
|
3464
|
+
config: options.config,
|
|
3465
|
+
noConfig: options.noConfig
|
|
2697
3466
|
};
|
|
2698
3467
|
if (tools.length > 0) {
|
|
2699
3468
|
generateOptions.tools = tools;
|
|
@@ -2706,4 +3475,5 @@ program.command("generate").description("Generate configuration files for AI too
|
|
|
2706
3475
|
program.command("validate").description("Validate rulesync configuration").action(validateCommand);
|
|
2707
3476
|
program.command("status").description("Show current status of rulesync").action(statusCommand);
|
|
2708
3477
|
program.command("watch").description("Watch for changes and auto-generate configurations").action(watchCommand);
|
|
3478
|
+
program.command("config").description("Show or initialize rulesync configuration").option("--init", "Initialize a new configuration file").option("--format <format>", "Configuration file format (jsonc, ts)", "jsonc").option("--targets <tools>", "Comma-separated list of tools to generate for").option("--exclude <tools>", "Comma-separated list of tools to exclude").option("--ai-rules-dir <dir>", "Directory containing AI rule files").option("--base-dir <path>", "Base directory for generation").option("--verbose", "Enable verbose output").option("--delete", "Delete existing files before generating").action(configCommand);
|
|
2709
3479
|
program.parse();
|