rulesync 0.2.0 → 0.4.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/LICENSE +21 -0
- package/README.md +55 -43
- package/dist/index.js +231 -163
- package/dist/index.mjs +229 -161
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -3,196 +3,178 @@
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
|
-
// src/generators/
|
|
6
|
+
// src/generators/claude.ts
|
|
7
7
|
import { join } from "path";
|
|
8
|
-
async function
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
async function generateClaudeConfig(rules, config) {
|
|
9
|
+
const outputs = [];
|
|
10
|
+
const overviewRules = rules.filter((r) => r.frontmatter.ruleLevel === "overview");
|
|
11
|
+
const detailRules = rules.filter((r) => r.frontmatter.ruleLevel === "detail");
|
|
12
|
+
const claudeMdContent = generateClaudeMarkdown(overviewRules, detailRules);
|
|
13
|
+
outputs.push({
|
|
14
|
+
tool: "claude",
|
|
15
|
+
filepath: join(config.outputPaths.claude, "CLAUDE.md"),
|
|
16
|
+
content: claudeMdContent
|
|
14
17
|
});
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
for (const rule of detailRules) {
|
|
19
|
+
const memoryContent = generateMemoryFile(rule);
|
|
20
|
+
outputs.push({
|
|
21
|
+
tool: "claude",
|
|
22
|
+
filepath: join(config.outputPaths.claude, ".claude", "memories", `${rule.filename}.md`),
|
|
23
|
+
content: memoryContent
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return outputs;
|
|
22
27
|
}
|
|
23
|
-
function
|
|
28
|
+
function generateClaudeMarkdown(overviewRules, detailRules) {
|
|
24
29
|
const lines = [];
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
lines.push("");
|
|
29
|
-
lines.push("These rules provide project-specific guidance for AI-assisted development.");
|
|
30
|
-
lines.push("");
|
|
31
|
-
const highPriorityRules = rules.filter((r) => r.frontmatter.priority === "high");
|
|
32
|
-
const lowPriorityRules = rules.filter((r) => r.frontmatter.priority === "low");
|
|
33
|
-
if (highPriorityRules.length > 0) {
|
|
34
|
-
lines.push("## High Priority Guidelines");
|
|
35
|
-
lines.push("");
|
|
36
|
-
lines.push("These are critical rules that should always be followed:");
|
|
37
|
-
lines.push("");
|
|
38
|
-
for (const rule of highPriorityRules) {
|
|
39
|
-
lines.push(...formatRuleForCline(rule));
|
|
30
|
+
if (detailRules.length > 0) {
|
|
31
|
+
for (const rule of detailRules) {
|
|
32
|
+
lines.push(`@${rule.filename}`);
|
|
40
33
|
}
|
|
41
|
-
}
|
|
42
|
-
if (lowPriorityRules.length > 0) {
|
|
43
|
-
lines.push("## Standard Guidelines");
|
|
44
|
-
lines.push("");
|
|
45
|
-
lines.push("These are recommended practices for this project:");
|
|
46
34
|
lines.push("");
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
}
|
|
36
|
+
lines.push("# Claude Code Memory - Project Instructions");
|
|
37
|
+
lines.push("");
|
|
38
|
+
lines.push(
|
|
39
|
+
"Generated from rulesync configuration. These instructions guide Claude Code's behavior for this project."
|
|
40
|
+
);
|
|
41
|
+
lines.push("");
|
|
42
|
+
if (overviewRules.length > 0) {
|
|
43
|
+
for (const rule of overviewRules) {
|
|
44
|
+
lines.push(...formatRuleForClaude(rule));
|
|
49
45
|
}
|
|
50
46
|
}
|
|
51
47
|
return lines.join("\n");
|
|
52
48
|
}
|
|
53
|
-
function
|
|
49
|
+
function formatRuleForClaude(rule) {
|
|
54
50
|
const lines = [];
|
|
55
51
|
lines.push(`### ${rule.filename}`);
|
|
56
52
|
lines.push("");
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
53
|
+
if (rule.frontmatter.description) {
|
|
54
|
+
lines.push(`**Description:** ${rule.frontmatter.description}`);
|
|
55
|
+
lines.push("");
|
|
56
|
+
}
|
|
57
|
+
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
58
|
+
lines.push(`**File patterns:** ${rule.frontmatter.globs.join(", ")}`);
|
|
61
59
|
lines.push("");
|
|
62
60
|
}
|
|
63
|
-
lines.push("**Guidelines:**");
|
|
64
|
-
lines.push("");
|
|
65
61
|
lines.push(rule.content);
|
|
66
62
|
lines.push("");
|
|
67
|
-
lines.push("---");
|
|
68
|
-
lines.push("");
|
|
69
63
|
return lines;
|
|
70
64
|
}
|
|
71
|
-
|
|
72
|
-
// src/generators/copilot.ts
|
|
73
|
-
import { join as join2 } from "path";
|
|
74
|
-
async function generateCopilotConfig(rules, config) {
|
|
75
|
-
const sortedRules = rules.sort((a, b) => {
|
|
76
|
-
if (a.frontmatter.priority !== b.frontmatter.priority) {
|
|
77
|
-
return a.frontmatter.priority === "high" ? -1 : 1;
|
|
78
|
-
}
|
|
79
|
-
return a.filename.localeCompare(b.filename);
|
|
80
|
-
});
|
|
81
|
-
const content = generateCopilotMarkdown(sortedRules);
|
|
82
|
-
const filepath = join2(config.outputPaths.copilot, "ai-rules.instructions.md");
|
|
83
|
-
return {
|
|
84
|
-
tool: "copilot",
|
|
85
|
-
filepath,
|
|
86
|
-
content
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
function generateCopilotMarkdown(rules) {
|
|
65
|
+
function generateMemoryFile(rule) {
|
|
90
66
|
const lines = [];
|
|
91
|
-
lines.push("
|
|
92
|
-
lines.push('description: "AI rules configuration for GitHub Copilot"');
|
|
93
|
-
lines.push('applyTo: "**"');
|
|
94
|
-
lines.push("---");
|
|
67
|
+
lines.push("Please also refer to the following files as needed:");
|
|
95
68
|
lines.push("");
|
|
96
|
-
lines.push("
|
|
69
|
+
lines.push("---");
|
|
97
70
|
lines.push("");
|
|
98
|
-
lines.push(
|
|
99
|
-
"Generated from ai-rules configuration. These instructions guide GitHub Copilot's code suggestions."
|
|
100
|
-
);
|
|
71
|
+
lines.push(`# ${rule.filename}`);
|
|
101
72
|
lines.push("");
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (highPriorityRules.length > 0) {
|
|
105
|
-
lines.push("## High Priority Rules");
|
|
73
|
+
if (rule.frontmatter.description) {
|
|
74
|
+
lines.push(`**Description:** ${rule.frontmatter.description}`);
|
|
106
75
|
lines.push("");
|
|
107
|
-
for (const rule of highPriorityRules) {
|
|
108
|
-
lines.push(...formatRuleForCopilot(rule));
|
|
109
|
-
}
|
|
110
76
|
}
|
|
111
|
-
if (
|
|
112
|
-
lines.push(
|
|
77
|
+
if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
|
|
78
|
+
lines.push(`**File patterns:** ${rule.frontmatter.globs.join(", ")}`);
|
|
113
79
|
lines.push("");
|
|
114
|
-
for (const rule of lowPriorityRules) {
|
|
115
|
-
lines.push(...formatRuleForCopilot(rule));
|
|
116
|
-
}
|
|
117
80
|
}
|
|
81
|
+
lines.push(rule.content);
|
|
118
82
|
return lines.join("\n");
|
|
119
83
|
}
|
|
120
|
-
|
|
84
|
+
|
|
85
|
+
// src/generators/cline.ts
|
|
86
|
+
import { join as join2 } from "path";
|
|
87
|
+
async function generateClineConfig(rules, config) {
|
|
88
|
+
const outputs = [];
|
|
89
|
+
for (const rule of rules) {
|
|
90
|
+
const content = generateClineMarkdown(rule);
|
|
91
|
+
const filepath = join2(config.outputPaths.cline, `${rule.filename}.md`);
|
|
92
|
+
outputs.push({
|
|
93
|
+
tool: "cline",
|
|
94
|
+
filepath,
|
|
95
|
+
content
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return outputs;
|
|
99
|
+
}
|
|
100
|
+
function generateClineMarkdown(rule) {
|
|
121
101
|
const lines = [];
|
|
122
|
-
lines.push(
|
|
123
|
-
lines.push("");
|
|
124
|
-
lines.push(`**Description:** ${rule.frontmatter.description}`);
|
|
102
|
+
lines.push(`# ${rule.frontmatter.description}`);
|
|
125
103
|
lines.push("");
|
|
126
104
|
if (rule.frontmatter.globs.length > 0) {
|
|
127
|
-
lines.push(`**Applies to:** ${rule.frontmatter.globs.join(", ")}`);
|
|
105
|
+
lines.push(`**Applies to files:** ${rule.frontmatter.globs.join(", ")}`);
|
|
128
106
|
lines.push("");
|
|
129
107
|
}
|
|
130
108
|
lines.push(rule.content);
|
|
131
|
-
lines.
|
|
132
|
-
return lines;
|
|
109
|
+
return lines.join("\n");
|
|
133
110
|
}
|
|
134
111
|
|
|
135
|
-
// src/generators/
|
|
112
|
+
// src/generators/copilot.ts
|
|
136
113
|
import { join as join3 } from "path";
|
|
137
|
-
async function
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
};
|
|
114
|
+
async function generateCopilotConfig(rules, config) {
|
|
115
|
+
const outputs = [];
|
|
116
|
+
for (const rule of rules) {
|
|
117
|
+
const content = generateCopilotMarkdown(rule);
|
|
118
|
+
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
119
|
+
const filepath = join3(config.outputPaths.copilot, `${baseFilename}.instructions.md`);
|
|
120
|
+
outputs.push({
|
|
121
|
+
tool: "copilot",
|
|
122
|
+
filepath,
|
|
123
|
+
content
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return outputs;
|
|
151
127
|
}
|
|
152
|
-
function
|
|
128
|
+
function generateCopilotMarkdown(rule) {
|
|
153
129
|
const lines = [];
|
|
154
130
|
lines.push("---");
|
|
155
|
-
lines.push(
|
|
156
|
-
|
|
131
|
+
lines.push(`description: "${rule.frontmatter.description}"`);
|
|
132
|
+
if (rule.frontmatter.globs.length > 0) {
|
|
133
|
+
lines.push(`applyTo: "${rule.frontmatter.globs.join(", ")}"`);
|
|
134
|
+
} else {
|
|
135
|
+
lines.push('applyTo: "**"');
|
|
136
|
+
}
|
|
157
137
|
lines.push("---");
|
|
158
138
|
lines.push("");
|
|
159
|
-
lines.push(
|
|
160
|
-
lines.
|
|
161
|
-
|
|
162
|
-
|
|
139
|
+
lines.push(rule.content);
|
|
140
|
+
return lines.join("\n");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/generators/cursor.ts
|
|
144
|
+
import { join as join4 } from "path";
|
|
145
|
+
async function generateCursorConfig(rules, config) {
|
|
146
|
+
const outputs = [];
|
|
163
147
|
for (const rule of rules) {
|
|
164
|
-
|
|
148
|
+
const content = generateCursorMarkdown(rule);
|
|
149
|
+
const filepath = join4(config.outputPaths.cursor, `${rule.filename}.md`);
|
|
150
|
+
outputs.push({
|
|
151
|
+
tool: "cursor",
|
|
152
|
+
filepath,
|
|
153
|
+
content
|
|
154
|
+
});
|
|
165
155
|
}
|
|
166
|
-
return
|
|
156
|
+
return outputs;
|
|
167
157
|
}
|
|
168
|
-
function
|
|
158
|
+
function generateCursorMarkdown(rule) {
|
|
169
159
|
const lines = [];
|
|
170
|
-
const priorityBadge = rule.frontmatter.priority === "high" ? "\u{1F534} HIGH" : "\u{1F7E1} STANDARD";
|
|
171
160
|
lines.push("---");
|
|
172
161
|
lines.push(`description: ${rule.frontmatter.description}`);
|
|
173
162
|
if (rule.frontmatter.globs.length > 0) {
|
|
174
163
|
lines.push(`globs: [${rule.frontmatter.globs.map((g) => `"${g}"`).join(", ")}]`);
|
|
175
164
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (rule.frontmatter.globs.length > 0) {
|
|
184
|
-
lines.push("**File Patterns:**");
|
|
185
|
-
for (const glob of rule.frontmatter.globs) {
|
|
186
|
-
lines.push(`- \`${glob}\``);
|
|
187
|
-
}
|
|
188
|
-
lines.push("");
|
|
165
|
+
let ruletype;
|
|
166
|
+
if (rule.frontmatter.ruleLevel === "overview") {
|
|
167
|
+
ruletype = "always";
|
|
168
|
+
} else if (rule.frontmatter.ruleLevel === "detail" && rule.frontmatter.globs.length === 0) {
|
|
169
|
+
ruletype = "agentrequested";
|
|
170
|
+
} else {
|
|
171
|
+
ruletype = "autoattached";
|
|
189
172
|
}
|
|
190
|
-
lines.push(
|
|
191
|
-
lines.push(rule.content);
|
|
192
|
-
lines.push("");
|
|
173
|
+
lines.push(`ruletype: ${ruletype}`);
|
|
193
174
|
lines.push("---");
|
|
194
175
|
lines.push("");
|
|
195
|
-
|
|
176
|
+
lines.push(rule.content);
|
|
177
|
+
return lines.join("\n");
|
|
196
178
|
}
|
|
197
179
|
|
|
198
180
|
// src/utils/config.ts
|
|
@@ -202,10 +184,11 @@ function getDefaultConfig() {
|
|
|
202
184
|
outputPaths: {
|
|
203
185
|
copilot: ".github/instructions",
|
|
204
186
|
cursor: ".cursor/rules",
|
|
205
|
-
cline: ".clinerules"
|
|
187
|
+
cline: ".clinerules",
|
|
188
|
+
claude: "."
|
|
206
189
|
},
|
|
207
190
|
watchEnabled: false,
|
|
208
|
-
defaultTargets: ["copilot", "cursor", "cline"]
|
|
191
|
+
defaultTargets: ["copilot", "cursor", "cline", "claude"]
|
|
209
192
|
};
|
|
210
193
|
}
|
|
211
194
|
function resolveTargets(targets, config) {
|
|
@@ -216,8 +199,8 @@ function resolveTargets(targets, config) {
|
|
|
216
199
|
}
|
|
217
200
|
|
|
218
201
|
// src/utils/file.ts
|
|
219
|
-
import { mkdir, readdir, readFile, stat, writeFile } from "fs/promises";
|
|
220
|
-
import { dirname, join as
|
|
202
|
+
import { mkdir, readdir, readFile, rm, stat, writeFile } from "fs/promises";
|
|
203
|
+
import { dirname, join as join5 } from "path";
|
|
221
204
|
async function ensureDir(dirPath) {
|
|
222
205
|
try {
|
|
223
206
|
await stat(dirPath);
|
|
@@ -235,7 +218,7 @@ async function writeFileContent(filepath, content) {
|
|
|
235
218
|
async function findFiles(dir, extension = ".md") {
|
|
236
219
|
try {
|
|
237
220
|
const files = await readdir(dir);
|
|
238
|
-
return files.filter((file) => file.endsWith(extension)).map((file) =>
|
|
221
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => join5(dir, file));
|
|
239
222
|
} catch {
|
|
240
223
|
return [];
|
|
241
224
|
}
|
|
@@ -248,6 +231,15 @@ async function fileExists(filepath) {
|
|
|
248
231
|
return false;
|
|
249
232
|
}
|
|
250
233
|
}
|
|
234
|
+
async function removeDirectory(dirPath) {
|
|
235
|
+
try {
|
|
236
|
+
if (await fileExists(dirPath)) {
|
|
237
|
+
await rm(dirPath, { recursive: true, force: true });
|
|
238
|
+
}
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
251
243
|
|
|
252
244
|
// src/core/generator.ts
|
|
253
245
|
async function generateConfigurations(rules, config, targetTools) {
|
|
@@ -259,9 +251,9 @@ async function generateConfigurations(rules, config, targetTools) {
|
|
|
259
251
|
console.warn(`No rules found for tool: ${tool}`);
|
|
260
252
|
continue;
|
|
261
253
|
}
|
|
262
|
-
const
|
|
263
|
-
if (
|
|
264
|
-
outputs.push(
|
|
254
|
+
const toolOutputs = await generateForTool(tool, relevantRules, config);
|
|
255
|
+
if (toolOutputs) {
|
|
256
|
+
outputs.push(...toolOutputs);
|
|
265
257
|
}
|
|
266
258
|
}
|
|
267
259
|
return outputs;
|
|
@@ -280,6 +272,8 @@ async function generateForTool(tool, rules, config) {
|
|
|
280
272
|
return generateCursorConfig(rules, config);
|
|
281
273
|
case "cline":
|
|
282
274
|
return generateClineConfig(rules, config);
|
|
275
|
+
case "claude":
|
|
276
|
+
return await generateClaudeConfig(rules, config);
|
|
283
277
|
default:
|
|
284
278
|
console.warn(`Unknown tool: ${tool}`);
|
|
285
279
|
return null;
|
|
@@ -320,8 +314,8 @@ function validateFrontmatter(data, filepath) {
|
|
|
320
314
|
throw new Error(`Invalid frontmatter in ${filepath}: must be an object`);
|
|
321
315
|
}
|
|
322
316
|
const obj = data;
|
|
323
|
-
if (!obj.
|
|
324
|
-
throw new Error(`Invalid
|
|
317
|
+
if (!obj.ruleLevel || !["overview", "detail"].includes(obj.ruleLevel)) {
|
|
318
|
+
throw new Error(`Invalid ruleLevel in ${filepath}: must be "overview" or "detail"`);
|
|
325
319
|
}
|
|
326
320
|
if (!Array.isArray(obj.targets)) {
|
|
327
321
|
throw new Error(`Invalid targets in ${filepath}: must be an array`);
|
|
@@ -358,6 +352,10 @@ async function validateRules(rules) {
|
|
|
358
352
|
}
|
|
359
353
|
filenames.add(rule.filename);
|
|
360
354
|
}
|
|
355
|
+
const overviewRules = rules.filter((rule) => rule.frontmatter.ruleLevel === "overview");
|
|
356
|
+
if (overviewRules.length > 1) {
|
|
357
|
+
errors.push(`Multiple overview rules found: ${overviewRules.map((r) => r.filename).join(", ")}. Only one overview rule is allowed.`);
|
|
358
|
+
}
|
|
361
359
|
for (const rule of rules) {
|
|
362
360
|
const ruleValidation = await validateRule(rule);
|
|
363
361
|
errors.push(...ruleValidation.errors);
|
|
@@ -411,6 +409,33 @@ async function generateCommand(options = {}) {
|
|
|
411
409
|
if (options.verbose) {
|
|
412
410
|
console.log(`Found ${rules.length} rule(s)`);
|
|
413
411
|
}
|
|
412
|
+
if (options.delete) {
|
|
413
|
+
if (options.verbose) {
|
|
414
|
+
console.log("Deleting existing output directories...");
|
|
415
|
+
}
|
|
416
|
+
const targetTools = options.tools || config.defaultTargets;
|
|
417
|
+
const deleteTasks = [];
|
|
418
|
+
for (const tool of targetTools) {
|
|
419
|
+
switch (tool) {
|
|
420
|
+
case "copilot":
|
|
421
|
+
deleteTasks.push(removeDirectory(config.outputPaths.copilot));
|
|
422
|
+
break;
|
|
423
|
+
case "cursor":
|
|
424
|
+
deleteTasks.push(removeDirectory(config.outputPaths.cursor));
|
|
425
|
+
break;
|
|
426
|
+
case "cline":
|
|
427
|
+
deleteTasks.push(removeDirectory(config.outputPaths.cline));
|
|
428
|
+
break;
|
|
429
|
+
case "claude":
|
|
430
|
+
deleteTasks.push(removeDirectory(config.outputPaths.claude));
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
await Promise.all(deleteTasks);
|
|
435
|
+
if (options.verbose) {
|
|
436
|
+
console.log("Deleted existing output directories");
|
|
437
|
+
}
|
|
438
|
+
}
|
|
414
439
|
const outputs = await generateConfigurations(rules, config, options.tools);
|
|
415
440
|
if (outputs.length === 0) {
|
|
416
441
|
console.warn("\u26A0\uFE0F No configurations generated");
|
|
@@ -428,17 +453,57 @@ async function generateCommand(options = {}) {
|
|
|
428
453
|
}
|
|
429
454
|
}
|
|
430
455
|
|
|
456
|
+
// src/cli/commands/gitignore.ts
|
|
457
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
458
|
+
import { join as join6 } from "path";
|
|
459
|
+
var gitignoreCommand = async () => {
|
|
460
|
+
const gitignorePath = join6(process.cwd(), ".gitignore");
|
|
461
|
+
const rulesFilesToIgnore = [
|
|
462
|
+
"# Generated by rulesync - AI tool configuration files",
|
|
463
|
+
".github/instructions/",
|
|
464
|
+
".cursor/rules/",
|
|
465
|
+
".clinerules/",
|
|
466
|
+
"CLAUDE.md"
|
|
467
|
+
];
|
|
468
|
+
let gitignoreContent = "";
|
|
469
|
+
if (existsSync(gitignorePath)) {
|
|
470
|
+
gitignoreContent = readFileSync(gitignorePath, "utf-8");
|
|
471
|
+
}
|
|
472
|
+
const linesToAdd = [];
|
|
473
|
+
for (const rule of rulesFilesToIgnore) {
|
|
474
|
+
if (!gitignoreContent.includes(rule)) {
|
|
475
|
+
linesToAdd.push(rule);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (linesToAdd.length === 0) {
|
|
479
|
+
console.log("\u2705 .gitignore\u306F\u65E2\u306B\u6700\u65B0\u3067\u3059");
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const newContent = gitignoreContent ? `${gitignoreContent.trimEnd()}
|
|
483
|
+
|
|
484
|
+
${linesToAdd.join("\n")}
|
|
485
|
+
` : `${linesToAdd.join("\n")}
|
|
486
|
+
`;
|
|
487
|
+
writeFileSync(gitignorePath, newContent);
|
|
488
|
+
console.log(`\u2705 .gitignore\u306B${linesToAdd.length}\u500B\u306E\u30EB\u30FC\u30EB\u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F:`);
|
|
489
|
+
for (const line of linesToAdd) {
|
|
490
|
+
if (!line.startsWith("#")) {
|
|
491
|
+
console.log(` ${line}`);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
|
|
431
496
|
// src/cli/commands/init.ts
|
|
432
|
-
import { join as
|
|
497
|
+
import { join as join7 } from "path";
|
|
433
498
|
async function initCommand() {
|
|
434
499
|
const aiRulesDir = ".rulesync";
|
|
435
|
-
console.log("Initializing
|
|
500
|
+
console.log("Initializing rulesync...");
|
|
436
501
|
await ensureDir(aiRulesDir);
|
|
437
502
|
await createSampleFiles(aiRulesDir);
|
|
438
|
-
console.log("\u2705
|
|
503
|
+
console.log("\u2705 rulesync initialized successfully!");
|
|
439
504
|
console.log("\nNext steps:");
|
|
440
505
|
console.log("1. Edit rule files in .rulesync/");
|
|
441
|
-
console.log("2. Run '
|
|
506
|
+
console.log("2. Run 'rulesync generate' to create configuration files");
|
|
442
507
|
}
|
|
443
508
|
async function createSampleFiles(aiRulesDir) {
|
|
444
509
|
const sampleFiles = [
|
|
@@ -519,7 +584,7 @@ globs: ["src/**/*.ts"]
|
|
|
519
584
|
}
|
|
520
585
|
];
|
|
521
586
|
for (const file of sampleFiles) {
|
|
522
|
-
const filepath =
|
|
587
|
+
const filepath = join7(aiRulesDir, file.filename);
|
|
523
588
|
if (!await fileExists(filepath)) {
|
|
524
589
|
await writeFileContent(filepath, file.content);
|
|
525
590
|
console.log(`Created ${filepath}`);
|
|
@@ -534,10 +599,10 @@ async function statusCommand() {
|
|
|
534
599
|
const config = getDefaultConfig();
|
|
535
600
|
console.log("rulesync Status");
|
|
536
601
|
console.log("===============");
|
|
537
|
-
const
|
|
602
|
+
const rulesyncExists = await fileExists(config.aiRulesDir);
|
|
538
603
|
console.log(`
|
|
539
|
-
\u{1F4C1} .rulesync directory: ${
|
|
540
|
-
if (!
|
|
604
|
+
\u{1F4C1} .rulesync directory: ${rulesyncExists ? "\u2705 Found" : "\u274C Not found"}`);
|
|
605
|
+
if (!rulesyncExists) {
|
|
541
606
|
console.log("\n\u{1F4A1} Run 'rulesync init' to get started");
|
|
542
607
|
return;
|
|
543
608
|
}
|
|
@@ -580,7 +645,7 @@ async function statusCommand() {
|
|
|
580
645
|
// src/cli/commands/validate.ts
|
|
581
646
|
async function validateCommand() {
|
|
582
647
|
const config = getDefaultConfig();
|
|
583
|
-
console.log("Validating
|
|
648
|
+
console.log("Validating rulesync configuration...");
|
|
584
649
|
if (!await fileExists(config.aiRulesDir)) {
|
|
585
650
|
console.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
|
|
586
651
|
process.exit(1);
|
|
@@ -588,7 +653,7 @@ async function validateCommand() {
|
|
|
588
653
|
try {
|
|
589
654
|
const rules = await parseRulesFromDirectory(config.aiRulesDir);
|
|
590
655
|
if (rules.length === 0) {
|
|
591
|
-
console.warn("\u26A0\uFE0F No rules found in .
|
|
656
|
+
console.warn("\u26A0\uFE0F No rules found in .rulesync directory");
|
|
592
657
|
return;
|
|
593
658
|
}
|
|
594
659
|
console.log(`Found ${rules.length} rule(s), validating...`);
|
|
@@ -660,22 +725,25 @@ async function watchCommand() {
|
|
|
660
725
|
|
|
661
726
|
// src/cli/index.ts
|
|
662
727
|
var program = new Command();
|
|
663
|
-
program.name("
|
|
664
|
-
program.command("init").description("Initialize
|
|
665
|
-
program.command("
|
|
728
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.1.0");
|
|
729
|
+
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
730
|
+
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
731
|
+
program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claude", "Generate only for Claude Code").option("--delete", "Delete all existing files in output directories before generating").option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
666
732
|
const tools = [];
|
|
667
733
|
if (options.copilot) tools.push("copilot");
|
|
668
734
|
if (options.cursor) tools.push("cursor");
|
|
669
735
|
if (options.cline) tools.push("cline");
|
|
736
|
+
if (options.claude) tools.push("claude");
|
|
670
737
|
const generateOptions = {
|
|
671
|
-
verbose: options.verbose
|
|
738
|
+
verbose: options.verbose,
|
|
739
|
+
delete: options.delete
|
|
672
740
|
};
|
|
673
741
|
if (tools.length > 0) {
|
|
674
742
|
generateOptions.tools = tools;
|
|
675
743
|
}
|
|
676
744
|
await generateCommand(generateOptions);
|
|
677
745
|
});
|
|
678
|
-
program.command("validate").description("Validate
|
|
679
|
-
program.command("status").description("Show current status of
|
|
746
|
+
program.command("validate").description("Validate rulesync configuration").action(validateCommand);
|
|
747
|
+
program.command("status").description("Show current status of rulesync").action(statusCommand);
|
|
680
748
|
program.command("watch").description("Watch for changes and auto-generate configurations").action(watchCommand);
|
|
681
749
|
program.parse();
|
package/package.json
CHANGED