rulesync 0.49.0 → 0.52.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-MCADLVGY.js → chunk-FFW6TGCM.js} +1 -1
- package/dist/chunk-FNL2KPM3.js +63 -0
- package/dist/chunk-HMMTSS5E.js +54 -0
- 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/chunk-YPIFCGAP.js +94 -0
- package/dist/{claudecode-OE4TSKPS.js → claudecode-4XWK2WAY.js} +3 -2
- package/dist/{cline-CWLQS5CV.js → cline-MNXOHP77.js} +3 -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 +1427 -587
- package/dist/index.js +1303 -541
- 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/chunk-XHNIEO22.js +0 -62
- package/dist/chunk-YVRWBSCK.js +0 -73
package/dist/index.js
CHANGED
|
@@ -1,33 +1,39 @@
|
|
|
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-FNL2KPM3.js";
|
|
11
14
|
import {
|
|
12
15
|
generateClineMcp
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-HMMTSS5E.js";
|
|
17
|
+
import "./chunk-YPIFCGAP.js";
|
|
14
18
|
import {
|
|
15
19
|
generateCopilotMcp
|
|
16
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-Y2XH4E5R.js";
|
|
17
21
|
import {
|
|
18
22
|
generateCursorMcp
|
|
19
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-TTHBLXOB.js";
|
|
20
24
|
import {
|
|
21
25
|
generateGeminiCliMcp
|
|
22
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-IXCMY24P.js";
|
|
23
27
|
import {
|
|
24
|
-
|
|
25
|
-
} from "./chunk-
|
|
28
|
+
generateJunieMcp
|
|
29
|
+
} from "./chunk-Y26DXTAT.js";
|
|
26
30
|
import {
|
|
31
|
+
ALL_TOOL_TARGETS,
|
|
27
32
|
RulesyncTargetsSchema,
|
|
28
33
|
ToolTargetSchema,
|
|
29
|
-
ToolTargetsSchema
|
|
30
|
-
|
|
34
|
+
ToolTargetsSchema,
|
|
35
|
+
isToolTarget
|
|
36
|
+
} from "./chunk-USKQYIZ2.js";
|
|
31
37
|
|
|
32
38
|
// src/cli/index.ts
|
|
33
39
|
import { Command } from "commander";
|
|
@@ -49,19 +55,11 @@ function getDefaultConfig() {
|
|
|
49
55
|
claudecode: ".",
|
|
50
56
|
roo: ".roo/rules",
|
|
51
57
|
geminicli: ".gemini/memories",
|
|
52
|
-
kiro: ".kiro/steering"
|
|
58
|
+
kiro: ".kiro/steering",
|
|
59
|
+
junie: "."
|
|
53
60
|
},
|
|
54
61
|
watchEnabled: false,
|
|
55
|
-
defaultTargets:
|
|
56
|
-
"augmentcode",
|
|
57
|
-
"copilot",
|
|
58
|
-
"cursor",
|
|
59
|
-
"cline",
|
|
60
|
-
"claudecode",
|
|
61
|
-
"roo",
|
|
62
|
-
"geminicli",
|
|
63
|
-
"kiro"
|
|
64
|
-
]
|
|
62
|
+
defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy")
|
|
65
63
|
};
|
|
66
64
|
}
|
|
67
65
|
function resolveTargets(targets, config) {
|
|
@@ -108,157 +106,652 @@ async function addCommand(filename) {
|
|
|
108
106
|
}
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
// src/
|
|
112
|
-
import {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
109
|
+
// src/cli/commands/config.ts
|
|
110
|
+
import { writeFileSync } from "fs";
|
|
111
|
+
import path2 from "path";
|
|
112
|
+
|
|
113
|
+
// src/types/claudecode.ts
|
|
114
|
+
import { z } from "zod/mini";
|
|
115
|
+
var ClaudeSettingsSchema = z.looseObject({
|
|
116
|
+
permissions: z._default(
|
|
117
|
+
z.looseObject({
|
|
118
|
+
deny: z._default(z.array(z.string()), [])
|
|
119
|
+
}),
|
|
120
|
+
{ deny: [] }
|
|
121
|
+
)
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// src/types/config.ts
|
|
125
|
+
import { z as z2 } from "zod/mini";
|
|
126
|
+
var ConfigSchema = z2.object({
|
|
127
|
+
aiRulesDir: z2.string(),
|
|
128
|
+
outputPaths: z2.record(ToolTargetSchema, z2.string()),
|
|
129
|
+
watchEnabled: z2.boolean(),
|
|
130
|
+
defaultTargets: ToolTargetsSchema
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// src/types/config-options.ts
|
|
134
|
+
import { z as z3 } from "zod/mini";
|
|
135
|
+
var OutputPathsSchema = z3.object({
|
|
136
|
+
augmentcode: z3.optional(z3.string()),
|
|
137
|
+
"augmentcode-legacy": z3.optional(z3.string()),
|
|
138
|
+
copilot: z3.optional(z3.string()),
|
|
139
|
+
cursor: z3.optional(z3.string()),
|
|
140
|
+
cline: z3.optional(z3.string()),
|
|
141
|
+
claudecode: z3.optional(z3.string()),
|
|
142
|
+
roo: z3.optional(z3.string()),
|
|
143
|
+
geminicli: z3.optional(z3.string()),
|
|
144
|
+
kiro: z3.optional(z3.string()),
|
|
145
|
+
junie: z3.optional(z3.string())
|
|
146
|
+
});
|
|
147
|
+
var ConfigOptionsSchema = z3.object({
|
|
148
|
+
aiRulesDir: z3.optional(z3.string()),
|
|
149
|
+
outputPaths: z3.optional(OutputPathsSchema),
|
|
150
|
+
watchEnabled: z3.optional(z3.boolean()),
|
|
151
|
+
defaultTargets: z3.optional(ToolTargetsSchema),
|
|
152
|
+
targets: z3.optional(z3.array(ToolTargetSchema)),
|
|
153
|
+
exclude: z3.optional(z3.array(ToolTargetSchema)),
|
|
154
|
+
verbose: z3.optional(z3.boolean()),
|
|
155
|
+
delete: z3.optional(z3.boolean()),
|
|
156
|
+
baseDir: z3.optional(z3.union([z3.string(), z3.array(z3.string())])),
|
|
157
|
+
watch: z3.optional(
|
|
158
|
+
z3.object({
|
|
159
|
+
enabled: z3.optional(z3.boolean()),
|
|
160
|
+
interval: z3.optional(z3.number()),
|
|
161
|
+
ignore: z3.optional(z3.array(z3.string()))
|
|
162
|
+
})
|
|
163
|
+
)
|
|
164
|
+
});
|
|
165
|
+
var MergedConfigSchema = z3.object({
|
|
166
|
+
aiRulesDir: z3.string(),
|
|
167
|
+
outputPaths: z3.record(ToolTargetSchema, z3.string()),
|
|
168
|
+
watchEnabled: z3.boolean(),
|
|
169
|
+
defaultTargets: ToolTargetsSchema,
|
|
170
|
+
targets: z3.optional(z3.array(ToolTargetSchema)),
|
|
171
|
+
exclude: z3.optional(z3.array(ToolTargetSchema)),
|
|
172
|
+
verbose: z3.optional(z3.boolean()),
|
|
173
|
+
delete: z3.optional(z3.boolean()),
|
|
174
|
+
baseDir: z3.optional(z3.union([z3.string(), z3.array(z3.string())])),
|
|
175
|
+
configPath: z3.optional(z3.string()),
|
|
176
|
+
watch: z3.optional(
|
|
177
|
+
z3.object({
|
|
178
|
+
enabled: z3.optional(z3.boolean()),
|
|
179
|
+
interval: z3.optional(z3.number()),
|
|
180
|
+
ignore: z3.optional(z3.array(z3.string()))
|
|
181
|
+
})
|
|
182
|
+
)
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// src/types/mcp.ts
|
|
186
|
+
import { z as z4 } from "zod/mini";
|
|
187
|
+
var McpTransportTypeSchema = z4.enum(["stdio", "sse", "http"]);
|
|
188
|
+
var McpServerBaseSchema = z4.object({
|
|
189
|
+
command: z4.optional(z4.string()),
|
|
190
|
+
args: z4.optional(z4.array(z4.string())),
|
|
191
|
+
url: z4.optional(z4.string()),
|
|
192
|
+
httpUrl: z4.optional(z4.string()),
|
|
193
|
+
env: z4.optional(z4.record(z4.string(), z4.string())),
|
|
194
|
+
disabled: z4.optional(z4.boolean()),
|
|
195
|
+
networkTimeout: z4.optional(z4.number()),
|
|
196
|
+
timeout: z4.optional(z4.number()),
|
|
197
|
+
trust: z4.optional(z4.boolean()),
|
|
198
|
+
cwd: z4.optional(z4.string()),
|
|
199
|
+
transport: z4.optional(McpTransportTypeSchema),
|
|
200
|
+
type: z4.optional(z4.enum(["sse", "streamable-http"])),
|
|
201
|
+
alwaysAllow: z4.optional(z4.array(z4.string())),
|
|
202
|
+
tools: z4.optional(z4.array(z4.string())),
|
|
203
|
+
kiroAutoApprove: z4.optional(z4.array(z4.string())),
|
|
204
|
+
kiroAutoBlock: z4.optional(z4.array(z4.string()))
|
|
205
|
+
});
|
|
206
|
+
var RulesyncMcpServerSchema = z4.extend(McpServerBaseSchema, {
|
|
207
|
+
targets: z4.optional(RulesyncTargetsSchema)
|
|
208
|
+
});
|
|
209
|
+
var McpConfigSchema = z4.object({
|
|
210
|
+
mcpServers: z4.record(z4.string(), McpServerBaseSchema)
|
|
211
|
+
});
|
|
212
|
+
var RulesyncMcpConfigSchema = z4.object({
|
|
213
|
+
mcpServers: z4.record(z4.string(), RulesyncMcpServerSchema)
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// src/types/rules.ts
|
|
217
|
+
import { z as z5 } from "zod/mini";
|
|
218
|
+
var RuleFrontmatterSchema = z5.object({
|
|
219
|
+
root: z5.boolean(),
|
|
220
|
+
targets: RulesyncTargetsSchema,
|
|
221
|
+
description: z5.string(),
|
|
222
|
+
globs: z5.array(z5.string()),
|
|
223
|
+
cursorRuleType: z5.optional(z5.enum(["always", "manual", "specificFiles", "intelligently"])),
|
|
224
|
+
tags: z5.optional(z5.array(z5.string()))
|
|
225
|
+
});
|
|
226
|
+
var ParsedRuleSchema = z5.object({
|
|
227
|
+
frontmatter: RuleFrontmatterSchema,
|
|
228
|
+
content: z5.string(),
|
|
229
|
+
filename: z5.string(),
|
|
230
|
+
filepath: z5.string()
|
|
231
|
+
});
|
|
232
|
+
var GeneratedOutputSchema = z5.object({
|
|
233
|
+
tool: ToolTargetSchema,
|
|
234
|
+
filepath: z5.string(),
|
|
235
|
+
content: z5.string()
|
|
236
|
+
});
|
|
237
|
+
var GenerateOptionsSchema = z5.object({
|
|
238
|
+
targetTools: z5.optional(ToolTargetsSchema),
|
|
239
|
+
outputDir: z5.optional(z5.string()),
|
|
240
|
+
watch: z5.optional(z5.boolean())
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// src/utils/config-loader.ts
|
|
244
|
+
import { loadConfig as loadC12Config } from "c12";
|
|
245
|
+
import { $ZodError } from "zod/v4/core";
|
|
246
|
+
var MODULE_NAME = "rulesync";
|
|
247
|
+
async function loadConfig(options = {}) {
|
|
248
|
+
const defaultConfig = getDefaultConfig();
|
|
249
|
+
if (options.noConfig) {
|
|
250
|
+
return {
|
|
251
|
+
config: defaultConfig,
|
|
252
|
+
isEmpty: true
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
try {
|
|
256
|
+
const loadOptions = {
|
|
257
|
+
name: MODULE_NAME,
|
|
258
|
+
cwd: options.cwd || process.cwd(),
|
|
259
|
+
rcFile: false,
|
|
260
|
+
// Disable rc file lookup
|
|
261
|
+
configFile: "rulesync",
|
|
262
|
+
// Will look for rulesync.jsonc, rulesync.ts, etc.
|
|
263
|
+
defaults: defaultConfig
|
|
264
|
+
};
|
|
265
|
+
if (options.configPath) {
|
|
266
|
+
loadOptions.configFile = options.configPath;
|
|
267
|
+
}
|
|
268
|
+
const { config, configFile } = await loadC12Config(loadOptions);
|
|
269
|
+
if (!config || Object.keys(config).length === 0) {
|
|
270
|
+
return {
|
|
271
|
+
config: defaultConfig,
|
|
272
|
+
isEmpty: true
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
ConfigOptionsSchema.parse(config);
|
|
277
|
+
} catch (error) {
|
|
278
|
+
if (error instanceof $ZodError) {
|
|
279
|
+
const issues = error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
|
|
280
|
+
throw new Error(`Invalid configuration in ${configFile}:
|
|
281
|
+
${issues}`);
|
|
282
|
+
}
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
const processedConfig = postProcessConfig(config);
|
|
286
|
+
const result = {
|
|
287
|
+
config: processedConfig,
|
|
288
|
+
isEmpty: false
|
|
289
|
+
};
|
|
290
|
+
if (configFile) {
|
|
291
|
+
result.filepath = configFile;
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
294
|
+
} catch (error) {
|
|
295
|
+
throw new Error(
|
|
296
|
+
`Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
124
299
|
}
|
|
125
|
-
function
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
"# This file provides Augment-specific exclusions and re-inclusions",
|
|
130
|
-
""
|
|
131
|
-
];
|
|
132
|
-
lines.push(
|
|
133
|
-
"# Security and Secrets (critical exclusions)",
|
|
134
|
-
"# Environment files",
|
|
135
|
-
".env*",
|
|
136
|
-
"",
|
|
137
|
-
"# Private keys and certificates",
|
|
138
|
-
"*.pem",
|
|
139
|
-
"*.key",
|
|
140
|
-
"*.p12",
|
|
141
|
-
"*.crt",
|
|
142
|
-
"*.der",
|
|
143
|
-
"",
|
|
144
|
-
"# SSH keys",
|
|
145
|
-
"id_rsa*",
|
|
146
|
-
"id_dsa*",
|
|
147
|
-
"",
|
|
148
|
-
"# AWS credentials",
|
|
149
|
-
".aws/",
|
|
150
|
-
"aws-exports.js",
|
|
151
|
-
"",
|
|
152
|
-
"# API keys and tokens",
|
|
153
|
-
"**/apikeys/",
|
|
154
|
-
"**/*_token*",
|
|
155
|
-
"**/*_secret*",
|
|
156
|
-
""
|
|
157
|
-
);
|
|
158
|
-
lines.push(
|
|
159
|
-
"# Build Artifacts and Dependencies",
|
|
160
|
-
"# Build outputs",
|
|
161
|
-
"dist/",
|
|
162
|
-
"build/",
|
|
163
|
-
"out/",
|
|
164
|
-
"target/",
|
|
165
|
-
"",
|
|
166
|
-
"# Dependencies",
|
|
167
|
-
"node_modules/",
|
|
168
|
-
"venv/",
|
|
169
|
-
"*.egg-info/",
|
|
170
|
-
"",
|
|
171
|
-
"# Logs",
|
|
172
|
-
"*.log",
|
|
173
|
-
"logs/",
|
|
174
|
-
"",
|
|
175
|
-
"# Temporary files",
|
|
176
|
-
"*.tmp",
|
|
177
|
-
"*.swp",
|
|
178
|
-
"*.swo",
|
|
179
|
-
"*~",
|
|
180
|
-
""
|
|
181
|
-
);
|
|
182
|
-
lines.push(
|
|
183
|
-
"# Large Files and Media",
|
|
184
|
-
"# Binary files",
|
|
185
|
-
"*.jar",
|
|
186
|
-
"*.png",
|
|
187
|
-
"*.jpg",
|
|
188
|
-
"*.jpeg",
|
|
189
|
-
"*.gif",
|
|
190
|
-
"*.mp4",
|
|
191
|
-
"*.avi",
|
|
192
|
-
"*.zip",
|
|
193
|
-
"*.tar.gz",
|
|
194
|
-
"*.rar",
|
|
195
|
-
"",
|
|
196
|
-
"# Database files",
|
|
197
|
-
"*.sqlite",
|
|
198
|
-
"*.db",
|
|
199
|
-
"*.mdb",
|
|
200
|
-
"",
|
|
201
|
-
"# Data files",
|
|
202
|
-
"*.csv",
|
|
203
|
-
"*.tsv",
|
|
204
|
-
"*.xlsx",
|
|
205
|
-
""
|
|
206
|
-
);
|
|
207
|
-
lines.push(
|
|
208
|
-
"# Performance Optimization",
|
|
209
|
-
"# Exclude files that are too large for effective AI processing",
|
|
210
|
-
"**/*.{mp4,avi,mov,mkv}",
|
|
211
|
-
"**/*.{zip,tar,gz,rar}",
|
|
212
|
-
"**/*.{pdf,doc,docx}",
|
|
213
|
-
"**/logs/**/*.log",
|
|
214
|
-
"",
|
|
215
|
-
"# But include small configuration files",
|
|
216
|
-
"!**/config.{json,yaml,yml}",
|
|
217
|
-
""
|
|
218
|
-
);
|
|
219
|
-
const rulePatterns = extractIgnorePatternsFromRules(rules);
|
|
220
|
-
if (rulePatterns.length > 0) {
|
|
221
|
-
lines.push("# Project-specific patterns from rulesync rules");
|
|
222
|
-
lines.push(...rulePatterns);
|
|
223
|
-
lines.push("");
|
|
300
|
+
function postProcessConfig(config) {
|
|
301
|
+
const processed = { ...config };
|
|
302
|
+
if (processed.baseDir && !Array.isArray(processed.baseDir)) {
|
|
303
|
+
processed.baseDir = [processed.baseDir];
|
|
224
304
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
)
|
|
305
|
+
if (config.targets || config.exclude) {
|
|
306
|
+
const baseTargets = config.targets || processed.defaultTargets;
|
|
307
|
+
if (config.exclude && config.exclude.length > 0) {
|
|
308
|
+
processed.defaultTargets = baseTargets.filter(
|
|
309
|
+
(target) => config.exclude && !config.exclude.includes(target)
|
|
310
|
+
);
|
|
311
|
+
} else {
|
|
312
|
+
processed.defaultTargets = baseTargets;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return processed;
|
|
316
|
+
}
|
|
317
|
+
function generateMinimalConfig(options) {
|
|
318
|
+
if (!options || Object.keys(options).length === 0) {
|
|
319
|
+
return generateSampleConfig();
|
|
320
|
+
}
|
|
321
|
+
const lines = ["{"];
|
|
322
|
+
if (options.targets || options.exclude) {
|
|
323
|
+
lines.push(` // Available tools: ${ALL_TOOL_TARGETS.join(", ")}`);
|
|
324
|
+
}
|
|
325
|
+
if (options.targets) {
|
|
326
|
+
lines.push(` "targets": ${JSON.stringify(options.targets)}`);
|
|
327
|
+
}
|
|
328
|
+
if (options.exclude) {
|
|
329
|
+
const comma = lines.length > 1 ? "," : "";
|
|
330
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
331
|
+
lines.push(` "exclude": ${JSON.stringify(options.exclude)}`);
|
|
332
|
+
}
|
|
333
|
+
if (options.aiRulesDir) {
|
|
334
|
+
const comma = lines.length > 1 ? "," : "";
|
|
335
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
336
|
+
lines.push(` "aiRulesDir": "${options.aiRulesDir}"`);
|
|
337
|
+
}
|
|
338
|
+
if (options.outputPaths) {
|
|
339
|
+
const comma = lines.length > 1 ? "," : "";
|
|
340
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
341
|
+
lines.push(
|
|
342
|
+
` "outputPaths": ${JSON.stringify(options.outputPaths, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n")}`
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
if (options.baseDir) {
|
|
346
|
+
const comma = lines.length > 1 ? "," : "";
|
|
347
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
348
|
+
lines.push(` "baseDir": ${JSON.stringify(options.baseDir)}`);
|
|
349
|
+
}
|
|
350
|
+
if (options.delete !== void 0) {
|
|
351
|
+
const comma = lines.length > 1 ? "," : "";
|
|
352
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
353
|
+
lines.push(` "delete": ${options.delete}`);
|
|
354
|
+
}
|
|
355
|
+
if (options.verbose !== void 0) {
|
|
356
|
+
const comma = lines.length > 1 ? "," : "";
|
|
357
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
358
|
+
lines.push(` "verbose": ${options.verbose}`);
|
|
359
|
+
}
|
|
360
|
+
if (options.watch) {
|
|
361
|
+
const comma = lines.length > 1 ? "," : "";
|
|
362
|
+
if (comma) lines[lines.length - 1] += comma;
|
|
363
|
+
lines.push(
|
|
364
|
+
` "watch": ${JSON.stringify(options.watch, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n")}`
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
lines.push("}");
|
|
243
368
|
return lines.join("\n");
|
|
244
369
|
}
|
|
370
|
+
function generateSampleConfig(options) {
|
|
371
|
+
const targets = options?.targets || ALL_TOOL_TARGETS;
|
|
372
|
+
const excludeValue = options?.exclude ? JSON.stringify(options.exclude) : null;
|
|
373
|
+
const aiRulesDir = options?.aiRulesDir || null;
|
|
374
|
+
const baseDir = options?.baseDir || null;
|
|
375
|
+
const deleteFlag = options?.delete || false;
|
|
376
|
+
const verbose = options?.verbose !== void 0 ? options.verbose : true;
|
|
377
|
+
return `{
|
|
378
|
+
// List of tools to generate configurations for
|
|
379
|
+
// Available: ${ALL_TOOL_TARGETS.join(", ")}
|
|
380
|
+
"targets": ${JSON.stringify(targets)},
|
|
381
|
+
|
|
382
|
+
// Tools to exclude from generation (overrides targets)
|
|
383
|
+
${excludeValue ? `"exclude": ${excludeValue},` : '// "exclude": ["roo"],'}
|
|
384
|
+
${aiRulesDir ? `
|
|
385
|
+
// Directory containing AI rule files
|
|
386
|
+
"aiRulesDir": "${aiRulesDir}",` : ""}
|
|
387
|
+
|
|
388
|
+
// Custom output paths for specific tools
|
|
389
|
+
"outputPaths": {
|
|
390
|
+
"copilot": ".github/copilot-instructions.md"
|
|
391
|
+
},
|
|
392
|
+
${baseDir ? `
|
|
393
|
+
// Base directory for generation
|
|
394
|
+
"baseDir": "${baseDir}",` : `
|
|
395
|
+
// Base directory or directories for generation
|
|
396
|
+
// "baseDir": "./packages",
|
|
397
|
+
// "baseDir": ["./packages/frontend", "./packages/backend"],`}
|
|
398
|
+
|
|
399
|
+
// Delete existing files before generating
|
|
400
|
+
"delete": ${deleteFlag},
|
|
401
|
+
|
|
402
|
+
// Enable verbose output
|
|
403
|
+
"verbose": ${verbose},
|
|
404
|
+
|
|
405
|
+
// Watch configuration
|
|
406
|
+
"watch": {
|
|
407
|
+
"enabled": false,
|
|
408
|
+
"interval": 1000,
|
|
409
|
+
"ignore": ["node_modules/**", "dist/**"]
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
`;
|
|
413
|
+
}
|
|
414
|
+
function mergeWithCliOptions(config, cliOptions) {
|
|
415
|
+
const merged = { ...config };
|
|
416
|
+
if (cliOptions.verbose !== void 0) {
|
|
417
|
+
merged.verbose = cliOptions.verbose;
|
|
418
|
+
}
|
|
419
|
+
if (cliOptions.delete !== void 0) {
|
|
420
|
+
merged.delete = cliOptions.delete;
|
|
421
|
+
}
|
|
422
|
+
if (cliOptions.baseDirs && cliOptions.baseDirs.length > 0) {
|
|
423
|
+
merged.baseDir = cliOptions.baseDirs;
|
|
424
|
+
}
|
|
425
|
+
if (cliOptions.tools && cliOptions.tools.length > 0) {
|
|
426
|
+
merged.defaultTargets = cliOptions.tools;
|
|
427
|
+
merged.exclude = void 0;
|
|
428
|
+
}
|
|
429
|
+
return merged;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/utils/file.ts
|
|
433
|
+
import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
|
|
434
|
+
import { dirname, join as join2 } from "path";
|
|
435
|
+
async function ensureDir(dirPath) {
|
|
436
|
+
try {
|
|
437
|
+
await stat(dirPath);
|
|
438
|
+
} catch {
|
|
439
|
+
await mkdir2(dirPath, { recursive: true });
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
async function readFileContent(filepath) {
|
|
443
|
+
return readFile(filepath, "utf-8");
|
|
444
|
+
}
|
|
445
|
+
async function writeFileContent(filepath, content) {
|
|
446
|
+
await ensureDir(dirname(filepath));
|
|
447
|
+
await writeFile2(filepath, content, "utf-8");
|
|
448
|
+
}
|
|
449
|
+
async function fileExists(filepath) {
|
|
450
|
+
try {
|
|
451
|
+
await stat(filepath);
|
|
452
|
+
return true;
|
|
453
|
+
} catch {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
async function findFiles(dir, extension = ".md") {
|
|
458
|
+
try {
|
|
459
|
+
const files = await readdir(dir);
|
|
460
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => join2(dir, file));
|
|
461
|
+
} catch {
|
|
462
|
+
return [];
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
async function removeDirectory(dirPath) {
|
|
466
|
+
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
467
|
+
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
468
|
+
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
try {
|
|
472
|
+
if (await fileExists(dirPath)) {
|
|
473
|
+
await rm(dirPath, { recursive: true, force: true });
|
|
474
|
+
}
|
|
475
|
+
} catch (error) {
|
|
476
|
+
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
async function removeFile(filepath) {
|
|
480
|
+
try {
|
|
481
|
+
if (await fileExists(filepath)) {
|
|
482
|
+
await rm(filepath);
|
|
483
|
+
}
|
|
484
|
+
} catch (error) {
|
|
485
|
+
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
async function removeClaudeGeneratedFiles() {
|
|
489
|
+
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
490
|
+
for (const fileOrDir of filesToRemove) {
|
|
491
|
+
if (fileOrDir.endsWith("/memories")) {
|
|
492
|
+
await removeDirectory(fileOrDir);
|
|
493
|
+
} else {
|
|
494
|
+
await removeFile(fileOrDir);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/utils/rules.ts
|
|
500
|
+
function isToolSpecificRule(rule, targetTool) {
|
|
501
|
+
const filename = rule.filename;
|
|
502
|
+
const toolPatterns = {
|
|
503
|
+
"augmentcode-legacy": /^specification-augmentcode-legacy-/i,
|
|
504
|
+
augmentcode: /^specification-augmentcode-/i,
|
|
505
|
+
copilot: /^specification-copilot-/i,
|
|
506
|
+
cursor: /^specification-cursor-/i,
|
|
507
|
+
cline: /^specification-cline-/i,
|
|
508
|
+
claudecode: /^specification-claudecode-/i,
|
|
509
|
+
roo: /^specification-roo-/i,
|
|
510
|
+
geminicli: /^specification-geminicli-/i,
|
|
511
|
+
kiro: /^specification-kiro-/i
|
|
512
|
+
};
|
|
513
|
+
for (const [tool, pattern] of Object.entries(toolPatterns)) {
|
|
514
|
+
if (pattern.test(filename)) {
|
|
515
|
+
return tool === targetTool;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// src/cli/commands/config.ts
|
|
522
|
+
async function configCommand(options = {}) {
|
|
523
|
+
if (options.init) {
|
|
524
|
+
await initConfig(options);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
await showConfig();
|
|
528
|
+
}
|
|
529
|
+
async function showConfig() {
|
|
530
|
+
console.log("Loading configuration...\n");
|
|
531
|
+
try {
|
|
532
|
+
const result = await loadConfig();
|
|
533
|
+
if (result.isEmpty) {
|
|
534
|
+
console.log("No configuration file found. Using default configuration.\n");
|
|
535
|
+
} else {
|
|
536
|
+
console.log(`Configuration loaded from: ${result.filepath}
|
|
537
|
+
`);
|
|
538
|
+
}
|
|
539
|
+
console.log("Current configuration:");
|
|
540
|
+
console.log("=====================");
|
|
541
|
+
const config = result.config;
|
|
542
|
+
console.log(`
|
|
543
|
+
AI Rules Directory: ${config.aiRulesDir}`);
|
|
544
|
+
console.log(`
|
|
545
|
+
Default Targets: ${config.defaultTargets.join(", ")}`);
|
|
546
|
+
if (config.exclude && config.exclude.length > 0) {
|
|
547
|
+
console.log(`Excluded Targets: ${config.exclude.join(", ")}`);
|
|
548
|
+
}
|
|
549
|
+
console.log("\nOutput Paths:");
|
|
550
|
+
for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
|
|
551
|
+
console.log(` ${tool}: ${outputPath}`);
|
|
552
|
+
}
|
|
553
|
+
if (config.baseDir) {
|
|
554
|
+
const dirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
|
|
555
|
+
console.log(`
|
|
556
|
+
Base Directories: ${dirs.join(", ")}`);
|
|
557
|
+
}
|
|
558
|
+
console.log(`
|
|
559
|
+
Verbose: ${config.verbose || false}`);
|
|
560
|
+
console.log(`Delete before generate: ${config.delete || false}`);
|
|
561
|
+
if (config.watch) {
|
|
562
|
+
console.log("\nWatch Configuration:");
|
|
563
|
+
console.log(` Enabled: ${config.watch.enabled || false}`);
|
|
564
|
+
if (config.watch.interval) {
|
|
565
|
+
console.log(` Interval: ${config.watch.interval}ms`);
|
|
566
|
+
}
|
|
567
|
+
if (config.watch.ignore && config.watch.ignore.length > 0) {
|
|
568
|
+
console.log(` Ignore patterns: ${config.watch.ignore.join(", ")}`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
console.log("\nTip: Use 'rulesync config init' to create a configuration file.");
|
|
572
|
+
} catch (error) {
|
|
573
|
+
console.error(
|
|
574
|
+
"\u274C Failed to load configuration:",
|
|
575
|
+
error instanceof Error ? error.message : String(error)
|
|
576
|
+
);
|
|
577
|
+
process.exit(1);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
var FORMAT_CONFIG = {
|
|
581
|
+
jsonc: {
|
|
582
|
+
filename: "rulesync.jsonc",
|
|
583
|
+
generator: generateJsoncConfig
|
|
584
|
+
},
|
|
585
|
+
ts: {
|
|
586
|
+
filename: "rulesync.ts",
|
|
587
|
+
generator: generateTsConfig
|
|
588
|
+
}
|
|
589
|
+
};
|
|
590
|
+
async function initConfig(options) {
|
|
591
|
+
const validFormats = Object.keys(FORMAT_CONFIG);
|
|
592
|
+
const selectedFormat = options.format || "jsonc";
|
|
593
|
+
if (!validFormats.includes(selectedFormat)) {
|
|
594
|
+
console.error(
|
|
595
|
+
`\u274C Invalid format: ${selectedFormat}. Valid formats are: ${validFormats.join(", ")}`
|
|
596
|
+
);
|
|
597
|
+
process.exit(1);
|
|
598
|
+
}
|
|
599
|
+
const formatConfig = FORMAT_CONFIG[selectedFormat];
|
|
600
|
+
const filename = formatConfig.filename;
|
|
601
|
+
const configOptions = {};
|
|
602
|
+
if (options.targets) {
|
|
603
|
+
const targets = options.targets.split(",").map((t) => t.trim());
|
|
604
|
+
const validTargets = [];
|
|
605
|
+
for (const target of targets) {
|
|
606
|
+
const result = ToolTargetSchema.safeParse(target);
|
|
607
|
+
if (result.success) {
|
|
608
|
+
validTargets.push(result.data);
|
|
609
|
+
} else {
|
|
610
|
+
console.error(`\u274C Invalid target: ${target}`);
|
|
611
|
+
process.exit(1);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
configOptions.targets = validTargets;
|
|
615
|
+
}
|
|
616
|
+
if (options.exclude) {
|
|
617
|
+
const excludes = options.exclude.split(",").map((t) => t.trim());
|
|
618
|
+
const validExcludes = [];
|
|
619
|
+
for (const exclude of excludes) {
|
|
620
|
+
const result = ToolTargetSchema.safeParse(exclude);
|
|
621
|
+
if (result.success) {
|
|
622
|
+
validExcludes.push(result.data);
|
|
623
|
+
} else {
|
|
624
|
+
console.error(`\u274C Invalid exclude target: ${exclude}`);
|
|
625
|
+
process.exit(1);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
configOptions.exclude = validExcludes;
|
|
629
|
+
}
|
|
630
|
+
if (options.aiRulesDir) {
|
|
631
|
+
configOptions.aiRulesDir = options.aiRulesDir;
|
|
632
|
+
}
|
|
633
|
+
if (options.baseDir) {
|
|
634
|
+
configOptions.baseDir = options.baseDir;
|
|
635
|
+
}
|
|
636
|
+
if (options.verbose !== void 0) {
|
|
637
|
+
configOptions.verbose = options.verbose;
|
|
638
|
+
}
|
|
639
|
+
if (options.delete !== void 0) {
|
|
640
|
+
configOptions.delete = options.delete;
|
|
641
|
+
}
|
|
642
|
+
const content = formatConfig.generator(configOptions);
|
|
643
|
+
const filepath = path2.join(process.cwd(), filename);
|
|
644
|
+
try {
|
|
645
|
+
const fs2 = await import("fs/promises");
|
|
646
|
+
await fs2.access(filepath);
|
|
647
|
+
console.error(`\u274C Configuration file already exists: ${filepath}`);
|
|
648
|
+
console.log("Remove the existing file or choose a different format.");
|
|
649
|
+
process.exit(1);
|
|
650
|
+
} catch {
|
|
651
|
+
}
|
|
652
|
+
try {
|
|
653
|
+
writeFileSync(filepath, content, "utf-8");
|
|
654
|
+
console.log(`\u2705 Created configuration file: ${filepath}`);
|
|
655
|
+
console.log("\nYou can now customize the configuration to fit your needs.");
|
|
656
|
+
console.log("Run 'rulesync generate' to use the new configuration.");
|
|
657
|
+
} catch (error) {
|
|
658
|
+
console.error(
|
|
659
|
+
`\u274C Failed to create configuration file: ${error instanceof Error ? error.message : String(error)}`
|
|
660
|
+
);
|
|
661
|
+
process.exit(1);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
function generateJsoncConfig(options) {
|
|
665
|
+
if (options && Object.keys(options).length > 0) {
|
|
666
|
+
return generateMinimalConfig(options);
|
|
667
|
+
}
|
|
668
|
+
return generateSampleConfig(options);
|
|
669
|
+
}
|
|
670
|
+
function generateTsConfig(options) {
|
|
671
|
+
if (!options || Object.keys(options).length === 0) {
|
|
672
|
+
return `import type { ConfigOptions } from "rulesync";
|
|
673
|
+
|
|
674
|
+
const config: ConfigOptions = {
|
|
675
|
+
// List of tools to generate configurations for
|
|
676
|
+
// Available: ${ALL_TOOL_TARGETS.join(", ")}
|
|
677
|
+
targets: ${JSON.stringify(ALL_TOOL_TARGETS)},
|
|
678
|
+
|
|
679
|
+
// Custom output paths for specific tools
|
|
680
|
+
// outputPaths: {
|
|
681
|
+
// copilot: ".github/copilot-instructions.md",
|
|
682
|
+
// },
|
|
683
|
+
|
|
684
|
+
// Delete existing files before generating
|
|
685
|
+
// delete: false,
|
|
686
|
+
|
|
687
|
+
// Enable verbose output
|
|
688
|
+
verbose: true,
|
|
689
|
+
};
|
|
690
|
+
|
|
691
|
+
export default config;`;
|
|
692
|
+
}
|
|
693
|
+
const configLines = [];
|
|
694
|
+
if (options.targets) {
|
|
695
|
+
configLines.push(` targets: ${JSON.stringify(options.targets)}`);
|
|
696
|
+
}
|
|
697
|
+
if (options.exclude) {
|
|
698
|
+
configLines.push(` exclude: ${JSON.stringify(options.exclude)}`);
|
|
699
|
+
}
|
|
700
|
+
if (options.aiRulesDir) {
|
|
701
|
+
configLines.push(` aiRulesDir: "${options.aiRulesDir}"`);
|
|
702
|
+
}
|
|
703
|
+
if (options.outputPaths) {
|
|
704
|
+
const pathsStr = JSON.stringify(options.outputPaths, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
|
|
705
|
+
configLines.push(` outputPaths: ${pathsStr}`);
|
|
706
|
+
}
|
|
707
|
+
if (options.baseDir) {
|
|
708
|
+
configLines.push(` baseDir: ${JSON.stringify(options.baseDir)}`);
|
|
709
|
+
}
|
|
710
|
+
if (options.delete !== void 0) {
|
|
711
|
+
configLines.push(` delete: ${options.delete}`);
|
|
712
|
+
}
|
|
713
|
+
if (options.verbose !== void 0) {
|
|
714
|
+
configLines.push(` verbose: ${options.verbose}`);
|
|
715
|
+
}
|
|
716
|
+
if (options.watch) {
|
|
717
|
+
const watchStr = JSON.stringify(options.watch, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
|
|
718
|
+
configLines.push(` watch: ${watchStr}`);
|
|
719
|
+
}
|
|
720
|
+
const configContent = `import type { ConfigOptions } from "rulesync";
|
|
721
|
+
|
|
722
|
+
const config: ConfigOptions = {
|
|
723
|
+
${configLines.join(",\n")},
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
export default config;
|
|
727
|
+
`;
|
|
728
|
+
return configContent;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/cli/commands/generate.ts
|
|
732
|
+
import { join as join13 } from "path";
|
|
733
|
+
|
|
734
|
+
// src/generators/ignore/shared-factory.ts
|
|
735
|
+
import { join as join3 } from "path";
|
|
736
|
+
|
|
737
|
+
// src/generators/ignore/shared-helpers.ts
|
|
245
738
|
function extractIgnorePatternsFromRules(rules) {
|
|
246
739
|
const patterns = [];
|
|
247
740
|
for (const rule of rules) {
|
|
248
741
|
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
249
742
|
for (const glob of rule.frontmatter.globs) {
|
|
250
|
-
if (
|
|
743
|
+
if (shouldExcludeFromAI(glob)) {
|
|
251
744
|
patterns.push(`# Exclude: ${rule.frontmatter.description}`);
|
|
252
745
|
patterns.push(glob);
|
|
253
746
|
}
|
|
254
747
|
}
|
|
255
748
|
}
|
|
256
|
-
const contentPatterns =
|
|
749
|
+
const contentPatterns = extractIgnorePatternsFromContent(rule.content);
|
|
257
750
|
patterns.push(...contentPatterns);
|
|
258
751
|
}
|
|
259
752
|
return patterns;
|
|
260
753
|
}
|
|
261
|
-
function
|
|
754
|
+
function shouldExcludeFromAI(glob) {
|
|
262
755
|
const excludePatterns = [
|
|
263
756
|
// Large generated files that slow indexing
|
|
264
757
|
"**/assets/generated/**",
|
|
@@ -274,8 +767,11 @@ function shouldExcludeFromAugmentCode(glob) {
|
|
|
274
767
|
// Configuration that might contain sensitive data
|
|
275
768
|
"**/config/production/**",
|
|
276
769
|
"**/config/secrets/**",
|
|
770
|
+
"**/config/prod/**",
|
|
277
771
|
"**/deploy/prod/**",
|
|
278
|
-
|
|
772
|
+
"**/*.prod.*",
|
|
773
|
+
// Internal documentation that might be sensitive
|
|
774
|
+
"**/internal/**",
|
|
279
775
|
"**/internal-docs/**",
|
|
280
776
|
"**/proprietary/**",
|
|
281
777
|
"**/personal-notes/**",
|
|
@@ -287,11 +783,17 @@ function shouldExcludeFromAugmentCode(glob) {
|
|
|
287
783
|
return regex.test(glob);
|
|
288
784
|
});
|
|
289
785
|
}
|
|
290
|
-
function
|
|
786
|
+
function extractIgnorePatternsFromContent(content) {
|
|
291
787
|
const patterns = [];
|
|
292
788
|
const lines = content.split("\n");
|
|
293
789
|
for (const line of lines) {
|
|
294
790
|
const trimmed = line.trim();
|
|
791
|
+
if (trimmed.startsWith("# IGNORE:") || trimmed.startsWith("# aiignore:")) {
|
|
792
|
+
const pattern = trimmed.replace(/^# (IGNORE|aiignore):\s*/, "").trim();
|
|
793
|
+
if (pattern) {
|
|
794
|
+
patterns.push(pattern);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
295
797
|
if (trimmed.startsWith("# AUGMENT_IGNORE:") || trimmed.startsWith("# augmentignore:")) {
|
|
296
798
|
const pattern = trimmed.replace(/^# (AUGMENT_IGNORE|augmentignore):\s*/, "").trim();
|
|
297
799
|
if (pattern) {
|
|
@@ -304,8 +806,8 @@ function extractAugmentCodeIgnorePatternsFromContent(content) {
|
|
|
304
806
|
patterns.push(`!${pattern}`);
|
|
305
807
|
}
|
|
306
808
|
}
|
|
307
|
-
if (trimmed.includes("
|
|
308
|
-
const matches = trimmed.match(/['"`]([^'"`]+\.(
|
|
809
|
+
if (trimmed.includes("exclude") || trimmed.includes("ignore")) {
|
|
810
|
+
const matches = trimmed.match(/['"`]([^'"`]+\.(log|tmp|cache|temp))['"`]/g);
|
|
309
811
|
if (matches) {
|
|
310
812
|
patterns.push(...matches.map((m) => m.replace(/['"`]/g, "")));
|
|
311
813
|
}
|
|
@@ -313,206 +815,384 @@ function extractAugmentCodeIgnorePatternsFromContent(content) {
|
|
|
313
815
|
}
|
|
314
816
|
return patterns;
|
|
315
817
|
}
|
|
818
|
+
function extractAugmentCodeIgnorePatternsFromContent(content) {
|
|
819
|
+
const patterns = [];
|
|
820
|
+
const lines = content.split("\n");
|
|
821
|
+
for (const line of lines) {
|
|
822
|
+
const trimmed = line.trim();
|
|
823
|
+
if (trimmed.startsWith("# AUGMENT_IGNORE:") || trimmed.startsWith("# augmentignore:")) {
|
|
824
|
+
const pattern = trimmed.replace(/^# (AUGMENT_IGNORE|augmentignore):\s*/, "").trim();
|
|
825
|
+
if (pattern) {
|
|
826
|
+
patterns.push(pattern);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
if (trimmed.startsWith("# AUGMENT_INCLUDE:") || trimmed.startsWith("# augmentinclude:")) {
|
|
830
|
+
const pattern = trimmed.replace(/^# (AUGMENT_INCLUDE|augmentinclude):\s*/, "").trim();
|
|
831
|
+
if (pattern) {
|
|
832
|
+
patterns.push(`!${pattern}`);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
if (trimmed.includes("large file") || trimmed.includes("binary") || trimmed.includes("media")) {
|
|
836
|
+
const regex = /['"`]([^'"`]+\.(mp4|avi|zip|tar\.gz|rar|pdf|doc|xlsx))['"`]/g;
|
|
837
|
+
let match;
|
|
838
|
+
while ((match = regex.exec(trimmed)) !== null) {
|
|
839
|
+
if (match[1]) {
|
|
840
|
+
patterns.push(match[1]);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return patterns;
|
|
846
|
+
}
|
|
316
847
|
|
|
317
|
-
// src/generators/ignore/
|
|
318
|
-
|
|
319
|
-
async function generateKiroIgnoreFiles(rules, config, baseDir) {
|
|
848
|
+
// src/generators/ignore/shared-factory.ts
|
|
849
|
+
function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
|
|
320
850
|
const outputs = [];
|
|
321
|
-
const
|
|
851
|
+
const content = generateIgnoreContent(rules, ignoreConfig);
|
|
322
852
|
const outputPath = baseDir || process.cwd();
|
|
323
|
-
const filepath = join3(outputPath,
|
|
853
|
+
const filepath = join3(outputPath, ignoreConfig.filename);
|
|
324
854
|
outputs.push({
|
|
325
|
-
tool:
|
|
855
|
+
tool: ignoreConfig.tool,
|
|
326
856
|
filepath,
|
|
327
|
-
content
|
|
857
|
+
content
|
|
328
858
|
});
|
|
329
859
|
return outputs;
|
|
330
860
|
}
|
|
331
|
-
function
|
|
332
|
-
const lines = [
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
"
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
861
|
+
function generateIgnoreContent(rules, config) {
|
|
862
|
+
const lines = [];
|
|
863
|
+
lines.push(...config.header);
|
|
864
|
+
lines.push("");
|
|
865
|
+
if (config.includeCommonPatterns) {
|
|
866
|
+
lines.push(...getCommonIgnorePatterns());
|
|
867
|
+
}
|
|
868
|
+
if (config.corePatterns.length > 0) {
|
|
869
|
+
lines.push(...config.corePatterns);
|
|
870
|
+
lines.push("");
|
|
871
|
+
}
|
|
872
|
+
const rulePatterns = extractIgnorePatternsFromRules(rules);
|
|
873
|
+
const customPatterns = config.customPatternProcessor ? config.customPatternProcessor(rules) : [];
|
|
874
|
+
const allPatterns = [...rulePatterns, ...customPatterns];
|
|
875
|
+
if (allPatterns.length > 0) {
|
|
876
|
+
const headerText = config.projectPatternsHeader || "# \u2500\u2500\u2500\u2500\u2500 Project-specific exclusions from rulesync rules \u2500\u2500\u2500\u2500\u2500";
|
|
877
|
+
lines.push(headerText);
|
|
878
|
+
lines.push(...allPatterns);
|
|
879
|
+
lines.push("");
|
|
880
|
+
}
|
|
881
|
+
return lines.join("\n");
|
|
882
|
+
}
|
|
883
|
+
function getCommonIgnorePatterns() {
|
|
884
|
+
return [
|
|
885
|
+
"# \u2500\u2500\u2500\u2500\u2500 Source Control Metadata \u2500\u2500\u2500\u2500\u2500",
|
|
886
|
+
".git/",
|
|
887
|
+
".svn/",
|
|
888
|
+
".hg/",
|
|
889
|
+
".idea/",
|
|
890
|
+
"*.iml",
|
|
891
|
+
".vscode/settings.json",
|
|
348
892
|
"",
|
|
349
|
-
"#
|
|
350
|
-
"
|
|
351
|
-
"
|
|
893
|
+
"# \u2500\u2500\u2500\u2500\u2500 Build Artifacts \u2500\u2500\u2500\u2500\u2500",
|
|
894
|
+
"/out/",
|
|
895
|
+
"/dist/",
|
|
896
|
+
"/target/",
|
|
897
|
+
"/build/",
|
|
898
|
+
"*.class",
|
|
899
|
+
"*.jar",
|
|
900
|
+
"*.war",
|
|
352
901
|
"",
|
|
353
|
-
"#
|
|
354
|
-
"
|
|
355
|
-
"
|
|
902
|
+
"# \u2500\u2500\u2500\u2500\u2500 Secrets & Credentials \u2500\u2500\u2500\u2500\u2500",
|
|
903
|
+
"# Environment files",
|
|
904
|
+
".env",
|
|
905
|
+
".env.*",
|
|
906
|
+
"!.env.example",
|
|
356
907
|
"",
|
|
357
|
-
"#
|
|
908
|
+
"# Key material",
|
|
358
909
|
"*.pem",
|
|
359
910
|
"*.key",
|
|
360
|
-
"
|
|
361
|
-
""
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
"
|
|
391
|
-
"
|
|
392
|
-
"
|
|
393
|
-
|
|
394
|
-
"**/
|
|
395
|
-
"
|
|
396
|
-
"
|
|
397
|
-
|
|
398
|
-
"**/
|
|
399
|
-
"
|
|
400
|
-
"
|
|
401
|
-
|
|
402
|
-
"
|
|
403
|
-
"
|
|
404
|
-
"
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
911
|
+
"*.crt",
|
|
912
|
+
"*.p12",
|
|
913
|
+
"*.pfx",
|
|
914
|
+
"*.der",
|
|
915
|
+
"id_rsa*",
|
|
916
|
+
"id_dsa*",
|
|
917
|
+
"*.ppk",
|
|
918
|
+
"",
|
|
919
|
+
"# Cloud and service configs",
|
|
920
|
+
"aws-credentials.json",
|
|
921
|
+
"gcp-service-account*.json",
|
|
922
|
+
"azure-credentials.json",
|
|
923
|
+
"secrets/**",
|
|
924
|
+
"config/secrets/",
|
|
925
|
+
"**/secrets/",
|
|
926
|
+
"",
|
|
927
|
+
"# Database credentials",
|
|
928
|
+
"database.yml",
|
|
929
|
+
"**/database/config.*",
|
|
930
|
+
"",
|
|
931
|
+
"# API keys and tokens",
|
|
932
|
+
"**/apikeys/",
|
|
933
|
+
"**/*_token*",
|
|
934
|
+
"**/*_secret*",
|
|
935
|
+
"**/*api_key*",
|
|
936
|
+
"",
|
|
937
|
+
"# \u2500\u2500\u2500\u2500\u2500 Infrastructure & Deployment \u2500\u2500\u2500\u2500\u2500",
|
|
938
|
+
"# Terraform state",
|
|
939
|
+
"*.tfstate",
|
|
940
|
+
"*.tfstate.*",
|
|
941
|
+
".terraform/",
|
|
942
|
+
"",
|
|
943
|
+
"# Kubernetes secrets",
|
|
944
|
+
"**/k8s/**/secret*.yaml",
|
|
945
|
+
"**/kubernetes/**/secret*.yaml",
|
|
946
|
+
"",
|
|
947
|
+
"# Docker secrets",
|
|
948
|
+
"docker-compose.override.yml",
|
|
949
|
+
"**/docker/secrets/",
|
|
950
|
+
"",
|
|
951
|
+
"# \u2500\u2500\u2500\u2500\u2500 Logs & Runtime Data \u2500\u2500\u2500\u2500\u2500",
|
|
952
|
+
"*.log",
|
|
953
|
+
"*.tmp",
|
|
954
|
+
"*.cache",
|
|
955
|
+
"logs/",
|
|
956
|
+
"/var/log/",
|
|
957
|
+
"coverage/",
|
|
958
|
+
".nyc_output/",
|
|
959
|
+
"",
|
|
960
|
+
"# \u2500\u2500\u2500\u2500\u2500 Large Data Files \u2500\u2500\u2500\u2500\u2500",
|
|
961
|
+
"*.csv",
|
|
962
|
+
"*.xlsx",
|
|
963
|
+
"*.sqlite",
|
|
964
|
+
"*.db",
|
|
965
|
+
"*.dump",
|
|
966
|
+
"data/",
|
|
967
|
+
"datasets/",
|
|
968
|
+
"",
|
|
969
|
+
"# \u2500\u2500\u2500\u2500\u2500 Node.js Specific \u2500\u2500\u2500\u2500\u2500",
|
|
970
|
+
"node_modules/",
|
|
971
|
+
".pnpm-store/",
|
|
972
|
+
".yarn/",
|
|
973
|
+
".next/",
|
|
974
|
+
".nuxt/",
|
|
975
|
+
".cache/",
|
|
976
|
+
".parcel-cache/",
|
|
977
|
+
"",
|
|
978
|
+
"# \u2500\u2500\u2500\u2500\u2500 Python Specific \u2500\u2500\u2500\u2500\u2500",
|
|
979
|
+
"__pycache__/",
|
|
980
|
+
"*.pyc",
|
|
981
|
+
"*.pyo",
|
|
982
|
+
"*.pyd",
|
|
983
|
+
".Python",
|
|
984
|
+
"venv/",
|
|
985
|
+
".venv/",
|
|
986
|
+
"env/",
|
|
987
|
+
".env/",
|
|
988
|
+
"",
|
|
989
|
+
"# \u2500\u2500\u2500\u2500\u2500 Java Specific \u2500\u2500\u2500\u2500\u2500",
|
|
990
|
+
"*.class",
|
|
991
|
+
"*.jar",
|
|
992
|
+
"*.war",
|
|
993
|
+
"target/",
|
|
994
|
+
""
|
|
995
|
+
];
|
|
996
|
+
}
|
|
997
|
+
var ignoreConfigs = {
|
|
998
|
+
junie: {
|
|
999
|
+
tool: "junie",
|
|
1000
|
+
filename: ".aiignore",
|
|
1001
|
+
header: [
|
|
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
|
+
corePatterns: [
|
|
1007
|
+
"# \u2500\u2500\u2500\u2500\u2500 Allow specific source files (uncomment as needed) \u2500\u2500\u2500\u2500\u2500",
|
|
1008
|
+
"# !src/**/*.ts",
|
|
1009
|
+
"# !src/**/*.js",
|
|
1010
|
+
"# !lib/**/*.py",
|
|
1011
|
+
"# !src/main/**/*.java"
|
|
1012
|
+
],
|
|
1013
|
+
includeCommonPatterns: true
|
|
1014
|
+
},
|
|
1015
|
+
kiro: {
|
|
1016
|
+
tool: "kiro",
|
|
1017
|
+
filename: ".aiignore",
|
|
1018
|
+
header: [
|
|
1019
|
+
"# Generated by rulesync - Kiro AI-specific exclusions",
|
|
1020
|
+
"# This file excludes files that can be in Git but shouldn't be read by the AI"
|
|
1021
|
+
],
|
|
1022
|
+
corePatterns: [
|
|
1023
|
+
"# Data files AI shouldn't process",
|
|
1024
|
+
"*.csv",
|
|
1025
|
+
"*.tsv",
|
|
1026
|
+
"*.sqlite",
|
|
1027
|
+
"*.db",
|
|
1028
|
+
"",
|
|
1029
|
+
"# Large binary files",
|
|
1030
|
+
"*.zip",
|
|
1031
|
+
"*.tar.gz",
|
|
1032
|
+
"*.rar",
|
|
1033
|
+
"",
|
|
1034
|
+
"# Sensitive documentation",
|
|
1035
|
+
"internal-docs/",
|
|
1036
|
+
"confidential/",
|
|
1037
|
+
"",
|
|
1038
|
+
"# Test data that might confuse AI",
|
|
1039
|
+
"test/fixtures/large-*.json",
|
|
1040
|
+
"benchmark-results/",
|
|
1041
|
+
"",
|
|
1042
|
+
"# Reinforce critical exclusions from .gitignore",
|
|
1043
|
+
"*.pem",
|
|
1044
|
+
"*.key",
|
|
1045
|
+
".env*"
|
|
1046
|
+
],
|
|
1047
|
+
includeCommonPatterns: false,
|
|
1048
|
+
projectPatternsHeader: "# Project-specific exclusions from rulesync rules"
|
|
1049
|
+
},
|
|
1050
|
+
augmentcode: {
|
|
1051
|
+
tool: "augmentcode",
|
|
1052
|
+
filename: ".augmentignore",
|
|
1053
|
+
header: [
|
|
1054
|
+
"# Generated by rulesync - AugmentCode ignore patterns",
|
|
1055
|
+
"# AugmentCode uses a two-tier approach: .gitignore first, then .augmentignore",
|
|
1056
|
+
"# This file provides Augment-specific exclusions and re-inclusions"
|
|
1057
|
+
],
|
|
1058
|
+
corePatterns: [
|
|
1059
|
+
"# Security and Secrets (critical exclusions)",
|
|
1060
|
+
"# Environment files",
|
|
1061
|
+
".env*",
|
|
1062
|
+
"",
|
|
1063
|
+
"# Private keys and certificates",
|
|
1064
|
+
"*.pem",
|
|
1065
|
+
"*.key",
|
|
1066
|
+
"*.p12",
|
|
1067
|
+
"*.crt",
|
|
1068
|
+
"*.der",
|
|
1069
|
+
"",
|
|
1070
|
+
"# SSH keys",
|
|
1071
|
+
"id_rsa*",
|
|
1072
|
+
"id_dsa*",
|
|
1073
|
+
"",
|
|
1074
|
+
"# AWS credentials",
|
|
1075
|
+
".aws/",
|
|
1076
|
+
"aws-exports.js",
|
|
1077
|
+
"",
|
|
1078
|
+
"# API keys and tokens",
|
|
1079
|
+
"**/apikeys/",
|
|
1080
|
+
"**/*_token*",
|
|
1081
|
+
"**/*_secret*",
|
|
1082
|
+
"",
|
|
1083
|
+
"# Build Artifacts and Dependencies",
|
|
1084
|
+
"# Build outputs",
|
|
1085
|
+
"dist/",
|
|
1086
|
+
"build/",
|
|
1087
|
+
"out/",
|
|
1088
|
+
"target/",
|
|
1089
|
+
"",
|
|
1090
|
+
"# Dependencies",
|
|
1091
|
+
"node_modules/",
|
|
1092
|
+
"venv/",
|
|
1093
|
+
"*.egg-info/",
|
|
1094
|
+
"",
|
|
1095
|
+
"# Logs",
|
|
1096
|
+
"*.log",
|
|
1097
|
+
"logs/",
|
|
1098
|
+
"",
|
|
1099
|
+
"# Temporary files",
|
|
1100
|
+
"*.tmp",
|
|
1101
|
+
"*.swp",
|
|
1102
|
+
"*.swo",
|
|
1103
|
+
"*~",
|
|
1104
|
+
"",
|
|
1105
|
+
"# Large Files and Media",
|
|
1106
|
+
"# Binary files",
|
|
1107
|
+
"*.jar",
|
|
1108
|
+
"*.png",
|
|
1109
|
+
"*.jpg",
|
|
1110
|
+
"*.jpeg",
|
|
1111
|
+
"*.gif",
|
|
1112
|
+
"*.mp4",
|
|
1113
|
+
"*.avi",
|
|
1114
|
+
"*.zip",
|
|
1115
|
+
"*.tar.gz",
|
|
1116
|
+
"*.rar",
|
|
1117
|
+
"",
|
|
1118
|
+
"# Database files",
|
|
1119
|
+
"*.sqlite",
|
|
1120
|
+
"*.db",
|
|
1121
|
+
"*.mdb",
|
|
1122
|
+
"",
|
|
1123
|
+
"# Data files",
|
|
1124
|
+
"*.csv",
|
|
1125
|
+
"*.tsv",
|
|
1126
|
+
"*.xlsx",
|
|
1127
|
+
"",
|
|
1128
|
+
"# Performance Optimization",
|
|
1129
|
+
"# Exclude files that are too large for effective AI processing",
|
|
1130
|
+
"**/*.{mp4,avi,mov,mkv}",
|
|
1131
|
+
"**/*.{zip,tar,gz,rar}",
|
|
1132
|
+
"**/*.{pdf,doc,docx}",
|
|
1133
|
+
"**/logs/**/*.log",
|
|
1134
|
+
"",
|
|
1135
|
+
"# But include small configuration files",
|
|
1136
|
+
"!**/config.{json,yaml,yml}",
|
|
1137
|
+
"",
|
|
1138
|
+
"# Team Collaboration",
|
|
1139
|
+
"# Exclude personal IDE settings",
|
|
1140
|
+
".vscode/settings.json",
|
|
1141
|
+
".idea/workspace.xml",
|
|
1142
|
+
"",
|
|
1143
|
+
"# But include shared team settings",
|
|
1144
|
+
"!.vscode/extensions.json",
|
|
1145
|
+
"!.idea/codeStyles/",
|
|
1146
|
+
"",
|
|
1147
|
+
"# Exclude test fixtures with sensitive data",
|
|
1148
|
+
"tests/fixtures/real-data/**",
|
|
1149
|
+
"",
|
|
1150
|
+
"# Re-include important documentation",
|
|
1151
|
+
"!vendor/*/README.md",
|
|
1152
|
+
"!third-party/*/LICENSE"
|
|
1153
|
+
],
|
|
1154
|
+
includeCommonPatterns: false,
|
|
1155
|
+
projectPatternsHeader: "# Project-specific patterns from rulesync rules",
|
|
1156
|
+
customPatternProcessor: (rules) => {
|
|
1157
|
+
const augmentPatterns = [];
|
|
1158
|
+
for (const rule of rules) {
|
|
1159
|
+
augmentPatterns.push(...extractAugmentCodeIgnorePatternsFromContent(rule.content));
|
|
426
1160
|
}
|
|
1161
|
+
return augmentPatterns;
|
|
427
1162
|
}
|
|
428
1163
|
}
|
|
429
|
-
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
// src/generators/ignore/augmentcode.ts
|
|
1167
|
+
async function generateAugmentCodeIgnoreFiles(rules, config, baseDir) {
|
|
1168
|
+
return generateIgnoreFile(rules, config, ignoreConfigs.augmentcode, baseDir);
|
|
430
1169
|
}
|
|
431
1170
|
|
|
432
|
-
// src/generators/
|
|
433
|
-
|
|
1171
|
+
// src/generators/ignore/junie.ts
|
|
1172
|
+
async function generateJunieIgnoreFiles(rules, config, baseDir) {
|
|
1173
|
+
return generateIgnoreFile(rules, config, ignoreConfigs.junie, baseDir);
|
|
1174
|
+
}
|
|
434
1175
|
|
|
435
|
-
// src/generators/
|
|
1176
|
+
// src/generators/ignore/kiro.ts
|
|
1177
|
+
async function generateKiroIgnoreFiles(rules, config, baseDir) {
|
|
1178
|
+
return generateIgnoreFile(rules, config, ignoreConfigs.kiro, baseDir);
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// src/generators/rules/augmentcode.ts
|
|
436
1182
|
import { join as join6 } from "path";
|
|
437
1183
|
|
|
438
|
-
// src/
|
|
1184
|
+
// src/generators/rules/shared-helpers.ts
|
|
439
1185
|
import { join as join5 } from "path";
|
|
440
|
-
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
1186
|
|
|
509
1187
|
// src/utils/ignore.ts
|
|
1188
|
+
import { join as join4 } from "path";
|
|
1189
|
+
import micromatch from "micromatch";
|
|
510
1190
|
var cachedIgnorePatterns = null;
|
|
511
1191
|
async function loadIgnorePatterns(baseDir = process.cwd()) {
|
|
512
1192
|
if (cachedIgnorePatterns) {
|
|
513
1193
|
return cachedIgnorePatterns;
|
|
514
1194
|
}
|
|
515
|
-
const ignorePath =
|
|
1195
|
+
const ignorePath = join4(baseDir, ".rulesyncignore");
|
|
516
1196
|
if (!await fileExists(ignorePath)) {
|
|
517
1197
|
cachedIgnorePatterns = { patterns: [] };
|
|
518
1198
|
return cachedIgnorePatterns;
|
|
@@ -557,7 +1237,7 @@ function filterIgnoredFiles(files, ignorePatterns) {
|
|
|
557
1237
|
|
|
558
1238
|
// src/generators/rules/shared-helpers.ts
|
|
559
1239
|
function resolveOutputDir(config, tool, baseDir) {
|
|
560
|
-
return baseDir ?
|
|
1240
|
+
return baseDir ? join5(baseDir, config.outputPaths[tool]) : config.outputPaths[tool];
|
|
561
1241
|
}
|
|
562
1242
|
function createOutputsArray() {
|
|
563
1243
|
return [];
|
|
@@ -566,7 +1246,7 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
|
|
|
566
1246
|
const outputDir = resolveOutputDir(config, tool, baseDir);
|
|
567
1247
|
outputs.push({
|
|
568
1248
|
tool,
|
|
569
|
-
filepath:
|
|
1249
|
+
filepath: join5(outputDir, relativePath),
|
|
570
1250
|
content
|
|
571
1251
|
});
|
|
572
1252
|
}
|
|
@@ -575,7 +1255,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
|
575
1255
|
for (const rule of rules) {
|
|
576
1256
|
const content = generatorConfig.generateContent(rule);
|
|
577
1257
|
const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
|
|
578
|
-
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) :
|
|
1258
|
+
const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : join5(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
|
|
579
1259
|
outputs.push({
|
|
580
1260
|
tool: generatorConfig.tool,
|
|
581
1261
|
filepath,
|
|
@@ -584,17 +1264,61 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
|
|
|
584
1264
|
}
|
|
585
1265
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
586
1266
|
if (ignorePatterns.patterns.length > 0) {
|
|
587
|
-
const ignorePath = baseDir ?
|
|
588
|
-
const ignoreContent =
|
|
1267
|
+
const ignorePath = baseDir ? join5(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
|
|
1268
|
+
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
|
|
1269
|
+
outputs.push({
|
|
1270
|
+
tool: generatorConfig.tool,
|
|
1271
|
+
filepath: ignorePath,
|
|
1272
|
+
content: ignoreContent
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
return outputs;
|
|
1276
|
+
}
|
|
1277
|
+
async function generateComplexRules(rules, config, generatorConfig, baseDir) {
|
|
1278
|
+
const outputs = [];
|
|
1279
|
+
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
1280
|
+
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
1281
|
+
const rootRule = rootRules[0];
|
|
1282
|
+
if (generatorConfig.generateDetailContent && generatorConfig.detailSubDir) {
|
|
1283
|
+
for (const rule of detailRules) {
|
|
1284
|
+
const content = generatorConfig.generateDetailContent(rule);
|
|
1285
|
+
const filepath = baseDir ? join5(baseDir, generatorConfig.detailSubDir, `${rule.filename}.md`) : join5(generatorConfig.detailSubDir, `${rule.filename}.md`);
|
|
1286
|
+
outputs.push({
|
|
1287
|
+
tool: generatorConfig.tool,
|
|
1288
|
+
filepath,
|
|
1289
|
+
content
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
if (generatorConfig.generateRootContent && generatorConfig.rootFilePath) {
|
|
1294
|
+
const rootContent = generatorConfig.generateRootContent(rootRule, detailRules, baseDir);
|
|
1295
|
+
const rootFilepath = baseDir ? join5(baseDir, generatorConfig.rootFilePath) : generatorConfig.rootFilePath;
|
|
1296
|
+
outputs.push({
|
|
1297
|
+
tool: generatorConfig.tool,
|
|
1298
|
+
filepath: rootFilepath,
|
|
1299
|
+
content: rootContent
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
1303
|
+
if (ignorePatterns.patterns.length > 0) {
|
|
1304
|
+
const ignorePath = baseDir ? join5(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
|
|
1305
|
+
const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
|
|
589
1306
|
outputs.push({
|
|
590
1307
|
tool: generatorConfig.tool,
|
|
591
1308
|
filepath: ignorePath,
|
|
592
1309
|
content: ignoreContent
|
|
593
1310
|
});
|
|
1311
|
+
if (generatorConfig.updateAdditionalConfig) {
|
|
1312
|
+
const additionalOutputs = await generatorConfig.updateAdditionalConfig(
|
|
1313
|
+
ignorePatterns.patterns,
|
|
1314
|
+
baseDir
|
|
1315
|
+
);
|
|
1316
|
+
outputs.push(...additionalOutputs);
|
|
1317
|
+
}
|
|
594
1318
|
}
|
|
595
1319
|
return outputs;
|
|
596
1320
|
}
|
|
597
|
-
function
|
|
1321
|
+
function generateIgnoreFile2(patterns, tool) {
|
|
598
1322
|
const lines = [
|
|
599
1323
|
"# Generated by rulesync from .rulesyncignore",
|
|
600
1324
|
"# This file is automatically generated. Do not edit manually."
|
|
@@ -627,7 +1351,7 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
|
|
|
627
1351
|
"augmentcode",
|
|
628
1352
|
config,
|
|
629
1353
|
baseDir,
|
|
630
|
-
|
|
1354
|
+
join6(".augment", "rules", `${rule.filename}.md`),
|
|
631
1355
|
generateRuleFile(rule)
|
|
632
1356
|
);
|
|
633
1357
|
});
|
|
@@ -680,42 +1404,29 @@ function generateLegacyGuidelinesFile(allRules) {
|
|
|
680
1404
|
}
|
|
681
1405
|
|
|
682
1406
|
// 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
|
|
1407
|
+
import { join as join7 } from "path";
|
|
697
1408
|
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
698
1409
|
const outputs = [];
|
|
699
1410
|
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
700
1411
|
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
701
1412
|
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
702
|
-
const claudeOutputDir = baseDir ?
|
|
1413
|
+
const claudeOutputDir = baseDir ? join7(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
|
|
703
1414
|
outputs.push({
|
|
704
1415
|
tool: "claudecode",
|
|
705
|
-
filepath:
|
|
1416
|
+
filepath: join7(claudeOutputDir, "CLAUDE.md"),
|
|
706
1417
|
content: claudeMdContent
|
|
707
1418
|
});
|
|
708
1419
|
for (const rule of detailRules) {
|
|
709
1420
|
const memoryContent = generateMemoryFile(rule);
|
|
710
1421
|
outputs.push({
|
|
711
1422
|
tool: "claudecode",
|
|
712
|
-
filepath:
|
|
1423
|
+
filepath: join7(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
|
|
713
1424
|
content: memoryContent
|
|
714
1425
|
});
|
|
715
1426
|
}
|
|
716
1427
|
const ignorePatterns = await loadIgnorePatterns(baseDir);
|
|
717
1428
|
if (ignorePatterns.patterns.length > 0) {
|
|
718
|
-
const settingsPath = baseDir ?
|
|
1429
|
+
const settingsPath = baseDir ? join7(baseDir, ".claude", "settings.json") : join7(".claude", "settings.json");
|
|
719
1430
|
await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
|
|
720
1431
|
}
|
|
721
1432
|
return outputs;
|
|
@@ -794,7 +1505,7 @@ async function generateClineConfig(rules, config, baseDir) {
|
|
|
794
1505
|
}
|
|
795
1506
|
|
|
796
1507
|
// src/generators/rules/copilot.ts
|
|
797
|
-
import { join as
|
|
1508
|
+
import { join as join8 } from "path";
|
|
798
1509
|
async function generateCopilotConfig(rules, config, baseDir) {
|
|
799
1510
|
return generateComplexRulesConfig(
|
|
800
1511
|
rules,
|
|
@@ -806,7 +1517,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
|
|
|
806
1517
|
generateContent: generateCopilotMarkdown,
|
|
807
1518
|
getOutputPath: (rule, outputDir) => {
|
|
808
1519
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
809
|
-
return
|
|
1520
|
+
return join8(outputDir, `${baseFilename}.instructions.md`);
|
|
810
1521
|
}
|
|
811
1522
|
},
|
|
812
1523
|
baseDir
|
|
@@ -827,7 +1538,7 @@ function generateCopilotMarkdown(rule) {
|
|
|
827
1538
|
}
|
|
828
1539
|
|
|
829
1540
|
// src/generators/rules/cursor.ts
|
|
830
|
-
import { join as
|
|
1541
|
+
import { join as join9 } from "path";
|
|
831
1542
|
async function generateCursorConfig(rules, config, baseDir) {
|
|
832
1543
|
return generateComplexRulesConfig(
|
|
833
1544
|
rules,
|
|
@@ -838,7 +1549,7 @@ async function generateCursorConfig(rules, config, baseDir) {
|
|
|
838
1549
|
ignoreFileName: ".cursorignore",
|
|
839
1550
|
generateContent: generateCursorMarkdown,
|
|
840
1551
|
getOutputPath: (rule, outputDir) => {
|
|
841
|
-
return
|
|
1552
|
+
return join9(outputDir, `${rule.filename}.mdc`);
|
|
842
1553
|
}
|
|
843
1554
|
},
|
|
844
1555
|
baseDir
|
|
@@ -898,39 +1609,18 @@ function determineCursorRuleType(frontmatter) {
|
|
|
898
1609
|
}
|
|
899
1610
|
|
|
900
1611
|
// src/generators/rules/geminicli.ts
|
|
901
|
-
import { join as join11 } from "path";
|
|
902
1612
|
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({
|
|
1613
|
+
const generatorConfig = {
|
|
919
1614
|
tool: "geminicli",
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
filepath: aiexcludePath,
|
|
930
|
-
content: aiexcludeContent
|
|
931
|
-
});
|
|
932
|
-
}
|
|
933
|
-
return outputs;
|
|
1615
|
+
fileExtension: ".md",
|
|
1616
|
+
ignoreFileName: ".aiexclude",
|
|
1617
|
+
generateContent: generateGeminiMemoryMarkdown,
|
|
1618
|
+
generateDetailContent: generateGeminiMemoryMarkdown,
|
|
1619
|
+
generateRootContent: generateGeminiRootMarkdown,
|
|
1620
|
+
rootFilePath: "GEMINI.md",
|
|
1621
|
+
detailSubDir: ".gemini/memories"
|
|
1622
|
+
};
|
|
1623
|
+
return generateComplexRules(rules, config, generatorConfig, baseDir);
|
|
934
1624
|
}
|
|
935
1625
|
function generateGeminiMemoryMarkdown(rule) {
|
|
936
1626
|
return rule.content.trim();
|
|
@@ -959,24 +1649,42 @@ function generateGeminiRootMarkdown(rootRule, memoryRules, _baseDir) {
|
|
|
959
1649
|
}
|
|
960
1650
|
return lines.join("\n");
|
|
961
1651
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
"",
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1652
|
+
|
|
1653
|
+
// src/generators/rules/junie.ts
|
|
1654
|
+
async function generateJunieConfig(rules, config, baseDir) {
|
|
1655
|
+
const generatorConfig = {
|
|
1656
|
+
tool: "junie",
|
|
1657
|
+
fileExtension: ".md",
|
|
1658
|
+
ignoreFileName: ".aiignore",
|
|
1659
|
+
generateContent: (rule) => rule.content.trim(),
|
|
1660
|
+
generateRootContent: generateGuidelinesMarkdown,
|
|
1661
|
+
rootFilePath: ".junie/guidelines.md"
|
|
1662
|
+
};
|
|
1663
|
+
return generateComplexRules(rules, config, generatorConfig, baseDir);
|
|
1664
|
+
}
|
|
1665
|
+
function generateGuidelinesMarkdown(rootRule, detailRules) {
|
|
1666
|
+
const lines = [];
|
|
1667
|
+
if (rootRule) {
|
|
1668
|
+
lines.push(rootRule.content);
|
|
1669
|
+
lines.push("");
|
|
1670
|
+
}
|
|
1671
|
+
if (detailRules.length > 0) {
|
|
1672
|
+
for (const rule of detailRules) {
|
|
1673
|
+
lines.push(rule.content);
|
|
1674
|
+
lines.push("");
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
return lines.join("\n").trim();
|
|
970
1678
|
}
|
|
971
1679
|
|
|
972
1680
|
// src/generators/rules/kiro.ts
|
|
973
|
-
import { join as
|
|
1681
|
+
import { join as join10 } from "path";
|
|
974
1682
|
async function generateKiroConfig(rules, config, baseDir) {
|
|
975
1683
|
const outputs = [];
|
|
976
1684
|
for (const rule of rules) {
|
|
977
1685
|
const content = generateKiroMarkdown(rule);
|
|
978
|
-
const outputDir = baseDir ?
|
|
979
|
-
const filepath =
|
|
1686
|
+
const outputDir = baseDir ? join10(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
|
|
1687
|
+
const filepath = join10(outputDir, `${rule.filename}.md`);
|
|
980
1688
|
outputs.push({
|
|
981
1689
|
tool: "kiro",
|
|
982
1690
|
filepath,
|
|
@@ -1030,7 +1738,10 @@ async function generateConfigurations(rules, config, targetTools, baseDir) {
|
|
|
1030
1738
|
function filterRulesForTool(rules, tool, config) {
|
|
1031
1739
|
return rules.filter((rule) => {
|
|
1032
1740
|
const targets = resolveTargets(rule.frontmatter.targets, config);
|
|
1033
|
-
|
|
1741
|
+
if (!targets.includes(tool)) {
|
|
1742
|
+
return false;
|
|
1743
|
+
}
|
|
1744
|
+
return isToolSpecificRule(rule, tool);
|
|
1034
1745
|
});
|
|
1035
1746
|
}
|
|
1036
1747
|
async function generateForTool(tool, rules, config, baseDir) {
|
|
@@ -1061,6 +1772,11 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
1061
1772
|
return generateRooConfig(rules, config, baseDir);
|
|
1062
1773
|
case "geminicli":
|
|
1063
1774
|
return generateGeminiConfig(rules, config, baseDir);
|
|
1775
|
+
case "junie": {
|
|
1776
|
+
const junieRulesOutputs = await generateJunieConfig(rules, config, baseDir);
|
|
1777
|
+
const junieIgnoreOutputs = await generateJunieIgnoreFiles(rules, config, baseDir);
|
|
1778
|
+
return [...junieRulesOutputs, ...junieIgnoreOutputs];
|
|
1779
|
+
}
|
|
1064
1780
|
case "kiro": {
|
|
1065
1781
|
const kiroRulesOutputs = await generateKiroConfig(rules, config, baseDir);
|
|
1066
1782
|
const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
|
|
@@ -1075,75 +1791,6 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
1075
1791
|
// src/core/parser.ts
|
|
1076
1792
|
import { basename } from "path";
|
|
1077
1793
|
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
1794
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
1148
1795
|
const ignorePatterns = await loadIgnorePatterns();
|
|
1149
1796
|
const allRuleFiles = await findFiles(aiRulesDir, ".md");
|
|
@@ -1245,13 +1892,13 @@ async function validateRule(rule) {
|
|
|
1245
1892
|
}
|
|
1246
1893
|
|
|
1247
1894
|
// src/core/mcp-generator.ts
|
|
1248
|
-
import * as
|
|
1895
|
+
import * as path4 from "path";
|
|
1249
1896
|
|
|
1250
1897
|
// src/core/mcp-parser.ts
|
|
1251
1898
|
import * as fs from "fs";
|
|
1252
|
-
import * as
|
|
1899
|
+
import * as path3 from "path";
|
|
1253
1900
|
function parseMcpConfig(projectRoot) {
|
|
1254
|
-
const mcpPath =
|
|
1901
|
+
const mcpPath = path3.join(projectRoot, ".rulesync", ".mcp.json");
|
|
1255
1902
|
if (!fs.existsSync(mcpPath)) {
|
|
1256
1903
|
return null;
|
|
1257
1904
|
}
|
|
@@ -1275,7 +1922,7 @@ function parseMcpConfig(projectRoot) {
|
|
|
1275
1922
|
}
|
|
1276
1923
|
|
|
1277
1924
|
// src/core/mcp-generator.ts
|
|
1278
|
-
async function generateMcpConfigs(projectRoot, baseDir) {
|
|
1925
|
+
async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
|
|
1279
1926
|
const results = [];
|
|
1280
1927
|
const targetRoot = baseDir || projectRoot;
|
|
1281
1928
|
const config = parseMcpConfig(projectRoot);
|
|
@@ -1285,55 +1932,70 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1285
1932
|
const generators = [
|
|
1286
1933
|
{
|
|
1287
1934
|
tool: "augmentcode-project",
|
|
1288
|
-
path:
|
|
1935
|
+
path: path4.join(targetRoot, ".mcp.json"),
|
|
1289
1936
|
generate: () => generateAugmentcodeMcp(config)
|
|
1290
1937
|
},
|
|
1291
1938
|
{
|
|
1292
1939
|
tool: "augmentcode-legacy-project",
|
|
1293
|
-
path:
|
|
1940
|
+
path: path4.join(targetRoot, ".mcp.json"),
|
|
1294
1941
|
generate: () => generateAugmentcodeMcp(config)
|
|
1295
1942
|
},
|
|
1296
1943
|
{
|
|
1297
1944
|
tool: "claude-project",
|
|
1298
|
-
path:
|
|
1945
|
+
path: path4.join(targetRoot, ".mcp.json"),
|
|
1299
1946
|
generate: () => generateClaudeMcp(config)
|
|
1300
1947
|
},
|
|
1301
1948
|
{
|
|
1302
1949
|
tool: "copilot-editor",
|
|
1303
|
-
path:
|
|
1950
|
+
path: path4.join(targetRoot, ".vscode", "mcp.json"),
|
|
1304
1951
|
generate: () => generateCopilotMcp(config, "editor")
|
|
1305
1952
|
},
|
|
1306
1953
|
{
|
|
1307
1954
|
tool: "cursor-project",
|
|
1308
|
-
path:
|
|
1955
|
+
path: path4.join(targetRoot, ".cursor", "mcp.json"),
|
|
1309
1956
|
generate: () => generateCursorMcp(config)
|
|
1310
1957
|
},
|
|
1311
1958
|
{
|
|
1312
1959
|
tool: "cline-project",
|
|
1313
|
-
path:
|
|
1960
|
+
path: path4.join(targetRoot, ".cline", "mcp.json"),
|
|
1314
1961
|
generate: () => generateClineMcp(config)
|
|
1315
1962
|
},
|
|
1316
1963
|
{
|
|
1317
1964
|
tool: "gemini-project",
|
|
1318
|
-
path:
|
|
1965
|
+
path: path4.join(targetRoot, ".gemini", "settings.json"),
|
|
1319
1966
|
generate: () => generateGeminiCliMcp(config)
|
|
1320
1967
|
},
|
|
1968
|
+
{
|
|
1969
|
+
tool: "junie-project",
|
|
1970
|
+
path: path4.join(targetRoot, ".junie", "mcp-config.json"),
|
|
1971
|
+
generate: () => generateJunieMcp(config)
|
|
1972
|
+
},
|
|
1321
1973
|
{
|
|
1322
1974
|
tool: "kiro-project",
|
|
1323
|
-
path:
|
|
1975
|
+
path: path4.join(targetRoot, ".kiro", "mcp.json"),
|
|
1324
1976
|
generate: () => generateKiroMcp(config)
|
|
1325
1977
|
},
|
|
1326
1978
|
{
|
|
1327
1979
|
tool: "roo-project",
|
|
1328
|
-
path:
|
|
1980
|
+
path: path4.join(targetRoot, ".roo", "mcp.json"),
|
|
1329
1981
|
generate: () => generateRooMcp(config)
|
|
1330
1982
|
}
|
|
1331
1983
|
];
|
|
1332
|
-
|
|
1984
|
+
const filteredGenerators = targetTools ? generators.filter((g) => {
|
|
1985
|
+
const baseTool = g.tool.split("-")[0];
|
|
1986
|
+
if (!isToolTarget(baseTool)) {
|
|
1987
|
+
return false;
|
|
1988
|
+
}
|
|
1989
|
+
if (baseTool === "augmentcode") {
|
|
1990
|
+
return targetTools.includes("augmentcode") || targetTools.includes("augmentcode-legacy");
|
|
1991
|
+
}
|
|
1992
|
+
return targetTools.includes(baseTool);
|
|
1993
|
+
}) : generators;
|
|
1994
|
+
for (const generator of filteredGenerators) {
|
|
1333
1995
|
try {
|
|
1334
1996
|
const content = generator.generate();
|
|
1335
1997
|
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")) {
|
|
1998
|
+
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
1999
|
if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
|
|
1338
2000
|
results.push({
|
|
1339
2001
|
tool: generator.tool,
|
|
@@ -1373,15 +2035,58 @@ async function generateMcpConfigs(projectRoot, baseDir) {
|
|
|
1373
2035
|
|
|
1374
2036
|
// src/cli/commands/generate.ts
|
|
1375
2037
|
async function generateCommand(options = {}) {
|
|
1376
|
-
const
|
|
1377
|
-
|
|
2038
|
+
const configLoaderOptions = {
|
|
2039
|
+
...options.config !== void 0 && { configPath: options.config },
|
|
2040
|
+
...options.noConfig !== void 0 && { noConfig: options.noConfig }
|
|
2041
|
+
};
|
|
2042
|
+
const configResult = await loadConfig(configLoaderOptions);
|
|
2043
|
+
const cliOptions = {
|
|
2044
|
+
...options.tools !== void 0 && { tools: options.tools },
|
|
2045
|
+
...options.verbose !== void 0 && { verbose: options.verbose },
|
|
2046
|
+
...options.delete !== void 0 && { delete: options.delete },
|
|
2047
|
+
...options.baseDirs !== void 0 && { baseDirs: options.baseDirs }
|
|
2048
|
+
};
|
|
2049
|
+
const config = mergeWithCliOptions(configResult.config, cliOptions);
|
|
2050
|
+
if (options.tools && options.tools.length > 0) {
|
|
2051
|
+
const configTargets = config.defaultTargets;
|
|
2052
|
+
const cliTools = options.tools;
|
|
2053
|
+
const cliToolsSet = new Set(cliTools);
|
|
2054
|
+
const configTargetsSet = new Set(configTargets);
|
|
2055
|
+
const notInConfig = cliTools.filter((tool) => !configTargetsSet.has(tool));
|
|
2056
|
+
const notInCli = configTargets.filter((tool) => !cliToolsSet.has(tool));
|
|
2057
|
+
if (notInConfig.length > 0 || notInCli.length > 0) {
|
|
2058
|
+
console.warn("\u26A0\uFE0F Warning: CLI tool selection differs from configuration!");
|
|
2059
|
+
console.warn(` Config targets: ${configTargets.join(", ")}`);
|
|
2060
|
+
console.warn(` CLI specified: ${cliTools.join(", ")}`);
|
|
2061
|
+
if (notInConfig.length > 0) {
|
|
2062
|
+
console.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
|
|
2063
|
+
}
|
|
2064
|
+
if (notInCli.length > 0) {
|
|
2065
|
+
console.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
|
|
2066
|
+
}
|
|
2067
|
+
console.warn("\n The configuration file targets will be used.");
|
|
2068
|
+
console.warn(" To change targets, update your rulesync config file.");
|
|
2069
|
+
console.warn("");
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
let baseDirs;
|
|
2073
|
+
if (config.baseDir) {
|
|
2074
|
+
baseDirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
|
|
2075
|
+
} else if (options.baseDirs) {
|
|
2076
|
+
baseDirs = options.baseDirs;
|
|
2077
|
+
} else {
|
|
2078
|
+
baseDirs = [process.cwd()];
|
|
2079
|
+
}
|
|
2080
|
+
if (config.verbose && configResult.filepath) {
|
|
2081
|
+
console.log(`Loaded configuration from: ${configResult.filepath}`);
|
|
2082
|
+
}
|
|
1378
2083
|
console.log("Generating configuration files...");
|
|
1379
2084
|
if (!await fileExists(config.aiRulesDir)) {
|
|
1380
2085
|
console.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
|
|
1381
2086
|
process.exit(1);
|
|
1382
2087
|
}
|
|
1383
2088
|
try {
|
|
1384
|
-
if (
|
|
2089
|
+
if (config.verbose) {
|
|
1385
2090
|
console.log(`Parsing rules from ${config.aiRulesDir}...`);
|
|
1386
2091
|
}
|
|
1387
2092
|
const rules = await parseRulesFromDirectory(config.aiRulesDir);
|
|
@@ -1389,18 +2094,26 @@ async function generateCommand(options = {}) {
|
|
|
1389
2094
|
console.warn("\u26A0\uFE0F No rules found in .rulesync directory");
|
|
1390
2095
|
return;
|
|
1391
2096
|
}
|
|
1392
|
-
if (
|
|
2097
|
+
if (config.verbose) {
|
|
1393
2098
|
console.log(`Found ${rules.length} rule(s)`);
|
|
1394
2099
|
console.log(`Base directories: ${baseDirs.join(", ")}`);
|
|
1395
2100
|
}
|
|
1396
|
-
if (
|
|
1397
|
-
if (
|
|
2101
|
+
if (config.delete) {
|
|
2102
|
+
if (config.verbose) {
|
|
1398
2103
|
console.log("Deleting existing output directories...");
|
|
1399
2104
|
}
|
|
1400
|
-
const targetTools =
|
|
2105
|
+
const targetTools = config.defaultTargets;
|
|
1401
2106
|
const deleteTasks = [];
|
|
1402
2107
|
for (const tool of targetTools) {
|
|
1403
2108
|
switch (tool) {
|
|
2109
|
+
case "augmentcode":
|
|
2110
|
+
deleteTasks.push(removeDirectory(join13(".augment", "rules")));
|
|
2111
|
+
deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
|
|
2112
|
+
break;
|
|
2113
|
+
case "augmentcode-legacy":
|
|
2114
|
+
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
2115
|
+
deleteTasks.push(removeDirectory(join13(".augment", "ignore")));
|
|
2116
|
+
break;
|
|
1404
2117
|
case "copilot":
|
|
1405
2118
|
deleteTasks.push(removeDirectory(config.outputPaths.copilot));
|
|
1406
2119
|
break;
|
|
@@ -1425,19 +2138,19 @@ async function generateCommand(options = {}) {
|
|
|
1425
2138
|
}
|
|
1426
2139
|
}
|
|
1427
2140
|
await Promise.all(deleteTasks);
|
|
1428
|
-
if (
|
|
2141
|
+
if (config.verbose) {
|
|
1429
2142
|
console.log("Deleted existing output directories");
|
|
1430
2143
|
}
|
|
1431
2144
|
}
|
|
1432
2145
|
let totalOutputs = 0;
|
|
1433
2146
|
for (const baseDir of baseDirs) {
|
|
1434
|
-
if (
|
|
2147
|
+
if (config.verbose) {
|
|
1435
2148
|
console.log(`
|
|
1436
2149
|
Generating configurations for base directory: ${baseDir}`);
|
|
1437
2150
|
}
|
|
1438
|
-
const outputs = await generateConfigurations(rules, config,
|
|
2151
|
+
const outputs = await generateConfigurations(rules, config, config.defaultTargets, baseDir);
|
|
1439
2152
|
if (outputs.length === 0) {
|
|
1440
|
-
if (
|
|
2153
|
+
if (config.verbose) {
|
|
1441
2154
|
console.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
|
|
1442
2155
|
}
|
|
1443
2156
|
continue;
|
|
@@ -1452,17 +2165,18 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1452
2165
|
console.warn("\u26A0\uFE0F No configurations generated");
|
|
1453
2166
|
return;
|
|
1454
2167
|
}
|
|
1455
|
-
if (
|
|
2168
|
+
if (config.verbose) {
|
|
1456
2169
|
console.log("\nGenerating MCP configurations...");
|
|
1457
2170
|
}
|
|
1458
2171
|
let totalMcpOutputs = 0;
|
|
1459
2172
|
for (const baseDir of baseDirs) {
|
|
1460
2173
|
const mcpResults = await generateMcpConfigs(
|
|
1461
2174
|
process.cwd(),
|
|
1462
|
-
baseDir === process.cwd() ? void 0 : baseDir
|
|
2175
|
+
baseDir === process.cwd() ? void 0 : baseDir,
|
|
2176
|
+
config.defaultTargets
|
|
1463
2177
|
);
|
|
1464
2178
|
if (mcpResults.length === 0) {
|
|
1465
|
-
if (
|
|
2179
|
+
if (config.verbose) {
|
|
1466
2180
|
console.log(`No MCP configuration found for ${baseDir}`);
|
|
1467
2181
|
}
|
|
1468
2182
|
continue;
|
|
@@ -1473,7 +2187,7 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1473
2187
|
totalMcpOutputs++;
|
|
1474
2188
|
} else if (result.status === "error") {
|
|
1475
2189
|
console.error(`\u274C Failed to generate ${result.tool} MCP configuration: ${result.error}`);
|
|
1476
|
-
} else if (
|
|
2190
|
+
} else if (config.verbose && result.status === "skipped") {
|
|
1477
2191
|
console.log(`\u23ED\uFE0F Skipped ${result.tool} MCP configuration (no servers configured)`);
|
|
1478
2192
|
}
|
|
1479
2193
|
}
|
|
@@ -1492,10 +2206,10 @@ Generating configurations for base directory: ${baseDir}`);
|
|
|
1492
2206
|
}
|
|
1493
2207
|
|
|
1494
2208
|
// src/cli/commands/gitignore.ts
|
|
1495
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1496
|
-
import { join as
|
|
2209
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2210
|
+
import { join as join14 } from "path";
|
|
1497
2211
|
var gitignoreCommand = async () => {
|
|
1498
|
-
const gitignorePath =
|
|
2212
|
+
const gitignorePath = join14(process.cwd(), ".gitignore");
|
|
1499
2213
|
const rulesFilesToIgnore = [
|
|
1500
2214
|
"# Generated by rulesync - AI tool configuration files",
|
|
1501
2215
|
"**/.github/copilot-instructions.md",
|
|
@@ -1517,6 +2231,8 @@ var gitignoreCommand = async () => {
|
|
|
1517
2231
|
"**/.kiro/steering/",
|
|
1518
2232
|
"**/.augment/rules/",
|
|
1519
2233
|
"**/.augment-guidelines",
|
|
2234
|
+
"**/.junie/guidelines.md",
|
|
2235
|
+
"**/.noai",
|
|
1520
2236
|
"**/.mcp.json",
|
|
1521
2237
|
"!.rulesync/.mcp.json",
|
|
1522
2238
|
"**/.cursor/mcp.json",
|
|
@@ -1544,7 +2260,7 @@ var gitignoreCommand = async () => {
|
|
|
1544
2260
|
${linesToAdd.join("\n")}
|
|
1545
2261
|
` : `${linesToAdd.join("\n")}
|
|
1546
2262
|
`;
|
|
1547
|
-
|
|
2263
|
+
writeFileSync2(gitignorePath, newContent);
|
|
1548
2264
|
console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
|
|
1549
2265
|
for (const line of linesToAdd) {
|
|
1550
2266
|
if (!line.startsWith("#")) {
|
|
@@ -1558,7 +2274,7 @@ import { join as join20 } from "path";
|
|
|
1558
2274
|
import matter5 from "gray-matter";
|
|
1559
2275
|
|
|
1560
2276
|
// src/parsers/augmentcode.ts
|
|
1561
|
-
import { basename as basename2, join as
|
|
2277
|
+
import { basename as basename2, join as join15 } from "path";
|
|
1562
2278
|
import matter2 from "gray-matter";
|
|
1563
2279
|
|
|
1564
2280
|
// src/utils/parser-helpers.ts
|
|
@@ -1599,7 +2315,7 @@ async function safeReadFile(operation, errorContext) {
|
|
|
1599
2315
|
// src/parsers/augmentcode.ts
|
|
1600
2316
|
async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
|
|
1601
2317
|
const result = createParseResult();
|
|
1602
|
-
const rulesDir =
|
|
2318
|
+
const rulesDir = join15(baseDir, ".augment", "rules");
|
|
1603
2319
|
if (await fileExists(rulesDir)) {
|
|
1604
2320
|
const rulesResult = await parseAugmentRules(rulesDir);
|
|
1605
2321
|
addRules(result, rulesResult.rules);
|
|
@@ -1617,7 +2333,7 @@ async function parseAugmentRules(rulesDir) {
|
|
|
1617
2333
|
const files = await readdir2(rulesDir);
|
|
1618
2334
|
for (const file of files) {
|
|
1619
2335
|
if (file.endsWith(".md") || file.endsWith(".mdc")) {
|
|
1620
|
-
const filePath =
|
|
2336
|
+
const filePath = join15(rulesDir, file);
|
|
1621
2337
|
try {
|
|
1622
2338
|
const rawContent = await readFileContent(filePath);
|
|
1623
2339
|
const parsed = matter2(rawContent);
|
|
@@ -1655,10 +2371,10 @@ async function parseAugmentRules(rulesDir) {
|
|
|
1655
2371
|
}
|
|
1656
2372
|
|
|
1657
2373
|
// src/parsers/augmentcode-legacy.ts
|
|
1658
|
-
import { join as
|
|
2374
|
+
import { join as join16 } from "path";
|
|
1659
2375
|
async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
|
|
1660
2376
|
const result = createParseResult();
|
|
1661
|
-
const guidelinesPath =
|
|
2377
|
+
const guidelinesPath = join16(baseDir, ".augment-guidelines");
|
|
1662
2378
|
if (await fileExists(guidelinesPath)) {
|
|
1663
2379
|
const guidelinesResult = await parseAugmentGuidelines(guidelinesPath);
|
|
1664
2380
|
if (guidelinesResult.rule) {
|
|
@@ -1701,13 +2417,13 @@ async function parseAugmentGuidelines(guidelinesPath) {
|
|
|
1701
2417
|
}
|
|
1702
2418
|
|
|
1703
2419
|
// src/parsers/shared-helpers.ts
|
|
1704
|
-
import { basename as basename3, join as
|
|
2420
|
+
import { basename as basename3, join as join17 } from "path";
|
|
1705
2421
|
import matter3 from "gray-matter";
|
|
1706
2422
|
async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
1707
2423
|
const errors = [];
|
|
1708
2424
|
const rules = [];
|
|
1709
2425
|
if (config.mainFile) {
|
|
1710
|
-
const mainFilePath =
|
|
2426
|
+
const mainFilePath = join17(baseDir, config.mainFile.path);
|
|
1711
2427
|
if (await fileExists(mainFilePath)) {
|
|
1712
2428
|
try {
|
|
1713
2429
|
const rawContent = await readFileContent(mainFilePath);
|
|
@@ -1747,14 +2463,14 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
|
|
|
1747
2463
|
}
|
|
1748
2464
|
if (config.directories) {
|
|
1749
2465
|
for (const dirConfig of config.directories) {
|
|
1750
|
-
const dirPath =
|
|
2466
|
+
const dirPath = join17(baseDir, dirConfig.directory);
|
|
1751
2467
|
if (await fileExists(dirPath)) {
|
|
1752
2468
|
try {
|
|
1753
2469
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
1754
2470
|
const files = await readdir2(dirPath);
|
|
1755
2471
|
for (const file of files) {
|
|
1756
2472
|
if (file.endsWith(dirConfig.filePattern)) {
|
|
1757
|
-
const filePath =
|
|
2473
|
+
const filePath = join17(dirPath, file);
|
|
1758
2474
|
try {
|
|
1759
2475
|
const rawContent = await readFileContent(filePath);
|
|
1760
2476
|
let content;
|
|
@@ -1802,7 +2518,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
1802
2518
|
const rules = [];
|
|
1803
2519
|
let ignorePatterns;
|
|
1804
2520
|
let mcpServers;
|
|
1805
|
-
const mainFilePath =
|
|
2521
|
+
const mainFilePath = join17(baseDir, config.mainFileName);
|
|
1806
2522
|
if (!await fileExists(mainFilePath)) {
|
|
1807
2523
|
errors.push(`${config.mainFileName} file not found`);
|
|
1808
2524
|
return { rules, errors };
|
|
@@ -1813,12 +2529,12 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
1813
2529
|
if (mainRule) {
|
|
1814
2530
|
rules.push(mainRule);
|
|
1815
2531
|
}
|
|
1816
|
-
const memoryDir =
|
|
2532
|
+
const memoryDir = join17(baseDir, config.memoryDirPath);
|
|
1817
2533
|
if (await fileExists(memoryDir)) {
|
|
1818
2534
|
const memoryRules = await parseMemoryFiles(memoryDir, config);
|
|
1819
2535
|
rules.push(...memoryRules);
|
|
1820
2536
|
}
|
|
1821
|
-
const settingsPath =
|
|
2537
|
+
const settingsPath = join17(baseDir, config.settingsPath);
|
|
1822
2538
|
if (await fileExists(settingsPath)) {
|
|
1823
2539
|
const settingsResult = await parseSettingsFile(settingsPath, config.tool);
|
|
1824
2540
|
if (settingsResult.ignorePatterns) {
|
|
@@ -1830,7 +2546,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
|
|
|
1830
2546
|
errors.push(...settingsResult.errors);
|
|
1831
2547
|
}
|
|
1832
2548
|
if (config.additionalIgnoreFile) {
|
|
1833
|
-
const additionalIgnorePath =
|
|
2549
|
+
const additionalIgnorePath = join17(baseDir, config.additionalIgnoreFile.path);
|
|
1834
2550
|
if (await fileExists(additionalIgnorePath)) {
|
|
1835
2551
|
const additionalPatterns = await config.additionalIgnoreFile.parser(additionalIgnorePath);
|
|
1836
2552
|
if (additionalPatterns.length > 0) {
|
|
@@ -1884,7 +2600,7 @@ async function parseMemoryFiles(memoryDir, config) {
|
|
|
1884
2600
|
const files = await readdir2(memoryDir);
|
|
1885
2601
|
for (const file of files) {
|
|
1886
2602
|
if (file.endsWith(".md")) {
|
|
1887
|
-
const filePath =
|
|
2603
|
+
const filePath = join17(memoryDir, file);
|
|
1888
2604
|
const content = await readFileContent(filePath);
|
|
1889
2605
|
if (content.trim()) {
|
|
1890
2606
|
const filename = basename3(file, ".md");
|
|
@@ -2000,10 +2716,10 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
|
2000
2716
|
}
|
|
2001
2717
|
|
|
2002
2718
|
// src/parsers/cursor.ts
|
|
2003
|
-
import { basename as basename4, join as
|
|
2719
|
+
import { basename as basename4, join as join18 } from "path";
|
|
2004
2720
|
import matter4 from "gray-matter";
|
|
2005
2721
|
import { DEFAULT_SCHEMA, FAILSAFE_SCHEMA, load } from "js-yaml";
|
|
2006
|
-
import { z as
|
|
2722
|
+
import { z as z6 } from "zod/mini";
|
|
2007
2723
|
var customMatterOptions = {
|
|
2008
2724
|
engines: {
|
|
2009
2725
|
yaml: {
|
|
@@ -2031,7 +2747,7 @@ var customMatterOptions = {
|
|
|
2031
2747
|
}
|
|
2032
2748
|
};
|
|
2033
2749
|
function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
2034
|
-
const FrontmatterSchema =
|
|
2750
|
+
const FrontmatterSchema = z6.record(z6.string(), z6.unknown());
|
|
2035
2751
|
const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
|
|
2036
2752
|
if (!parseResult.success) {
|
|
2037
2753
|
return {
|
|
@@ -2125,7 +2841,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
2125
2841
|
const rules = [];
|
|
2126
2842
|
let ignorePatterns;
|
|
2127
2843
|
let mcpServers;
|
|
2128
|
-
const cursorFilePath =
|
|
2844
|
+
const cursorFilePath = join18(baseDir, ".cursorrules");
|
|
2129
2845
|
if (await fileExists(cursorFilePath)) {
|
|
2130
2846
|
try {
|
|
2131
2847
|
const rawContent = await readFileContent(cursorFilePath);
|
|
@@ -2146,14 +2862,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
2146
2862
|
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
2147
2863
|
}
|
|
2148
2864
|
}
|
|
2149
|
-
const cursorRulesDir =
|
|
2865
|
+
const cursorRulesDir = join18(baseDir, ".cursor", "rules");
|
|
2150
2866
|
if (await fileExists(cursorRulesDir)) {
|
|
2151
2867
|
try {
|
|
2152
2868
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
2153
2869
|
const files = await readdir2(cursorRulesDir);
|
|
2154
2870
|
for (const file of files) {
|
|
2155
2871
|
if (file.endsWith(".mdc")) {
|
|
2156
|
-
const filePath =
|
|
2872
|
+
const filePath = join18(cursorRulesDir, file);
|
|
2157
2873
|
try {
|
|
2158
2874
|
const rawContent = await readFileContent(filePath);
|
|
2159
2875
|
const parsed = matter4(rawContent, customMatterOptions);
|
|
@@ -2182,7 +2898,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
2182
2898
|
if (rules.length === 0) {
|
|
2183
2899
|
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
2184
2900
|
}
|
|
2185
|
-
const cursorIgnorePath =
|
|
2901
|
+
const cursorIgnorePath = join18(baseDir, ".cursorignore");
|
|
2186
2902
|
if (await fileExists(cursorIgnorePath)) {
|
|
2187
2903
|
try {
|
|
2188
2904
|
const content = await readFileContent(cursorIgnorePath);
|
|
@@ -2195,7 +2911,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
2195
2911
|
errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
|
|
2196
2912
|
}
|
|
2197
2913
|
}
|
|
2198
|
-
const cursorMcpPath =
|
|
2914
|
+
const cursorMcpPath = join18(baseDir, ".cursor", "mcp.json");
|
|
2199
2915
|
if (await fileExists(cursorMcpPath)) {
|
|
2200
2916
|
try {
|
|
2201
2917
|
const content = await readFileContent(cursorMcpPath);
|
|
@@ -2243,6 +2959,42 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
|
|
|
2243
2959
|
});
|
|
2244
2960
|
}
|
|
2245
2961
|
|
|
2962
|
+
// src/parsers/junie.ts
|
|
2963
|
+
import { join as join19 } from "path";
|
|
2964
|
+
async function parseJunieConfiguration(baseDir = process.cwd()) {
|
|
2965
|
+
const errors = [];
|
|
2966
|
+
const rules = [];
|
|
2967
|
+
const guidelinesPath = join19(baseDir, ".junie", "guidelines.md");
|
|
2968
|
+
if (!await fileExists(guidelinesPath)) {
|
|
2969
|
+
errors.push(".junie/guidelines.md file not found");
|
|
2970
|
+
return { rules, errors };
|
|
2971
|
+
}
|
|
2972
|
+
try {
|
|
2973
|
+
const content = await readFileContent(guidelinesPath);
|
|
2974
|
+
if (content.trim()) {
|
|
2975
|
+
const frontmatter = {
|
|
2976
|
+
root: false,
|
|
2977
|
+
targets: ["junie"],
|
|
2978
|
+
description: "Junie project guidelines",
|
|
2979
|
+
globs: ["**/*"]
|
|
2980
|
+
};
|
|
2981
|
+
rules.push({
|
|
2982
|
+
frontmatter,
|
|
2983
|
+
content: content.trim(),
|
|
2984
|
+
filename: "junie-guidelines",
|
|
2985
|
+
filepath: guidelinesPath
|
|
2986
|
+
});
|
|
2987
|
+
}
|
|
2988
|
+
} catch (error) {
|
|
2989
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2990
|
+
errors.push(`Failed to parse .junie/guidelines.md: ${errorMessage}`);
|
|
2991
|
+
}
|
|
2992
|
+
if (rules.length === 0) {
|
|
2993
|
+
errors.push("No valid Junie configuration found");
|
|
2994
|
+
}
|
|
2995
|
+
return { rules, errors };
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2246
2998
|
// src/parsers/roo.ts
|
|
2247
2999
|
async function parseRooConfiguration(baseDir = process.cwd()) {
|
|
2248
3000
|
return parseConfigurationFiles(baseDir, {
|
|
@@ -2329,6 +3081,12 @@ async function importConfiguration(options) {
|
|
|
2329
3081
|
mcpServers = geminiResult.mcpServers;
|
|
2330
3082
|
break;
|
|
2331
3083
|
}
|
|
3084
|
+
case "junie": {
|
|
3085
|
+
const junieResult = await parseJunieConfiguration(baseDir);
|
|
3086
|
+
rules = junieResult.rules;
|
|
3087
|
+
errors.push(...junieResult.errors);
|
|
3088
|
+
break;
|
|
3089
|
+
}
|
|
2332
3090
|
default:
|
|
2333
3091
|
errors.push(`Unsupported tool: ${tool}`);
|
|
2334
3092
|
return { success: false, rulesCreated: 0, errors };
|
|
@@ -2353,7 +3111,7 @@ async function importConfiguration(options) {
|
|
|
2353
3111
|
let rulesCreated = 0;
|
|
2354
3112
|
for (const rule of rules) {
|
|
2355
3113
|
try {
|
|
2356
|
-
const baseFilename =
|
|
3114
|
+
const baseFilename = rule.filename;
|
|
2357
3115
|
const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
|
|
2358
3116
|
const filePath = join20(rulesDirPath, `${filename}.md`);
|
|
2359
3117
|
const content = generateRuleFileContent(rule);
|
|
@@ -2425,7 +3183,7 @@ async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
|
2425
3183
|
async function importCommand(options = {}) {
|
|
2426
3184
|
const tools = [];
|
|
2427
3185
|
if (options.augmentcode) tools.push("augmentcode");
|
|
2428
|
-
if (options
|
|
3186
|
+
if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
|
|
2429
3187
|
if (options.claudecode) tools.push("claudecode");
|
|
2430
3188
|
if (options.cursor) tools.push("cursor");
|
|
2431
3189
|
if (options.copilot) tools.push("copilot");
|
|
@@ -2434,7 +3192,7 @@ async function importCommand(options = {}) {
|
|
|
2434
3192
|
if (options.geminicli) tools.push("geminicli");
|
|
2435
3193
|
if (tools.length === 0) {
|
|
2436
3194
|
console.error(
|
|
2437
|
-
"\u274C Please specify one tool to import from (--augmentcode, --
|
|
3195
|
+
"\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
|
|
2438
3196
|
);
|
|
2439
3197
|
process.exit(1);
|
|
2440
3198
|
}
|
|
@@ -2642,11 +3400,11 @@ async function watchCommand() {
|
|
|
2642
3400
|
persistent: true
|
|
2643
3401
|
});
|
|
2644
3402
|
let isGenerating = false;
|
|
2645
|
-
const handleChange = async (
|
|
3403
|
+
const handleChange = async (path5) => {
|
|
2646
3404
|
if (isGenerating) return;
|
|
2647
3405
|
isGenerating = true;
|
|
2648
3406
|
console.log(`
|
|
2649
|
-
\u{1F4DD} Detected change in ${
|
|
3407
|
+
\u{1F4DD} Detected change in ${path5}`);
|
|
2650
3408
|
try {
|
|
2651
3409
|
await generateCommand({ verbose: false });
|
|
2652
3410
|
console.log("\u2705 Regenerated configuration files");
|
|
@@ -2656,10 +3414,10 @@ async function watchCommand() {
|
|
|
2656
3414
|
isGenerating = false;
|
|
2657
3415
|
}
|
|
2658
3416
|
};
|
|
2659
|
-
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (
|
|
3417
|
+
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path5) => {
|
|
2660
3418
|
console.log(`
|
|
2661
|
-
\u{1F5D1}\uFE0F Removed ${
|
|
2662
|
-
handleChange(
|
|
3419
|
+
\u{1F5D1}\uFE0F Removed ${path5}`);
|
|
3420
|
+
handleChange(path5);
|
|
2663
3421
|
}).on("error", (error) => {
|
|
2664
3422
|
console.error("\u274C Watcher error:", error);
|
|
2665
3423
|
});
|
|
@@ -2672,28 +3430,31 @@ async function watchCommand() {
|
|
|
2672
3430
|
|
|
2673
3431
|
// src/cli/index.ts
|
|
2674
3432
|
var program = new Command();
|
|
2675
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
3433
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.52.0");
|
|
2676
3434
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2677
3435
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2678
3436
|
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("--
|
|
3437
|
+
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);
|
|
3438
|
+
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
3439
|
"-b, --base-dir <paths>",
|
|
2682
3440
|
"Base directories to generate files (comma-separated for multiple paths)"
|
|
2683
|
-
).option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
3441
|
+
).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
|
|
2684
3442
|
const tools = [];
|
|
2685
3443
|
if (options.augmentcode) tools.push("augmentcode");
|
|
2686
|
-
if (options
|
|
3444
|
+
if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
|
|
2687
3445
|
if (options.copilot) tools.push("copilot");
|
|
2688
3446
|
if (options.cursor) tools.push("cursor");
|
|
2689
3447
|
if (options.cline) tools.push("cline");
|
|
2690
3448
|
if (options.claudecode) tools.push("claudecode");
|
|
2691
3449
|
if (options.roo) tools.push("roo");
|
|
2692
3450
|
if (options.geminicli) tools.push("geminicli");
|
|
3451
|
+
if (options.junie) tools.push("junie");
|
|
2693
3452
|
if (options.kiro) tools.push("kiro");
|
|
2694
3453
|
const generateOptions = {
|
|
2695
3454
|
verbose: options.verbose,
|
|
2696
|
-
delete: options.delete
|
|
3455
|
+
delete: options.delete,
|
|
3456
|
+
config: options.config,
|
|
3457
|
+
noConfig: options.noConfig
|
|
2697
3458
|
};
|
|
2698
3459
|
if (tools.length > 0) {
|
|
2699
3460
|
generateOptions.tools = tools;
|
|
@@ -2706,4 +3467,5 @@ program.command("generate").description("Generate configuration files for AI too
|
|
|
2706
3467
|
program.command("validate").description("Validate rulesync configuration").action(validateCommand);
|
|
2707
3468
|
program.command("status").description("Show current status of rulesync").action(statusCommand);
|
|
2708
3469
|
program.command("watch").description("Watch for changes and auto-generate configurations").action(watchCommand);
|
|
3470
|
+
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
3471
|
program.parse();
|