rulesync 0.4.0 → 0.5.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 +237 -37
- package/dist/index.js +16 -14
- package/dist/index.mjs +16 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,9 +8,9 @@ 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
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
@@ -22,6 +22,132 @@ pnpm add -g rulesync
|
|
|
22
22
|
yarn global add rulesync
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
## Getting Started
|
|
26
|
+
|
|
27
|
+
### Quick Start Example
|
|
28
|
+
|
|
29
|
+
1. **Initialize your project:**
|
|
30
|
+
```bash
|
|
31
|
+
rulesync init
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
2. **Create an overview file** (`.rulesync/overview.md`):
|
|
35
|
+
```markdown
|
|
36
|
+
---
|
|
37
|
+
root: true
|
|
38
|
+
targets: ["*"]
|
|
39
|
+
description: "Project overview and development philosophy"
|
|
40
|
+
globs: ["src/**/*.ts", "src/**/*.js"]
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
# Project Development Guidelines
|
|
44
|
+
|
|
45
|
+
This is a TypeScript/JavaScript project following clean architecture principles.
|
|
46
|
+
We prioritize code readability, maintainability, and type safety.
|
|
47
|
+
|
|
48
|
+
## Tech Stack
|
|
49
|
+
- TypeScript for type safety
|
|
50
|
+
- Node.js runtime
|
|
51
|
+
- Modern ES6+ features
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
3. **Create detail rules** (`.rulesync/coding-rules.md`):
|
|
55
|
+
```markdown
|
|
56
|
+
---
|
|
57
|
+
root: false
|
|
58
|
+
targets: ["copilot", "cursor", "cline"]
|
|
59
|
+
description: "TypeScript coding standards and best practices"
|
|
60
|
+
globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
# TypeScript Coding Rules
|
|
64
|
+
|
|
65
|
+
## Code Style
|
|
66
|
+
- Use strict TypeScript configuration
|
|
67
|
+
- Prefer `const` over `let` when possible
|
|
68
|
+
- Use meaningful, descriptive variable names
|
|
69
|
+
- Write JSDoc comments for public APIs
|
|
70
|
+
|
|
71
|
+
## Type Definitions
|
|
72
|
+
- Prefer interfaces over types for object shapes
|
|
73
|
+
- Use union types for controlled values
|
|
74
|
+
- Avoid `any` type - use `unknown` instead
|
|
75
|
+
- Define return types for functions explicitly
|
|
76
|
+
|
|
77
|
+
## Error Handling
|
|
78
|
+
- Use Result pattern for error handling
|
|
79
|
+
- Throw errors only for unexpected conditions
|
|
80
|
+
- Validate input parameters at function boundaries
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
4. **Generate configuration files:**
|
|
84
|
+
```bash
|
|
85
|
+
rulesync generate
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
5. **Optional: Add generated files to .gitignore:**
|
|
89
|
+
```bash
|
|
90
|
+
rulesync gitignore
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This will create tool-specific configuration files that your AI coding assistants can use automatically.
|
|
94
|
+
|
|
95
|
+
## Why rulesync?
|
|
96
|
+
|
|
97
|
+
### 🔧 **Tool Flexibility**
|
|
98
|
+
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.
|
|
99
|
+
|
|
100
|
+
### 📈 **Future-Proof Development**
|
|
101
|
+
AI development tools evolve rapidly with new tools emerging frequently. With rulesync, switching between tools doesn't require redefining your rules from scratch.
|
|
102
|
+
|
|
103
|
+
### 🎯 **Multi-Tool Workflow**
|
|
104
|
+
Enable hybrid development workflows combining multiple AI tools:
|
|
105
|
+
- GitHub Copilot for code completion
|
|
106
|
+
- Cursor for refactoring
|
|
107
|
+
- Claude Code for architecture design
|
|
108
|
+
- Cline for debugging assistance
|
|
109
|
+
|
|
110
|
+
### 🔓 **No Vendor Lock-in**
|
|
111
|
+
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.
|
|
112
|
+
|
|
113
|
+
### 🎯 **Consistency Across Tools**
|
|
114
|
+
Apply consistent rules across all AI tools, improving code quality and development experience for the entire team.
|
|
115
|
+
|
|
116
|
+
## Claude Code Integration
|
|
117
|
+
|
|
118
|
+
### Creating Custom Slash Commands
|
|
119
|
+
|
|
120
|
+
Instead of using Claude Code's built-in `/init` command, we recommend creating a custom slash command specifically for rulesync.
|
|
121
|
+
|
|
122
|
+
Refer to the [Claude Code slash commands documentation](https://docs.anthropic.com/en/docs/claude-code/slash-commands) and add the following custom command:
|
|
123
|
+
|
|
124
|
+
**`.claude/commands/init-rulesync.md`**
|
|
125
|
+
|
|
126
|
+
```markdown
|
|
127
|
+
Review this project's content and update .rulesync/*.md files as needed.
|
|
128
|
+
|
|
129
|
+
Steps:
|
|
130
|
+
1. Analyze project structure and codebase
|
|
131
|
+
2. Review existing .rulesync/ files
|
|
132
|
+
3. Consider project's tech stack, architecture, and coding conventions
|
|
133
|
+
4. Update .rulesync/*.md files if missing elements or improvements are found
|
|
134
|
+
5. Run rulesync generate if necessary
|
|
135
|
+
|
|
136
|
+
Project characteristics to consider:
|
|
137
|
+
- Technology stack
|
|
138
|
+
- Architecture patterns
|
|
139
|
+
- Coding conventions
|
|
140
|
+
- Security requirements
|
|
141
|
+
- Performance considerations
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Integration Benefits
|
|
145
|
+
|
|
146
|
+
- **Project-Specific Initialization**: Optimized rule configuration for each project
|
|
147
|
+
- **Automatic Rule Updates**: Rules adapt to project changes automatically
|
|
148
|
+
- **Team Standardization**: All members use the same rule set
|
|
149
|
+
- **Continuous Improvement**: Rules evolve with project growth
|
|
150
|
+
|
|
25
151
|
## Usage
|
|
26
152
|
|
|
27
153
|
### 1. Initialize
|
|
@@ -38,7 +164,7 @@ Define metadata in front matter for each Markdown file:
|
|
|
38
164
|
|
|
39
165
|
```markdown
|
|
40
166
|
---
|
|
41
|
-
|
|
167
|
+
root: true # or false
|
|
42
168
|
targets: ["*"] # or [copilot, cursor, cline, claudecode]
|
|
43
169
|
description: "TypeScript coding rules"
|
|
44
170
|
globs: ["**/*.ts", "**/*.tsx"]
|
|
@@ -52,12 +178,25 @@ globs: ["**/*.ts", "**/*.tsx"]
|
|
|
52
178
|
|
|
53
179
|
### Rule Levels
|
|
54
180
|
|
|
55
|
-
|
|
56
|
-
|
|
181
|
+
rulesync uses a two-level rule system:
|
|
182
|
+
|
|
183
|
+
- **root: true**: Project-wide overview and policies
|
|
184
|
+
- Only **one** root file is allowed per project
|
|
185
|
+
- Contains high-level guidelines and project context
|
|
186
|
+
- **root: false**: Specific implementation rules and detailed guidelines
|
|
187
|
+
- Multiple non-root files are allowed
|
|
188
|
+
- Contains specific coding rules, naming conventions, etc.
|
|
189
|
+
|
|
190
|
+
#### Tool-Specific Behavior
|
|
191
|
+
|
|
192
|
+
Each AI tool handles rule levels differently:
|
|
57
193
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
194
|
+
| Tool | Root Rules | Non-Root Rules | Special Behavior |
|
|
195
|
+
|------|------------|----------------|------------------|
|
|
196
|
+
| **Claude Code** | `./CLAUDE.md` | `.claude/memories/*.md` | CLAUDE.md includes `@filename` references to detail files |
|
|
197
|
+
| **Cursor** | `ruletype: always` | `ruletype: autoattached` | Detail rules without globs use `ruletype: agentrequested` |
|
|
198
|
+
| **GitHub Copilot** | Standard format | Standard format | All rules use same format with frontmatter |
|
|
199
|
+
| **Cline** | Standard format | Standard format | All rules use plain Markdown format |
|
|
61
200
|
|
|
62
201
|
### 3. Generate Configuration Files
|
|
63
202
|
|
|
@@ -70,62 +209,123 @@ rulesync generate --copilot
|
|
|
70
209
|
rulesync generate --cursor
|
|
71
210
|
rulesync generate --cline
|
|
72
211
|
rulesync generate --claude
|
|
212
|
+
|
|
213
|
+
# Clean build (delete existing files first)
|
|
214
|
+
rulesync generate --delete
|
|
215
|
+
|
|
216
|
+
# Clean build for specific tools
|
|
217
|
+
rulesync generate --copilot --cursor --delete
|
|
218
|
+
|
|
219
|
+
# Verbose output
|
|
220
|
+
rulesync generate --verbose
|
|
221
|
+
rulesync generate --delete --verbose
|
|
73
222
|
```
|
|
74
223
|
|
|
224
|
+
#### Generate Options
|
|
225
|
+
|
|
226
|
+
- `--delete`: Remove all existing generated files before creating new ones
|
|
227
|
+
- `--verbose`: Show detailed output during generation process
|
|
228
|
+
- `--copilot`, `--cursor`, `--cline`, `--claude`: Generate only for specified tools
|
|
229
|
+
|
|
75
230
|
### 4. Other Commands
|
|
76
231
|
|
|
77
232
|
```bash
|
|
78
|
-
#
|
|
233
|
+
# Initialize project with sample files
|
|
234
|
+
rulesync init
|
|
235
|
+
|
|
236
|
+
# Validate rule files
|
|
79
237
|
rulesync validate
|
|
80
238
|
|
|
81
|
-
# Check current status
|
|
239
|
+
# Check current status
|
|
82
240
|
rulesync status
|
|
83
241
|
|
|
84
242
|
# Watch files and auto-generate
|
|
85
243
|
rulesync watch
|
|
244
|
+
|
|
245
|
+
# Add generated files to .gitignore
|
|
246
|
+
rulesync gitignore
|
|
86
247
|
```
|
|
87
248
|
|
|
88
249
|
## Configuration File Structure
|
|
89
250
|
|
|
90
251
|
```
|
|
91
252
|
.rulesync/
|
|
92
|
-
├──
|
|
93
|
-
├──
|
|
94
|
-
├──
|
|
95
|
-
├──
|
|
96
|
-
|
|
253
|
+
├── overview.md # Project overview (root: true, only one)
|
|
254
|
+
├── coding-rules.md # Coding rules (root: false)
|
|
255
|
+
├── naming-conventions.md # Naming conventions (root: false)
|
|
256
|
+
├── architecture.md # Architecture guidelines (root: false)
|
|
257
|
+
├── security.md # Security rules (root: false)
|
|
258
|
+
└── custom.md # Project-specific rules (root: false)
|
|
97
259
|
```
|
|
98
260
|
|
|
99
|
-
|
|
261
|
+
### Frontmatter Schema
|
|
100
262
|
|
|
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 |
|
|
263
|
+
Each rule file must include frontmatter with the following fields:
|
|
107
264
|
|
|
108
|
-
|
|
265
|
+
```yaml
|
|
266
|
+
---
|
|
267
|
+
root: true | false # Required: Rule level (true for overview, false for details)
|
|
268
|
+
targets: ["*"] # Required: Target tools (* = all, or specific tools)
|
|
269
|
+
description: "Brief description" # Required: Rule description
|
|
270
|
+
globs: ["**/*.ts", "**/*.js"] # Required: File patterns (can be empty array)
|
|
271
|
+
---
|
|
272
|
+
```
|
|
109
273
|
|
|
110
|
-
|
|
111
|
-
# Install dependencies
|
|
112
|
-
pnpm install
|
|
274
|
+
### Example Files
|
|
113
275
|
|
|
114
|
-
|
|
115
|
-
|
|
276
|
+
**Root file** (`.rulesync/overview.md`):
|
|
277
|
+
```markdown
|
|
278
|
+
---
|
|
279
|
+
root: true
|
|
280
|
+
targets: ["*"]
|
|
281
|
+
description: "Project overview and development philosophy"
|
|
282
|
+
globs: ["src/**/*.ts"]
|
|
283
|
+
---
|
|
116
284
|
|
|
117
|
-
#
|
|
118
|
-
pnpm build
|
|
285
|
+
# Project Development Guidelines
|
|
119
286
|
|
|
120
|
-
|
|
121
|
-
|
|
287
|
+
This project follows TypeScript-first development with clean architecture principles.
|
|
288
|
+
```
|
|
122
289
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
290
|
+
**Non-root file** (`.rulesync/coding-rules.md`):
|
|
291
|
+
```markdown
|
|
292
|
+
---
|
|
293
|
+
root: false
|
|
294
|
+
targets: ["copilot", "cursor"]
|
|
295
|
+
description: "TypeScript coding standards"
|
|
296
|
+
globs: ["**/*.ts", "**/*.tsx"]
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
# TypeScript Coding Rules
|
|
300
|
+
|
|
301
|
+
- Use strict TypeScript configuration
|
|
302
|
+
- Prefer interfaces over types for object shapes
|
|
303
|
+
- Use meaningful variable names
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
## Generated Configuration Files
|
|
307
|
+
|
|
308
|
+
| Tool | Output Path | Format | Rule Level Handling |
|
|
309
|
+
|------|------------|--------|-------------------|
|
|
310
|
+
| **GitHub Copilot** | `.github/instructions/*.instructions.md` | Front Matter + Markdown | Both levels use same format |
|
|
311
|
+
| **Cursor** | `.cursor/rules/*.mdc` | MDC (YAML header + Markdown) | Root: `ruletype: always`<br>Non-root: `ruletype: autoattached`<br>Non-root without globs: `ruletype: agentrequested` |
|
|
312
|
+
| **Cline** | `.clinerules/*.md` | Plain Markdown | Both levels use same format |
|
|
313
|
+
| **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 |
|
|
314
|
+
|
|
315
|
+
## Validation
|
|
316
|
+
|
|
317
|
+
rulesync validates your rule files and provides helpful error messages:
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
rulesync validate
|
|
127
321
|
```
|
|
128
322
|
|
|
323
|
+
Common validation rules:
|
|
324
|
+
- Only one root file (root: true) is allowed per project
|
|
325
|
+
- All frontmatter fields are required and properly formatted
|
|
326
|
+
- File patterns (globs) use valid syntax
|
|
327
|
+
- Target tools are recognized values
|
|
328
|
+
|
|
129
329
|
## License
|
|
130
330
|
|
|
131
331
|
MIT License
|
|
@@ -134,4 +334,4 @@ MIT License
|
|
|
134
334
|
|
|
135
335
|
Issues and Pull Requests are welcome!
|
|
136
336
|
|
|
137
|
-
For
|
|
337
|
+
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,7 +48,7 @@ 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
54
|
for (const rule of detailRules) {
|
|
@@ -62,8 +62,8 @@ function generateClaudeMarkdown(overviewRules, detailRules) {
|
|
|
62
62
|
"Generated from rulesync configuration. These instructions guide Claude Code's behavior for this project."
|
|
63
63
|
);
|
|
64
64
|
lines.push("");
|
|
65
|
-
if (
|
|
66
|
-
for (const rule of
|
|
65
|
+
if (rootRules.length > 0) {
|
|
66
|
+
for (const rule of rootRules) {
|
|
67
67
|
lines.push(...formatRuleForClaude(rule));
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -169,7 +169,7 @@ async function generateCursorConfig(rules, config) {
|
|
|
169
169
|
const outputs = [];
|
|
170
170
|
for (const rule of rules) {
|
|
171
171
|
const content = generateCursorMarkdown(rule);
|
|
172
|
-
const filepath = (0, import_node_path4.join)(config.outputPaths.cursor, `${rule.filename}.
|
|
172
|
+
const filepath = (0, import_node_path4.join)(config.outputPaths.cursor, `${rule.filename}.mdc`);
|
|
173
173
|
outputs.push({
|
|
174
174
|
tool: "cursor",
|
|
175
175
|
filepath,
|
|
@@ -186,9 +186,9 @@ function generateCursorMarkdown(rule) {
|
|
|
186
186
|
lines.push(`globs: [${rule.frontmatter.globs.map((g) => `"${g}"`).join(", ")}]`);
|
|
187
187
|
}
|
|
188
188
|
let ruletype;
|
|
189
|
-
if (rule.frontmatter.
|
|
189
|
+
if (rule.frontmatter.root === true) {
|
|
190
190
|
ruletype = "always";
|
|
191
|
-
} else if (rule.frontmatter.
|
|
191
|
+
} else if (rule.frontmatter.root === false && rule.frontmatter.globs.length === 0) {
|
|
192
192
|
ruletype = "agentrequested";
|
|
193
193
|
} else {
|
|
194
194
|
ruletype = "autoattached";
|
|
@@ -337,8 +337,8 @@ function validateFrontmatter(data, filepath) {
|
|
|
337
337
|
throw new Error(`Invalid frontmatter in ${filepath}: must be an object`);
|
|
338
338
|
}
|
|
339
339
|
const obj = data;
|
|
340
|
-
if (
|
|
341
|
-
throw new Error(`Invalid
|
|
340
|
+
if (typeof obj.root !== "boolean") {
|
|
341
|
+
throw new Error(`Invalid root in ${filepath}: must be a boolean`);
|
|
342
342
|
}
|
|
343
343
|
if (!Array.isArray(obj.targets)) {
|
|
344
344
|
throw new Error(`Invalid targets in ${filepath}: must be an array`);
|
|
@@ -375,9 +375,11 @@ async function validateRules(rules) {
|
|
|
375
375
|
}
|
|
376
376
|
filenames.add(rule.filename);
|
|
377
377
|
}
|
|
378
|
-
const
|
|
379
|
-
if (
|
|
380
|
-
errors.push(
|
|
378
|
+
const rootRules = rules.filter((rule) => rule.frontmatter.root === true);
|
|
379
|
+
if (rootRules.length > 1) {
|
|
380
|
+
errors.push(
|
|
381
|
+
`Multiple root rules found: ${rootRules.map((r) => r.filename).join(", ")}. Only one root rule is allowed.`
|
|
382
|
+
);
|
|
381
383
|
}
|
|
382
384
|
for (const rule of rules) {
|
|
383
385
|
const ruleValidation = await validateRule(rule);
|
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,7 +25,7 @@ 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
31
|
for (const rule of detailRules) {
|
|
@@ -39,8 +39,8 @@ function generateClaudeMarkdown(overviewRules, detailRules) {
|
|
|
39
39
|
"Generated from rulesync configuration. These instructions guide Claude Code's behavior for this project."
|
|
40
40
|
);
|
|
41
41
|
lines.push("");
|
|
42
|
-
if (
|
|
43
|
-
for (const rule of
|
|
42
|
+
if (rootRules.length > 0) {
|
|
43
|
+
for (const rule of rootRules) {
|
|
44
44
|
lines.push(...formatRuleForClaude(rule));
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -146,7 +146,7 @@ async function generateCursorConfig(rules, config) {
|
|
|
146
146
|
const outputs = [];
|
|
147
147
|
for (const rule of rules) {
|
|
148
148
|
const content = generateCursorMarkdown(rule);
|
|
149
|
-
const filepath = join4(config.outputPaths.cursor, `${rule.filename}.
|
|
149
|
+
const filepath = join4(config.outputPaths.cursor, `${rule.filename}.mdc`);
|
|
150
150
|
outputs.push({
|
|
151
151
|
tool: "cursor",
|
|
152
152
|
filepath,
|
|
@@ -163,9 +163,9 @@ function generateCursorMarkdown(rule) {
|
|
|
163
163
|
lines.push(`globs: [${rule.frontmatter.globs.map((g) => `"${g}"`).join(", ")}]`);
|
|
164
164
|
}
|
|
165
165
|
let ruletype;
|
|
166
|
-
if (rule.frontmatter.
|
|
166
|
+
if (rule.frontmatter.root === true) {
|
|
167
167
|
ruletype = "always";
|
|
168
|
-
} else if (rule.frontmatter.
|
|
168
|
+
} else if (rule.frontmatter.root === false && rule.frontmatter.globs.length === 0) {
|
|
169
169
|
ruletype = "agentrequested";
|
|
170
170
|
} else {
|
|
171
171
|
ruletype = "autoattached";
|
|
@@ -314,8 +314,8 @@ function validateFrontmatter(data, filepath) {
|
|
|
314
314
|
throw new Error(`Invalid frontmatter in ${filepath}: must be an object`);
|
|
315
315
|
}
|
|
316
316
|
const obj = data;
|
|
317
|
-
if (
|
|
318
|
-
throw new Error(`Invalid
|
|
317
|
+
if (typeof obj.root !== "boolean") {
|
|
318
|
+
throw new Error(`Invalid root in ${filepath}: must be a boolean`);
|
|
319
319
|
}
|
|
320
320
|
if (!Array.isArray(obj.targets)) {
|
|
321
321
|
throw new Error(`Invalid targets in ${filepath}: must be an array`);
|
|
@@ -352,9 +352,11 @@ async function validateRules(rules) {
|
|
|
352
352
|
}
|
|
353
353
|
filenames.add(rule.filename);
|
|
354
354
|
}
|
|
355
|
-
const
|
|
356
|
-
if (
|
|
357
|
-
errors.push(
|
|
355
|
+
const rootRules = rules.filter((rule) => rule.frontmatter.root === true);
|
|
356
|
+
if (rootRules.length > 1) {
|
|
357
|
+
errors.push(
|
|
358
|
+
`Multiple root rules found: ${rootRules.map((r) => r.filename).join(", ")}. Only one root rule is allowed.`
|
|
359
|
+
);
|
|
358
360
|
}
|
|
359
361
|
for (const rule of rules) {
|
|
360
362
|
const ruleValidation = await validateRule(rule);
|
package/package.json
CHANGED