rulesync 0.4.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +242 -38
- package/dist/index.js +87 -33
- package/dist/index.mjs +84 -30
- package/package.json +71 -70
package/README.md
CHANGED
|
@@ -8,9 +8,10 @@ A Node.js CLI tool that automatically generates configuration files for various
|
|
|
8
8
|
## Supported Tools
|
|
9
9
|
|
|
10
10
|
- **GitHub Copilot Custom Instructions** (`.github/instructions/*.instructions.md`)
|
|
11
|
-
- **Cursor Project Rules** (`.cursor/rules/*.
|
|
11
|
+
- **Cursor Project Rules** (`.cursor/rules/*.mdc`)
|
|
12
12
|
- **Cline Rules** (`.clinerules/*.md`)
|
|
13
|
-
- **Claude Code Memory** (`./CLAUDE.md`)
|
|
13
|
+
- **Claude Code Memory** (`./CLAUDE.md` + `.claude/memories/*.md`)
|
|
14
|
+
- **Roo Code Rules** (`.roo/rules/*.md`)
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -22,6 +23,132 @@ pnpm add -g rulesync
|
|
|
22
23
|
yarn global add rulesync
|
|
23
24
|
```
|
|
24
25
|
|
|
26
|
+
## Getting Started
|
|
27
|
+
|
|
28
|
+
### Quick Start Example
|
|
29
|
+
|
|
30
|
+
1. **Initialize your project:**
|
|
31
|
+
```bash
|
|
32
|
+
rulesync init
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
2. **Create an overview file** (`.rulesync/overview.md`):
|
|
36
|
+
```markdown
|
|
37
|
+
---
|
|
38
|
+
root: true
|
|
39
|
+
targets: ["*"]
|
|
40
|
+
description: "Project overview and development philosophy"
|
|
41
|
+
globs: ["src/**/*.ts", "src/**/*.js"]
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# Project Development Guidelines
|
|
45
|
+
|
|
46
|
+
This is a TypeScript/JavaScript project following clean architecture principles.
|
|
47
|
+
We prioritize code readability, maintainability, and type safety.
|
|
48
|
+
|
|
49
|
+
## Tech Stack
|
|
50
|
+
- TypeScript for type safety
|
|
51
|
+
- Node.js runtime
|
|
52
|
+
- Modern ES6+ features
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
3. **Create detail rules** (`.rulesync/coding-rules.md`):
|
|
56
|
+
```markdown
|
|
57
|
+
---
|
|
58
|
+
root: false
|
|
59
|
+
targets: ["copilot", "cursor", "cline"]
|
|
60
|
+
description: "TypeScript coding standards and best practices"
|
|
61
|
+
globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
# TypeScript Coding Rules
|
|
65
|
+
|
|
66
|
+
## Code Style
|
|
67
|
+
- Use strict TypeScript configuration
|
|
68
|
+
- Prefer `const` over `let` when possible
|
|
69
|
+
- Use meaningful, descriptive variable names
|
|
70
|
+
- Write JSDoc comments for public APIs
|
|
71
|
+
|
|
72
|
+
## Type Definitions
|
|
73
|
+
- Prefer interfaces over types for object shapes
|
|
74
|
+
- Use union types for controlled values
|
|
75
|
+
- Avoid `any` type - use `unknown` instead
|
|
76
|
+
- Define return types for functions explicitly
|
|
77
|
+
|
|
78
|
+
## Error Handling
|
|
79
|
+
- Use Result pattern for error handling
|
|
80
|
+
- Throw errors only for unexpected conditions
|
|
81
|
+
- Validate input parameters at function boundaries
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
4. **Generate configuration files:**
|
|
85
|
+
```bash
|
|
86
|
+
rulesync generate
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
5. **Optional: Add generated files to .gitignore:**
|
|
90
|
+
```bash
|
|
91
|
+
rulesync gitignore
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This will create tool-specific configuration files that your AI coding assistants can use automatically.
|
|
95
|
+
|
|
96
|
+
## Why rulesync?
|
|
97
|
+
|
|
98
|
+
### 🔧 **Tool Flexibility**
|
|
99
|
+
Team members can freely choose their preferred AI coding tools. Whether it's GitHub Copilot, Cursor, Cline, or Claude Code, each developer can use the tool that maximizes their productivity.
|
|
100
|
+
|
|
101
|
+
### 📈 **Future-Proof Development**
|
|
102
|
+
AI development tools evolve rapidly with new tools emerging frequently. With rulesync, switching between tools doesn't require redefining your rules from scratch.
|
|
103
|
+
|
|
104
|
+
### 🎯 **Multi-Tool Workflow**
|
|
105
|
+
Enable hybrid development workflows combining multiple AI tools:
|
|
106
|
+
- GitHub Copilot for code completion
|
|
107
|
+
- Cursor for refactoring
|
|
108
|
+
- Claude Code for architecture design
|
|
109
|
+
- Cline for debugging assistance
|
|
110
|
+
|
|
111
|
+
### 🔓 **No Vendor Lock-in**
|
|
112
|
+
Avoid vendor lock-in completely. If you decide to stop using rulesync, you can continue using the generated rule files (`.github/instructions/`, `.cursor/rules/`, `.clinerules/`, `CLAUDE.md`, etc.) as-is.
|
|
113
|
+
|
|
114
|
+
### 🎯 **Consistency Across Tools**
|
|
115
|
+
Apply consistent rules across all AI tools, improving code quality and development experience for the entire team.
|
|
116
|
+
|
|
117
|
+
## Claude Code Integration
|
|
118
|
+
|
|
119
|
+
### Creating Custom Slash Commands
|
|
120
|
+
|
|
121
|
+
Instead of using Claude Code's built-in `/init` command, we recommend creating a custom slash command specifically for rulesync.
|
|
122
|
+
|
|
123
|
+
Refer to the [Claude Code slash commands documentation](https://docs.anthropic.com/en/docs/claude-code/slash-commands) and add the following custom command:
|
|
124
|
+
|
|
125
|
+
**`.claude/commands/init-rulesync.md`**
|
|
126
|
+
|
|
127
|
+
```markdown
|
|
128
|
+
Review this project's content and update .rulesync/*.md files as needed.
|
|
129
|
+
|
|
130
|
+
Steps:
|
|
131
|
+
1. Analyze project structure and codebase
|
|
132
|
+
2. Review existing .rulesync/ files
|
|
133
|
+
3. Consider project's tech stack, architecture, and coding conventions
|
|
134
|
+
4. Update .rulesync/*.md files if missing elements or improvements are found
|
|
135
|
+
5. Run rulesync generate if necessary
|
|
136
|
+
|
|
137
|
+
Project characteristics to consider:
|
|
138
|
+
- Technology stack
|
|
139
|
+
- Architecture patterns
|
|
140
|
+
- Coding conventions
|
|
141
|
+
- Security requirements
|
|
142
|
+
- Performance considerations
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Integration Benefits
|
|
146
|
+
|
|
147
|
+
- **Project-Specific Initialization**: Optimized rule configuration for each project
|
|
148
|
+
- **Automatic Rule Updates**: Rules adapt to project changes automatically
|
|
149
|
+
- **Team Standardization**: All members use the same rule set
|
|
150
|
+
- **Continuous Improvement**: Rules evolve with project growth
|
|
151
|
+
|
|
25
152
|
## Usage
|
|
26
153
|
|
|
27
154
|
### 1. Initialize
|
|
@@ -38,8 +165,8 @@ Define metadata in front matter for each Markdown file:
|
|
|
38
165
|
|
|
39
166
|
```markdown
|
|
40
167
|
---
|
|
41
|
-
|
|
42
|
-
targets: ["*"] # or [copilot, cursor, cline,
|
|
168
|
+
root: true # or false
|
|
169
|
+
targets: ["*"] # or [copilot, cursor, cline, claude, roo]
|
|
43
170
|
description: "TypeScript coding rules"
|
|
44
171
|
globs: ["**/*.ts", "**/*.tsx"]
|
|
45
172
|
---
|
|
@@ -52,12 +179,26 @@ globs: ["**/*.ts", "**/*.tsx"]
|
|
|
52
179
|
|
|
53
180
|
### Rule Levels
|
|
54
181
|
|
|
55
|
-
|
|
56
|
-
|
|
182
|
+
rulesync uses a two-level rule system:
|
|
183
|
+
|
|
184
|
+
- **root: true**: Project-wide overview and policies
|
|
185
|
+
- Only **one** root file is allowed per project
|
|
186
|
+
- Contains high-level guidelines and project context
|
|
187
|
+
- **root: false**: Specific implementation rules and detailed guidelines
|
|
188
|
+
- Multiple non-root files are allowed
|
|
189
|
+
- Contains specific coding rules, naming conventions, etc.
|
|
190
|
+
|
|
191
|
+
#### Tool-Specific Behavior
|
|
192
|
+
|
|
193
|
+
Each AI tool handles rule levels differently:
|
|
57
194
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
195
|
+
| Tool | Root Rules | Non-Root Rules | Special Behavior |
|
|
196
|
+
|------|------------|----------------|------------------|
|
|
197
|
+
| **Claude Code** | `./CLAUDE.md` | `.claude/memories/*.md` | CLAUDE.md includes `@filename` references to detail files |
|
|
198
|
+
| **Cursor** | `ruletype: always` | `ruletype: autoattached` | Detail rules without globs use `ruletype: agentrequested` |
|
|
199
|
+
| **GitHub Copilot** | Standard format | Standard format | All rules use same format with frontmatter |
|
|
200
|
+
| **Cline** | Standard format | Standard format | All rules use plain Markdown format |
|
|
201
|
+
| **Roo Code** | Standard format | Standard format | All rules use plain Markdown format with description header |
|
|
61
202
|
|
|
62
203
|
### 3. Generate Configuration Files
|
|
63
204
|
|
|
@@ -70,62 +211,125 @@ rulesync generate --copilot
|
|
|
70
211
|
rulesync generate --cursor
|
|
71
212
|
rulesync generate --cline
|
|
72
213
|
rulesync generate --claude
|
|
214
|
+
rulesync generate --roo
|
|
215
|
+
|
|
216
|
+
# Clean build (delete existing files first)
|
|
217
|
+
rulesync generate --delete
|
|
218
|
+
|
|
219
|
+
# Clean build for specific tools
|
|
220
|
+
rulesync generate --copilot --cursor --delete
|
|
221
|
+
|
|
222
|
+
# Verbose output
|
|
223
|
+
rulesync generate --verbose
|
|
224
|
+
rulesync generate --delete --verbose
|
|
73
225
|
```
|
|
74
226
|
|
|
227
|
+
#### Generate Options
|
|
228
|
+
|
|
229
|
+
- `--delete`: Remove all existing generated files before creating new ones
|
|
230
|
+
- `--verbose`: Show detailed output during generation process
|
|
231
|
+
- `--copilot`, `--cursor`, `--cline`, `--claude`, `--roo`: Generate only for specified tools
|
|
232
|
+
|
|
75
233
|
### 4. Other Commands
|
|
76
234
|
|
|
77
235
|
```bash
|
|
78
|
-
#
|
|
236
|
+
# Initialize project with sample files
|
|
237
|
+
rulesync init
|
|
238
|
+
|
|
239
|
+
# Validate rule files
|
|
79
240
|
rulesync validate
|
|
80
241
|
|
|
81
|
-
# Check current status
|
|
242
|
+
# Check current status
|
|
82
243
|
rulesync status
|
|
83
244
|
|
|
84
245
|
# Watch files and auto-generate
|
|
85
246
|
rulesync watch
|
|
247
|
+
|
|
248
|
+
# Add generated files to .gitignore
|
|
249
|
+
rulesync gitignore
|
|
86
250
|
```
|
|
87
251
|
|
|
88
252
|
## Configuration File Structure
|
|
89
253
|
|
|
90
254
|
```
|
|
91
255
|
.rulesync/
|
|
92
|
-
├──
|
|
93
|
-
├──
|
|
94
|
-
├──
|
|
95
|
-
├──
|
|
96
|
-
|
|
256
|
+
├── overview.md # Project overview (root: true, only one)
|
|
257
|
+
├── coding-rules.md # Coding rules (root: false)
|
|
258
|
+
├── naming-conventions.md # Naming conventions (root: false)
|
|
259
|
+
├── architecture.md # Architecture guidelines (root: false)
|
|
260
|
+
├── security.md # Security rules (root: false)
|
|
261
|
+
└── custom.md # Project-specific rules (root: false)
|
|
97
262
|
```
|
|
98
263
|
|
|
99
|
-
|
|
264
|
+
### Frontmatter Schema
|
|
100
265
|
|
|
101
|
-
|
|
102
|
-
|------|------------|--------|
|
|
103
|
-
| GitHub Copilot | `.github/instructions/*.instructions.md` | Front Matter + Markdown |
|
|
104
|
-
| Cursor | `.cursor/rules/*.md` | MDC (YAML header + Markdown) |
|
|
105
|
-
| Cline | `.clinerules/*.md` | Plain Markdown |
|
|
106
|
-
| Claude Code | `./CLAUDE.md` (overview), `.claude/memories/*.md` (detail) | Plain Markdown |
|
|
266
|
+
Each rule file must include frontmatter with the following fields:
|
|
107
267
|
|
|
108
|
-
|
|
268
|
+
```yaml
|
|
269
|
+
---
|
|
270
|
+
root: true | false # Required: Rule level (true for overview, false for details)
|
|
271
|
+
targets: ["*"] # Required: Target tools (* = all, or specific tools)
|
|
272
|
+
description: "Brief description" # Required: Rule description
|
|
273
|
+
globs: ["**/*.ts", "**/*.js"] # Required: File patterns (can be empty array)
|
|
274
|
+
---
|
|
275
|
+
```
|
|
109
276
|
|
|
110
|
-
|
|
111
|
-
# Install dependencies
|
|
112
|
-
pnpm install
|
|
277
|
+
### Example Files
|
|
113
278
|
|
|
114
|
-
|
|
115
|
-
|
|
279
|
+
**Root file** (`.rulesync/overview.md`):
|
|
280
|
+
```markdown
|
|
281
|
+
---
|
|
282
|
+
root: true
|
|
283
|
+
targets: ["*"]
|
|
284
|
+
description: "Project overview and development philosophy"
|
|
285
|
+
globs: ["src/**/*.ts"]
|
|
286
|
+
---
|
|
116
287
|
|
|
117
|
-
#
|
|
118
|
-
pnpm build
|
|
288
|
+
# Project Development Guidelines
|
|
119
289
|
|
|
120
|
-
|
|
121
|
-
|
|
290
|
+
This project follows TypeScript-first development with clean architecture principles.
|
|
291
|
+
```
|
|
122
292
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
293
|
+
**Non-root file** (`.rulesync/coding-rules.md`):
|
|
294
|
+
```markdown
|
|
295
|
+
---
|
|
296
|
+
root: false
|
|
297
|
+
targets: ["copilot", "cursor", "roo"]
|
|
298
|
+
description: "TypeScript coding standards"
|
|
299
|
+
globs: ["**/*.ts", "**/*.tsx"]
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
# TypeScript Coding Rules
|
|
303
|
+
|
|
304
|
+
- Use strict TypeScript configuration
|
|
305
|
+
- Prefer interfaces over types for object shapes
|
|
306
|
+
- Use meaningful variable names
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Generated Configuration Files
|
|
310
|
+
|
|
311
|
+
| Tool | Output Path | Format | Rule Level Handling |
|
|
312
|
+
|------|------------|--------|-------------------|
|
|
313
|
+
| **GitHub Copilot** | `.github/instructions/*.instructions.md` | Front Matter + Markdown | Both levels use same format |
|
|
314
|
+
| **Cursor** | `.cursor/rules/*.mdc` | MDC (YAML header + Markdown) | Root: `ruletype: always`<br>Non-root: `ruletype: autoattached`<br>Non-root without globs: `ruletype: agentrequested` |
|
|
315
|
+
| **Cline** | `.clinerules/*.md` | Plain Markdown | Both levels use same format |
|
|
316
|
+
| **Claude Code** | `./CLAUDE.md` (root)<br>`.claude/memories/*.md` (non-root) | Plain Markdown | Root goes to CLAUDE.md<br>Non-root go to separate memory files<br>CLAUDE.md includes `@filename` references |
|
|
317
|
+
| **Roo Code** | `.roo/rules/*.md` | Plain Markdown | Both levels use same format with description header |
|
|
318
|
+
|
|
319
|
+
## Validation
|
|
320
|
+
|
|
321
|
+
rulesync validates your rule files and provides helpful error messages:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
rulesync validate
|
|
127
325
|
```
|
|
128
326
|
|
|
327
|
+
Common validation rules:
|
|
328
|
+
- Only one root file (root: true) is allowed per project
|
|
329
|
+
- All frontmatter fields are required and properly formatted
|
|
330
|
+
- File patterns (globs) use valid syntax
|
|
331
|
+
- Target tools are recognized values
|
|
332
|
+
|
|
129
333
|
## License
|
|
130
334
|
|
|
131
335
|
MIT License
|
|
@@ -134,4 +338,4 @@ MIT License
|
|
|
134
338
|
|
|
135
339
|
Issues and Pull Requests are welcome!
|
|
136
340
|
|
|
137
|
-
For
|
|
341
|
+
For development setup and contribution guidelines, see [CONTRIBUTING.md](./CONTRIBUTING.md).
|
package/dist/index.js
CHANGED
|
@@ -30,9 +30,9 @@ var import_commander = require("commander");
|
|
|
30
30
|
var import_node_path = require("path");
|
|
31
31
|
async function generateClaudeConfig(rules, config) {
|
|
32
32
|
const outputs = [];
|
|
33
|
-
const
|
|
34
|
-
const detailRules = rules.filter((r) => r.frontmatter.
|
|
35
|
-
const claudeMdContent = generateClaudeMarkdown(
|
|
33
|
+
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
34
|
+
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
35
|
+
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
36
36
|
outputs.push({
|
|
37
37
|
tool: "claude",
|
|
38
38
|
filepath: (0, import_node_path.join)(config.outputPaths.claude, "CLAUDE.md"),
|
|
@@ -48,11 +48,13 @@ async function generateClaudeConfig(rules, config) {
|
|
|
48
48
|
}
|
|
49
49
|
return outputs;
|
|
50
50
|
}
|
|
51
|
-
function generateClaudeMarkdown(
|
|
51
|
+
function generateClaudeMarkdown(rootRules, detailRules) {
|
|
52
52
|
const lines = [];
|
|
53
53
|
if (detailRules.length > 0) {
|
|
54
|
+
lines.push("Please also reference the following documents as needed:");
|
|
55
|
+
lines.push("");
|
|
54
56
|
for (const rule of detailRules) {
|
|
55
|
-
lines.push(
|
|
57
|
+
lines.push(`@.claude/memories/${rule.filename}.md`);
|
|
56
58
|
}
|
|
57
59
|
lines.push("");
|
|
58
60
|
}
|
|
@@ -62,8 +64,8 @@ function generateClaudeMarkdown(overviewRules, detailRules) {
|
|
|
62
64
|
"Generated from rulesync configuration. These instructions guide Claude Code's behavior for this project."
|
|
63
65
|
);
|
|
64
66
|
lines.push("");
|
|
65
|
-
if (
|
|
66
|
-
for (const rule of
|
|
67
|
+
if (rootRules.length > 0) {
|
|
68
|
+
for (const rule of rootRules) {
|
|
67
69
|
lines.push(...formatRuleForClaude(rule));
|
|
68
70
|
}
|
|
69
71
|
}
|
|
@@ -87,10 +89,6 @@ function formatRuleForClaude(rule) {
|
|
|
87
89
|
}
|
|
88
90
|
function generateMemoryFile(rule) {
|
|
89
91
|
const lines = [];
|
|
90
|
-
lines.push("Please also refer to the following files as needed:");
|
|
91
|
-
lines.push("");
|
|
92
|
-
lines.push("---");
|
|
93
|
-
lines.push("");
|
|
94
92
|
lines.push(`# ${rule.filename}`);
|
|
95
93
|
lines.push("");
|
|
96
94
|
if (rule.frontmatter.description) {
|
|
@@ -158,7 +156,6 @@ function generateCopilotMarkdown(rule) {
|
|
|
158
156
|
lines.push('applyTo: "**"');
|
|
159
157
|
}
|
|
160
158
|
lines.push("---");
|
|
161
|
-
lines.push("");
|
|
162
159
|
lines.push(rule.content);
|
|
163
160
|
return lines.join("\n");
|
|
164
161
|
}
|
|
@@ -169,7 +166,7 @@ async function generateCursorConfig(rules, config) {
|
|
|
169
166
|
const outputs = [];
|
|
170
167
|
for (const rule of rules) {
|
|
171
168
|
const content = generateCursorMarkdown(rule);
|
|
172
|
-
const filepath = (0, import_node_path4.join)(config.outputPaths.cursor, `${rule.filename}.
|
|
169
|
+
const filepath = (0, import_node_path4.join)(config.outputPaths.cursor, `${rule.filename}.mdc`);
|
|
173
170
|
outputs.push({
|
|
174
171
|
tool: "cursor",
|
|
175
172
|
filepath,
|
|
@@ -186,15 +183,37 @@ function generateCursorMarkdown(rule) {
|
|
|
186
183
|
lines.push(`globs: [${rule.frontmatter.globs.map((g) => `"${g}"`).join(", ")}]`);
|
|
187
184
|
}
|
|
188
185
|
let ruletype;
|
|
189
|
-
if (rule.frontmatter.
|
|
186
|
+
if (rule.frontmatter.root === true) {
|
|
190
187
|
ruletype = "always";
|
|
191
|
-
} else if (rule.frontmatter.
|
|
188
|
+
} else if (rule.frontmatter.root === false && rule.frontmatter.globs.length === 0) {
|
|
192
189
|
ruletype = "agentrequested";
|
|
193
190
|
} else {
|
|
194
191
|
ruletype = "autoattached";
|
|
195
192
|
}
|
|
196
193
|
lines.push(`ruletype: ${ruletype}`);
|
|
197
194
|
lines.push("---");
|
|
195
|
+
lines.push(rule.content);
|
|
196
|
+
return lines.join("\n");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/generators/roo.ts
|
|
200
|
+
var import_node_path5 = require("path");
|
|
201
|
+
async function generateRooConfig(rules, config) {
|
|
202
|
+
const outputs = [];
|
|
203
|
+
for (const rule of rules) {
|
|
204
|
+
const content = generateRooMarkdown(rule);
|
|
205
|
+
const filepath = (0, import_node_path5.join)(config.outputPaths.roo, `${rule.filename}.md`);
|
|
206
|
+
outputs.push({
|
|
207
|
+
tool: "roo",
|
|
208
|
+
filepath,
|
|
209
|
+
content
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return outputs;
|
|
213
|
+
}
|
|
214
|
+
function generateRooMarkdown(rule) {
|
|
215
|
+
const lines = [];
|
|
216
|
+
lines.push(`# ${rule.frontmatter.description}`);
|
|
198
217
|
lines.push("");
|
|
199
218
|
lines.push(rule.content);
|
|
200
219
|
return lines.join("\n");
|
|
@@ -208,10 +227,11 @@ function getDefaultConfig() {
|
|
|
208
227
|
copilot: ".github/instructions",
|
|
209
228
|
cursor: ".cursor/rules",
|
|
210
229
|
cline: ".clinerules",
|
|
211
|
-
claude: "."
|
|
230
|
+
claude: ".",
|
|
231
|
+
roo: ".roo/rules"
|
|
212
232
|
},
|
|
213
233
|
watchEnabled: false,
|
|
214
|
-
defaultTargets: ["copilot", "cursor", "cline", "claude"]
|
|
234
|
+
defaultTargets: ["copilot", "cursor", "cline", "claude", "roo"]
|
|
215
235
|
};
|
|
216
236
|
}
|
|
217
237
|
function resolveTargets(targets, config) {
|
|
@@ -223,7 +243,7 @@ function resolveTargets(targets, config) {
|
|
|
223
243
|
|
|
224
244
|
// src/utils/file.ts
|
|
225
245
|
var import_promises = require("fs/promises");
|
|
226
|
-
var
|
|
246
|
+
var import_node_path6 = require("path");
|
|
227
247
|
async function ensureDir(dirPath) {
|
|
228
248
|
try {
|
|
229
249
|
await (0, import_promises.stat)(dirPath);
|
|
@@ -235,13 +255,13 @@ async function readFileContent(filepath) {
|
|
|
235
255
|
return (0, import_promises.readFile)(filepath, "utf-8");
|
|
236
256
|
}
|
|
237
257
|
async function writeFileContent(filepath, content) {
|
|
238
|
-
await ensureDir((0,
|
|
258
|
+
await ensureDir((0, import_node_path6.dirname)(filepath));
|
|
239
259
|
await (0, import_promises.writeFile)(filepath, content, "utf-8");
|
|
240
260
|
}
|
|
241
261
|
async function findFiles(dir, extension = ".md") {
|
|
242
262
|
try {
|
|
243
263
|
const files = await (0, import_promises.readdir)(dir);
|
|
244
|
-
return files.filter((file) => file.endsWith(extension)).map((file) => (0,
|
|
264
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path6.join)(dir, file));
|
|
245
265
|
} catch {
|
|
246
266
|
return [];
|
|
247
267
|
}
|
|
@@ -255,6 +275,11 @@ async function fileExists(filepath) {
|
|
|
255
275
|
}
|
|
256
276
|
}
|
|
257
277
|
async function removeDirectory(dirPath) {
|
|
278
|
+
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
279
|
+
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
280
|
+
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
258
283
|
try {
|
|
259
284
|
if (await fileExists(dirPath)) {
|
|
260
285
|
await (0, import_promises.rm)(dirPath, { recursive: true, force: true });
|
|
@@ -263,6 +288,25 @@ async function removeDirectory(dirPath) {
|
|
|
263
288
|
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
264
289
|
}
|
|
265
290
|
}
|
|
291
|
+
async function removeFile(filepath) {
|
|
292
|
+
try {
|
|
293
|
+
if (await fileExists(filepath)) {
|
|
294
|
+
await (0, import_promises.rm)(filepath);
|
|
295
|
+
}
|
|
296
|
+
} catch (error) {
|
|
297
|
+
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async function removeClaudeGeneratedFiles() {
|
|
301
|
+
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
302
|
+
for (const fileOrDir of filesToRemove) {
|
|
303
|
+
if (fileOrDir.endsWith("/memories")) {
|
|
304
|
+
await removeDirectory(fileOrDir);
|
|
305
|
+
} else {
|
|
306
|
+
await removeFile(fileOrDir);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
266
310
|
|
|
267
311
|
// src/core/generator.ts
|
|
268
312
|
async function generateConfigurations(rules, config, targetTools) {
|
|
@@ -297,6 +341,8 @@ async function generateForTool(tool, rules, config) {
|
|
|
297
341
|
return generateClineConfig(rules, config);
|
|
298
342
|
case "claude":
|
|
299
343
|
return await generateClaudeConfig(rules, config);
|
|
344
|
+
case "roo":
|
|
345
|
+
return generateRooConfig(rules, config);
|
|
300
346
|
default:
|
|
301
347
|
console.warn(`Unknown tool: ${tool}`);
|
|
302
348
|
return null;
|
|
@@ -304,7 +350,7 @@ async function generateForTool(tool, rules, config) {
|
|
|
304
350
|
}
|
|
305
351
|
|
|
306
352
|
// src/core/parser.ts
|
|
307
|
-
var
|
|
353
|
+
var import_node_path7 = require("path");
|
|
308
354
|
var import_gray_matter = __toESM(require("gray-matter"));
|
|
309
355
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
310
356
|
const ruleFiles = await findFiles(aiRulesDir);
|
|
@@ -324,7 +370,7 @@ async function parseRuleFile(filepath) {
|
|
|
324
370
|
const parsed = (0, import_gray_matter.default)(content);
|
|
325
371
|
validateFrontmatter(parsed.data, filepath);
|
|
326
372
|
const frontmatter = parsed.data;
|
|
327
|
-
const filename = (0,
|
|
373
|
+
const filename = (0, import_node_path7.basename)(filepath, ".md");
|
|
328
374
|
return {
|
|
329
375
|
frontmatter,
|
|
330
376
|
content: parsed.content,
|
|
@@ -337,8 +383,8 @@ function validateFrontmatter(data, filepath) {
|
|
|
337
383
|
throw new Error(`Invalid frontmatter in ${filepath}: must be an object`);
|
|
338
384
|
}
|
|
339
385
|
const obj = data;
|
|
340
|
-
if (
|
|
341
|
-
throw new Error(`Invalid
|
|
386
|
+
if (typeof obj.root !== "boolean") {
|
|
387
|
+
throw new Error(`Invalid root in ${filepath}: must be a boolean`);
|
|
342
388
|
}
|
|
343
389
|
if (!Array.isArray(obj.targets)) {
|
|
344
390
|
throw new Error(`Invalid targets in ${filepath}: must be an array`);
|
|
@@ -375,9 +421,11 @@ async function validateRules(rules) {
|
|
|
375
421
|
}
|
|
376
422
|
filenames.add(rule.filename);
|
|
377
423
|
}
|
|
378
|
-
const
|
|
379
|
-
if (
|
|
380
|
-
errors.push(
|
|
424
|
+
const rootRules = rules.filter((rule) => rule.frontmatter.root === true);
|
|
425
|
+
if (rootRules.length > 1) {
|
|
426
|
+
errors.push(
|
|
427
|
+
`Multiple root rules found: ${rootRules.map((r) => r.filename).join(", ")}. Only one root rule is allowed.`
|
|
428
|
+
);
|
|
381
429
|
}
|
|
382
430
|
for (const rule of rules) {
|
|
383
431
|
const ruleValidation = await validateRule(rule);
|
|
@@ -450,7 +498,10 @@ async function generateCommand(options = {}) {
|
|
|
450
498
|
deleteTasks.push(removeDirectory(config.outputPaths.cline));
|
|
451
499
|
break;
|
|
452
500
|
case "claude":
|
|
453
|
-
deleteTasks.push(
|
|
501
|
+
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
502
|
+
break;
|
|
503
|
+
case "roo":
|
|
504
|
+
deleteTasks.push(removeDirectory(config.outputPaths.roo));
|
|
454
505
|
break;
|
|
455
506
|
}
|
|
456
507
|
}
|
|
@@ -478,15 +529,18 @@ async function generateCommand(options = {}) {
|
|
|
478
529
|
|
|
479
530
|
// src/cli/commands/gitignore.ts
|
|
480
531
|
var import_node_fs = require("fs");
|
|
481
|
-
var
|
|
532
|
+
var import_node_path8 = require("path");
|
|
482
533
|
var gitignoreCommand = async () => {
|
|
483
|
-
const gitignorePath = (0,
|
|
534
|
+
const gitignorePath = (0, import_node_path8.join)(process.cwd(), ".gitignore");
|
|
484
535
|
const rulesFilesToIgnore = [
|
|
485
536
|
"# Generated by rulesync - AI tool configuration files",
|
|
537
|
+
".github/copilot-instructions.md",
|
|
486
538
|
".github/instructions/",
|
|
487
539
|
".cursor/rules/",
|
|
488
540
|
".clinerules/",
|
|
489
|
-
"CLAUDE.md"
|
|
541
|
+
"CLAUDE.md",
|
|
542
|
+
".claude/memories/",
|
|
543
|
+
".roo/rules/"
|
|
490
544
|
];
|
|
491
545
|
let gitignoreContent = "";
|
|
492
546
|
if ((0, import_node_fs.existsSync)(gitignorePath)) {
|
|
@@ -517,7 +571,7 @@ ${linesToAdd.join("\n")}
|
|
|
517
571
|
};
|
|
518
572
|
|
|
519
573
|
// src/cli/commands/init.ts
|
|
520
|
-
var
|
|
574
|
+
var import_node_path9 = require("path");
|
|
521
575
|
async function initCommand() {
|
|
522
576
|
const aiRulesDir = ".rulesync";
|
|
523
577
|
console.log("Initializing rulesync...");
|
|
@@ -607,7 +661,7 @@ globs: ["src/**/*.ts"]
|
|
|
607
661
|
}
|
|
608
662
|
];
|
|
609
663
|
for (const file of sampleFiles) {
|
|
610
|
-
const filepath = (0,
|
|
664
|
+
const filepath = (0, import_node_path9.join)(aiRulesDir, file.filename);
|
|
611
665
|
if (!await fileExists(filepath)) {
|
|
612
666
|
await writeFileContent(filepath, file.content);
|
|
613
667
|
console.log(`Created ${filepath}`);
|
package/dist/index.mjs
CHANGED
|
@@ -7,9 +7,9 @@ import { Command } from "commander";
|
|
|
7
7
|
import { join } from "path";
|
|
8
8
|
async function generateClaudeConfig(rules, config) {
|
|
9
9
|
const outputs = [];
|
|
10
|
-
const
|
|
11
|
-
const detailRules = rules.filter((r) => r.frontmatter.
|
|
12
|
-
const claudeMdContent = generateClaudeMarkdown(
|
|
10
|
+
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
11
|
+
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
12
|
+
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
13
13
|
outputs.push({
|
|
14
14
|
tool: "claude",
|
|
15
15
|
filepath: join(config.outputPaths.claude, "CLAUDE.md"),
|
|
@@ -25,11 +25,13 @@ async function generateClaudeConfig(rules, config) {
|
|
|
25
25
|
}
|
|
26
26
|
return outputs;
|
|
27
27
|
}
|
|
28
|
-
function generateClaudeMarkdown(
|
|
28
|
+
function generateClaudeMarkdown(rootRules, detailRules) {
|
|
29
29
|
const lines = [];
|
|
30
30
|
if (detailRules.length > 0) {
|
|
31
|
+
lines.push("Please also reference the following documents as needed:");
|
|
32
|
+
lines.push("");
|
|
31
33
|
for (const rule of detailRules) {
|
|
32
|
-
lines.push(
|
|
34
|
+
lines.push(`@.claude/memories/${rule.filename}.md`);
|
|
33
35
|
}
|
|
34
36
|
lines.push("");
|
|
35
37
|
}
|
|
@@ -39,8 +41,8 @@ function generateClaudeMarkdown(overviewRules, detailRules) {
|
|
|
39
41
|
"Generated from rulesync configuration. These instructions guide Claude Code's behavior for this project."
|
|
40
42
|
);
|
|
41
43
|
lines.push("");
|
|
42
|
-
if (
|
|
43
|
-
for (const rule of
|
|
44
|
+
if (rootRules.length > 0) {
|
|
45
|
+
for (const rule of rootRules) {
|
|
44
46
|
lines.push(...formatRuleForClaude(rule));
|
|
45
47
|
}
|
|
46
48
|
}
|
|
@@ -64,10 +66,6 @@ function formatRuleForClaude(rule) {
|
|
|
64
66
|
}
|
|
65
67
|
function generateMemoryFile(rule) {
|
|
66
68
|
const lines = [];
|
|
67
|
-
lines.push("Please also refer to the following files as needed:");
|
|
68
|
-
lines.push("");
|
|
69
|
-
lines.push("---");
|
|
70
|
-
lines.push("");
|
|
71
69
|
lines.push(`# ${rule.filename}`);
|
|
72
70
|
lines.push("");
|
|
73
71
|
if (rule.frontmatter.description) {
|
|
@@ -135,7 +133,6 @@ function generateCopilotMarkdown(rule) {
|
|
|
135
133
|
lines.push('applyTo: "**"');
|
|
136
134
|
}
|
|
137
135
|
lines.push("---");
|
|
138
|
-
lines.push("");
|
|
139
136
|
lines.push(rule.content);
|
|
140
137
|
return lines.join("\n");
|
|
141
138
|
}
|
|
@@ -146,7 +143,7 @@ async function generateCursorConfig(rules, config) {
|
|
|
146
143
|
const outputs = [];
|
|
147
144
|
for (const rule of rules) {
|
|
148
145
|
const content = generateCursorMarkdown(rule);
|
|
149
|
-
const filepath = join4(config.outputPaths.cursor, `${rule.filename}.
|
|
146
|
+
const filepath = join4(config.outputPaths.cursor, `${rule.filename}.mdc`);
|
|
150
147
|
outputs.push({
|
|
151
148
|
tool: "cursor",
|
|
152
149
|
filepath,
|
|
@@ -163,15 +160,37 @@ function generateCursorMarkdown(rule) {
|
|
|
163
160
|
lines.push(`globs: [${rule.frontmatter.globs.map((g) => `"${g}"`).join(", ")}]`);
|
|
164
161
|
}
|
|
165
162
|
let ruletype;
|
|
166
|
-
if (rule.frontmatter.
|
|
163
|
+
if (rule.frontmatter.root === true) {
|
|
167
164
|
ruletype = "always";
|
|
168
|
-
} else if (rule.frontmatter.
|
|
165
|
+
} else if (rule.frontmatter.root === false && rule.frontmatter.globs.length === 0) {
|
|
169
166
|
ruletype = "agentrequested";
|
|
170
167
|
} else {
|
|
171
168
|
ruletype = "autoattached";
|
|
172
169
|
}
|
|
173
170
|
lines.push(`ruletype: ${ruletype}`);
|
|
174
171
|
lines.push("---");
|
|
172
|
+
lines.push(rule.content);
|
|
173
|
+
return lines.join("\n");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/generators/roo.ts
|
|
177
|
+
import { join as join5 } from "path";
|
|
178
|
+
async function generateRooConfig(rules, config) {
|
|
179
|
+
const outputs = [];
|
|
180
|
+
for (const rule of rules) {
|
|
181
|
+
const content = generateRooMarkdown(rule);
|
|
182
|
+
const filepath = join5(config.outputPaths.roo, `${rule.filename}.md`);
|
|
183
|
+
outputs.push({
|
|
184
|
+
tool: "roo",
|
|
185
|
+
filepath,
|
|
186
|
+
content
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return outputs;
|
|
190
|
+
}
|
|
191
|
+
function generateRooMarkdown(rule) {
|
|
192
|
+
const lines = [];
|
|
193
|
+
lines.push(`# ${rule.frontmatter.description}`);
|
|
175
194
|
lines.push("");
|
|
176
195
|
lines.push(rule.content);
|
|
177
196
|
return lines.join("\n");
|
|
@@ -185,10 +204,11 @@ function getDefaultConfig() {
|
|
|
185
204
|
copilot: ".github/instructions",
|
|
186
205
|
cursor: ".cursor/rules",
|
|
187
206
|
cline: ".clinerules",
|
|
188
|
-
claude: "."
|
|
207
|
+
claude: ".",
|
|
208
|
+
roo: ".roo/rules"
|
|
189
209
|
},
|
|
190
210
|
watchEnabled: false,
|
|
191
|
-
defaultTargets: ["copilot", "cursor", "cline", "claude"]
|
|
211
|
+
defaultTargets: ["copilot", "cursor", "cline", "claude", "roo"]
|
|
192
212
|
};
|
|
193
213
|
}
|
|
194
214
|
function resolveTargets(targets, config) {
|
|
@@ -200,7 +220,7 @@ function resolveTargets(targets, config) {
|
|
|
200
220
|
|
|
201
221
|
// src/utils/file.ts
|
|
202
222
|
import { mkdir, readdir, readFile, rm, stat, writeFile } from "fs/promises";
|
|
203
|
-
import { dirname, join as
|
|
223
|
+
import { dirname, join as join6 } from "path";
|
|
204
224
|
async function ensureDir(dirPath) {
|
|
205
225
|
try {
|
|
206
226
|
await stat(dirPath);
|
|
@@ -218,7 +238,7 @@ async function writeFileContent(filepath, content) {
|
|
|
218
238
|
async function findFiles(dir, extension = ".md") {
|
|
219
239
|
try {
|
|
220
240
|
const files = await readdir(dir);
|
|
221
|
-
return files.filter((file) => file.endsWith(extension)).map((file) =>
|
|
241
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => join6(dir, file));
|
|
222
242
|
} catch {
|
|
223
243
|
return [];
|
|
224
244
|
}
|
|
@@ -232,6 +252,11 @@ async function fileExists(filepath) {
|
|
|
232
252
|
}
|
|
233
253
|
}
|
|
234
254
|
async function removeDirectory(dirPath) {
|
|
255
|
+
const dangerousPaths = [".", "/", "~", "src", "node_modules"];
|
|
256
|
+
if (dangerousPaths.includes(dirPath) || dirPath === "") {
|
|
257
|
+
console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
235
260
|
try {
|
|
236
261
|
if (await fileExists(dirPath)) {
|
|
237
262
|
await rm(dirPath, { recursive: true, force: true });
|
|
@@ -240,6 +265,25 @@ async function removeDirectory(dirPath) {
|
|
|
240
265
|
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
241
266
|
}
|
|
242
267
|
}
|
|
268
|
+
async function removeFile(filepath) {
|
|
269
|
+
try {
|
|
270
|
+
if (await fileExists(filepath)) {
|
|
271
|
+
await rm(filepath);
|
|
272
|
+
}
|
|
273
|
+
} catch (error) {
|
|
274
|
+
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
async function removeClaudeGeneratedFiles() {
|
|
278
|
+
const filesToRemove = ["CLAUDE.md", ".claude/memories"];
|
|
279
|
+
for (const fileOrDir of filesToRemove) {
|
|
280
|
+
if (fileOrDir.endsWith("/memories")) {
|
|
281
|
+
await removeDirectory(fileOrDir);
|
|
282
|
+
} else {
|
|
283
|
+
await removeFile(fileOrDir);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
243
287
|
|
|
244
288
|
// src/core/generator.ts
|
|
245
289
|
async function generateConfigurations(rules, config, targetTools) {
|
|
@@ -274,6 +318,8 @@ async function generateForTool(tool, rules, config) {
|
|
|
274
318
|
return generateClineConfig(rules, config);
|
|
275
319
|
case "claude":
|
|
276
320
|
return await generateClaudeConfig(rules, config);
|
|
321
|
+
case "roo":
|
|
322
|
+
return generateRooConfig(rules, config);
|
|
277
323
|
default:
|
|
278
324
|
console.warn(`Unknown tool: ${tool}`);
|
|
279
325
|
return null;
|
|
@@ -314,8 +360,8 @@ function validateFrontmatter(data, filepath) {
|
|
|
314
360
|
throw new Error(`Invalid frontmatter in ${filepath}: must be an object`);
|
|
315
361
|
}
|
|
316
362
|
const obj = data;
|
|
317
|
-
if (
|
|
318
|
-
throw new Error(`Invalid
|
|
363
|
+
if (typeof obj.root !== "boolean") {
|
|
364
|
+
throw new Error(`Invalid root in ${filepath}: must be a boolean`);
|
|
319
365
|
}
|
|
320
366
|
if (!Array.isArray(obj.targets)) {
|
|
321
367
|
throw new Error(`Invalid targets in ${filepath}: must be an array`);
|
|
@@ -352,9 +398,11 @@ async function validateRules(rules) {
|
|
|
352
398
|
}
|
|
353
399
|
filenames.add(rule.filename);
|
|
354
400
|
}
|
|
355
|
-
const
|
|
356
|
-
if (
|
|
357
|
-
errors.push(
|
|
401
|
+
const rootRules = rules.filter((rule) => rule.frontmatter.root === true);
|
|
402
|
+
if (rootRules.length > 1) {
|
|
403
|
+
errors.push(
|
|
404
|
+
`Multiple root rules found: ${rootRules.map((r) => r.filename).join(", ")}. Only one root rule is allowed.`
|
|
405
|
+
);
|
|
358
406
|
}
|
|
359
407
|
for (const rule of rules) {
|
|
360
408
|
const ruleValidation = await validateRule(rule);
|
|
@@ -427,7 +475,10 @@ async function generateCommand(options = {}) {
|
|
|
427
475
|
deleteTasks.push(removeDirectory(config.outputPaths.cline));
|
|
428
476
|
break;
|
|
429
477
|
case "claude":
|
|
430
|
-
deleteTasks.push(
|
|
478
|
+
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
479
|
+
break;
|
|
480
|
+
case "roo":
|
|
481
|
+
deleteTasks.push(removeDirectory(config.outputPaths.roo));
|
|
431
482
|
break;
|
|
432
483
|
}
|
|
433
484
|
}
|
|
@@ -455,15 +506,18 @@ async function generateCommand(options = {}) {
|
|
|
455
506
|
|
|
456
507
|
// src/cli/commands/gitignore.ts
|
|
457
508
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
458
|
-
import { join as
|
|
509
|
+
import { join as join7 } from "path";
|
|
459
510
|
var gitignoreCommand = async () => {
|
|
460
|
-
const gitignorePath =
|
|
511
|
+
const gitignorePath = join7(process.cwd(), ".gitignore");
|
|
461
512
|
const rulesFilesToIgnore = [
|
|
462
513
|
"# Generated by rulesync - AI tool configuration files",
|
|
514
|
+
".github/copilot-instructions.md",
|
|
463
515
|
".github/instructions/",
|
|
464
516
|
".cursor/rules/",
|
|
465
517
|
".clinerules/",
|
|
466
|
-
"CLAUDE.md"
|
|
518
|
+
"CLAUDE.md",
|
|
519
|
+
".claude/memories/",
|
|
520
|
+
".roo/rules/"
|
|
467
521
|
];
|
|
468
522
|
let gitignoreContent = "";
|
|
469
523
|
if (existsSync(gitignorePath)) {
|
|
@@ -494,7 +548,7 @@ ${linesToAdd.join("\n")}
|
|
|
494
548
|
};
|
|
495
549
|
|
|
496
550
|
// src/cli/commands/init.ts
|
|
497
|
-
import { join as
|
|
551
|
+
import { join as join8 } from "path";
|
|
498
552
|
async function initCommand() {
|
|
499
553
|
const aiRulesDir = ".rulesync";
|
|
500
554
|
console.log("Initializing rulesync...");
|
|
@@ -584,7 +638,7 @@ globs: ["src/**/*.ts"]
|
|
|
584
638
|
}
|
|
585
639
|
];
|
|
586
640
|
for (const file of sampleFiles) {
|
|
587
|
-
const filepath =
|
|
641
|
+
const filepath = join8(aiRulesDir, file.filename);
|
|
588
642
|
if (!await fileExists(filepath)) {
|
|
589
643
|
await writeFileContent(filepath, file.content);
|
|
590
644
|
console.log(`Created ${filepath}`);
|
package/package.json
CHANGED
|
@@ -1,71 +1,72 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
2
|
+
"name": "rulesync",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"rulesync": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"dev": "tsx src/cli/index.ts",
|
|
16
|
+
"build": "tsup src/cli/index.ts --format cjs,esm --dts --clean",
|
|
17
|
+
"lint": "biome lint src/",
|
|
18
|
+
"format": "biome format --write src/",
|
|
19
|
+
"format:check": "biome format src/",
|
|
20
|
+
"check": "biome check src/",
|
|
21
|
+
"secretlint": "secretlint \"**/*\"",
|
|
22
|
+
"test": "vitest",
|
|
23
|
+
"test:watch": "vitest --watch",
|
|
24
|
+
"test:coverage": "vitest --coverage",
|
|
25
|
+
"prepublishOnly": "pnpm build"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20.0.0"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"ai",
|
|
32
|
+
"rules",
|
|
33
|
+
"cli",
|
|
34
|
+
"copilot",
|
|
35
|
+
"cursor",
|
|
36
|
+
"cline",
|
|
37
|
+
"configuration",
|
|
38
|
+
"development"
|
|
39
|
+
],
|
|
40
|
+
"author": "dyoshikawa",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/dyoshikawa/rulesync.git"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/dyoshikawa/rulesync/issues"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/dyoshikawa/rulesync#readme",
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"packageManager": "pnpm@7.33.7",
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@biomejs/biome": "2.0.0",
|
|
56
|
+
"@secretlint/secretlint-rule-preset-recommend": "10.1.0",
|
|
57
|
+
"@tsconfig/node24": "24.0.1",
|
|
58
|
+
"@types/node": "24.0.3",
|
|
59
|
+
"@vitest/coverage-v8": "3.2.4",
|
|
60
|
+
"secretlint": "10.1.0",
|
|
61
|
+
"tsup": "8.5.0",
|
|
62
|
+
"tsx": "4.20.3",
|
|
63
|
+
"typescript": "5.8.3",
|
|
64
|
+
"vitest": "3.2.4"
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"chokidar": "4.0.3",
|
|
68
|
+
"commander": "14.0.0",
|
|
69
|
+
"gray-matter": "4.0.3",
|
|
70
|
+
"marked": "15.0.12"
|
|
71
|
+
}
|
|
72
|
+
}
|