rulesync 0.40.0 → 0.41.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 +57 -19
- package/README.md +44 -4
- package/dist/{chunk-XSHEX7Q4.js → chunk-PPX47BRK.js} +4 -0
- package/dist/index.cjs +132 -30
- package/dist/index.js +133 -31
- package/dist/{roo-EHU65CDY.js → roo-KUGAURJB.js} +1 -1
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -164,14 +164,14 @@ rulesyncは2レベルのルールシステムを使用します:
|
|
|
164
164
|
|
|
165
165
|
各AIツールはルールレベルを異なって処理します:
|
|
166
166
|
|
|
167
|
-
| ツール
|
|
168
|
-
|
|
169
|
-
| **Claude Code**
|
|
170
|
-
| **Cursor**
|
|
171
|
-
| **GitHub Copilot** | 標準フォーマット
|
|
172
|
-
| **Cline**
|
|
173
|
-
| **Roo Code**
|
|
174
|
-
| **Gemini CLI**
|
|
167
|
+
| ツール | ルートルール | 非ルートルール | 特別な動作 |
|
|
168
|
+
| ------------------ | ------------------ | ------------------------ | -------------------------------------------------------------------- |
|
|
169
|
+
| **Claude Code** | `./CLAUDE.md` | `.claude/memories/*.md` | CLAUDE.mdが詳細ファイルへの`@filename`参照を含む |
|
|
170
|
+
| **Cursor** | `cursorRuleType: always` | `cursorRuleType: specificFiles` (globs指定時)<br>`cursorRuleType: intelligently` (description指定時)<br>`cursorRuleType: manual` (デフォルト) | コンテンツ解析に基づく高度なルールタイプシステム |
|
|
171
|
+
| **GitHub Copilot** | 標準フォーマット | 標準フォーマット | すべてのルールがフロントマター付きの同じフォーマットを使用 |
|
|
172
|
+
| **Cline** | 標準フォーマット | 標準フォーマット | すべてのルールがプレーンMarkdownフォーマットを使用 |
|
|
173
|
+
| **Roo Code** | 標準フォーマット | 標準フォーマット | すべてのルールが説明ヘッダー付きのプレーンMarkdownフォーマットを使用 |
|
|
174
|
+
| **Gemini CLI** | `GEMINI.md` | `.gemini/memories/*.md` | GEMINI.mdがメモリファイルへの`@filename`参照を含む |
|
|
175
175
|
|
|
176
176
|
### 3. 設定ファイルの生成
|
|
177
177
|
|
|
@@ -241,6 +241,34 @@ importコマンドの動作:
|
|
|
241
241
|
- YAMLフロントマター付きのCursorのMDCファイルなど複雑なフォーマットをサポート
|
|
242
242
|
- 複数ファイルのインポート(例:`.claude/memories/`ディレクトリのすべてのファイル)に対応
|
|
243
243
|
|
|
244
|
+
### Cursorインポートの詳細
|
|
245
|
+
|
|
246
|
+
Cursorからのインポートでは、以下の4つのルールタイプが自動的に識別されます:
|
|
247
|
+
|
|
248
|
+
1. **always** (`cursorRuleType: always`)
|
|
249
|
+
- 条件: `alwaysApply: true` が設定されている場合
|
|
250
|
+
- 変換: ルートルール(`root: false`)としてインポート、`globs: ["**/*"]`を設定
|
|
251
|
+
|
|
252
|
+
2. **manual** (`cursorRuleType: manual`)
|
|
253
|
+
- 条件: description空 + globs空 + `alwaysApply: false`
|
|
254
|
+
- 変換: 空のglobsパターンでインポート(手動適用ルール)
|
|
255
|
+
|
|
256
|
+
3. **specificFiles** (`cursorRuleType: specificFiles`)
|
|
257
|
+
- 条件: globs指定あり(description有無問わず)
|
|
258
|
+
- 変換: 指定されたglobsパターンを配列として保持、descriptionは空文字に設定
|
|
259
|
+
|
|
260
|
+
4. **intelligently** (`cursorRuleType: intelligently`)
|
|
261
|
+
- 条件: description指定あり + globs空
|
|
262
|
+
- 変換: descriptionを保持、空のglobsパターンを設定
|
|
263
|
+
|
|
264
|
+
#### エッジケース処理
|
|
265
|
+
- **description非空 + globs非空の場合**: `specificFiles`として処理(globsパターンを優先)
|
|
266
|
+
- **判定条件に該当しない場合**: `manual`として処理(デフォルト)
|
|
267
|
+
|
|
268
|
+
#### Cursorのサポートファイル
|
|
269
|
+
- `.cursor/rules/*.mdc` (モダンな推奨形式)
|
|
270
|
+
- `.cursorrules` (レガシーな形式)
|
|
271
|
+
|
|
244
272
|
### 5. その他のコマンド
|
|
245
273
|
|
|
246
274
|
```bash
|
|
@@ -314,9 +342,19 @@ root: true | false # 必須: ルールレベル (概要の場合tr
|
|
|
314
342
|
targets: ["*"] # 必須: ターゲットツール (* = すべて、または特定のツール)
|
|
315
343
|
description: "簡潔な説明" # 必須: ルールの説明
|
|
316
344
|
globs: "**/*.ts,**/*.js" # 必須: ファイルパターン (カンマ区切りまたは空文字列)
|
|
345
|
+
cursorRuleType: "always" # オプション: Cursor固有のルールタイプ (always, manual, specificFiles, intelligently)
|
|
317
346
|
---
|
|
318
347
|
```
|
|
319
348
|
|
|
349
|
+
#### cursorRuleTypeフィールド (オプション)
|
|
350
|
+
|
|
351
|
+
Cursorツール用の追加メタデータフィールド:
|
|
352
|
+
|
|
353
|
+
- **`always`**: プロジェクト全体に常に適用されるルール
|
|
354
|
+
- **`manual`**: 手動で適用するルール(デフォルト)
|
|
355
|
+
- **`specificFiles`**: 特定のファイルパターンに自動適用されるルール
|
|
356
|
+
- **`intelligently`**: AIが判断して適用するルール
|
|
357
|
+
|
|
320
358
|
### ファイル例
|
|
321
359
|
|
|
322
360
|
**ルートファイル** (`.rulesync/overview.md`):
|
|
@@ -351,14 +389,14 @@ globs: "**/*.ts,**/*.tsx"
|
|
|
351
389
|
|
|
352
390
|
## 生成される設定ファイル
|
|
353
391
|
|
|
354
|
-
| ツール
|
|
355
|
-
|
|
356
|
-
| **GitHub Copilot** | `.github/instructions/*.instructions.md`
|
|
357
|
-
| **Cursor**
|
|
358
|
-
| **Cline**
|
|
359
|
-
| **Claude Code**
|
|
360
|
-
| **Roo Code**
|
|
361
|
-
| **Gemini CLI**
|
|
392
|
+
| ツール | 出力パス | フォーマット | ルールレベル処理 |
|
|
393
|
+
| ------------------ | ------------------------------------------------------------ | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
394
|
+
| **GitHub Copilot** | `.github/instructions/*.instructions.md` | フロントマター + Markdown | 両レベルとも同じフォーマットを使用 |
|
|
395
|
+
| **Cursor** | `.cursor/rules/*.mdc` | MDC (YAMLヘッダー + Markdown) | ルート: `cursorRuleType: always`<br>非ルート: `cursorRuleType: specificFiles` (globs指定時)<br>非ルート: `cursorRuleType: intelligently` (description指定時)<br>非ルート: `cursorRuleType: manual` (デフォルト) |
|
|
396
|
+
| **Cline** | `.clinerules/*.md` | プレーンMarkdown | 両レベルとも同じフォーマットを使用 |
|
|
397
|
+
| **Claude Code** | `./CLAUDE.md` (ルート)<br>`.claude/memories/*.md` (非ルート) | プレーンMarkdown | ルートはCLAUDE.mdに移動<br>非ルートは別メモリファイルに移動<br>CLAUDE.mdは`@filename`参照を含む |
|
|
398
|
+
| **Roo Code** | `.roo/rules/*.md` | プレーンMarkdown | 両レベルとも説明ヘッダー付きの同じフォーマットを使用 |
|
|
399
|
+
| **Gemini CLI** | `GEMINI.md` (ルート)<br>`.gemini/memories/*.md` (非ルート) | プレーンMarkdown | ルートはGEMINI.mdに移動<br>非ルートは別メモリファイルに移動<br>GEMINI.mdは`@filename`参照を含む |
|
|
362
400
|
|
|
363
401
|
## バリデーション
|
|
364
402
|
|
|
@@ -402,7 +440,7 @@ rulesyncは、対応するAIツール用のMCPサーバー設定も管理でき
|
|
|
402
440
|
"ghcr.io/github/github-mcp-server"
|
|
403
441
|
],
|
|
404
442
|
"env": {},
|
|
405
|
-
"
|
|
443
|
+
"targets": ["*"]
|
|
406
444
|
}
|
|
407
445
|
}
|
|
408
446
|
}
|
|
@@ -415,7 +453,7 @@ rulesyncは、対応するAIツール用のMCPサーバー設定も管理でき
|
|
|
415
453
|
- **`args`**: コマンド引数
|
|
416
454
|
- **`url`**: HTTP/SSEベースのサーバー用URL
|
|
417
455
|
- **`env`**: サーバーに渡す環境変数
|
|
418
|
-
- **`
|
|
456
|
+
- **`targets`**: このサーバーをデプロイするツール名の配列
|
|
419
457
|
- 特定のツール名を使用: `["claude", "cursor", "copilot"]`
|
|
420
458
|
- すべてのサポートツールにデプロイするには`["*"]`を使用
|
|
421
459
|
- 省略した場合、デフォルトですべてのツールにデプロイ
|
|
@@ -445,4 +483,4 @@ MIT License
|
|
|
445
483
|
|
|
446
484
|
Issues と Pull Requests を歓迎します!
|
|
447
485
|
|
|
448
|
-
開発環境の設定と貢献ガイドラインについては、[CONTRIBUTING.ja.md](./CONTRIBUTING.ja.md)を参照してください。
|
|
486
|
+
開発環境の設定と貢献ガイドラインについては、[CONTRIBUTING.ja.md](./CONTRIBUTING.ja.md)を参照してください。
|
package/README.md
CHANGED
|
@@ -167,7 +167,7 @@ Each AI tool handles rule levels differently:
|
|
|
167
167
|
| Tool | Root Rules | Non-Root Rules | Special Behavior |
|
|
168
168
|
|------|------------|----------------|------------------|
|
|
169
169
|
| **Claude Code** | `./CLAUDE.md` | `.claude/memories/*.md` | CLAUDE.md includes `@filename` references to detail files |
|
|
170
|
-
| **Cursor** | `
|
|
170
|
+
| **Cursor** | `cursorRuleType: always` | `cursorRuleType: specificFiles` (with globs)<br>`cursorRuleType: intelligently` (with description)<br>`cursorRuleType: manual` (default) | Advanced rule type system based on content analysis |
|
|
171
171
|
| **GitHub Copilot** | Standard format | Standard format | All rules use same format with frontmatter |
|
|
172
172
|
| **Cline** | Standard format | Standard format | All rules use plain Markdown format |
|
|
173
173
|
| **Roo Code** | Standard format | Standard format | All rules use plain Markdown format with description header |
|
|
@@ -241,6 +241,36 @@ The import command will:
|
|
|
241
241
|
- Support complex formats like Cursor's MDC files with YAML frontmatter
|
|
242
242
|
- Handle multiple file imports (e.g., all files from `.claude/memories/` directory)
|
|
243
243
|
|
|
244
|
+
### Cursor Import Details
|
|
245
|
+
|
|
246
|
+
When importing from Cursor, the following four rule types are automatically identified:
|
|
247
|
+
|
|
248
|
+
1. **always** (`cursorRuleType: always`)
|
|
249
|
+
- Condition: `alwaysApply: true` is set
|
|
250
|
+
- Conversion: Imported as root rule (`root: false`), with `globs: ["**/*"]` set
|
|
251
|
+
|
|
252
|
+
2. **manual** (`cursorRuleType: manual`)
|
|
253
|
+
- Condition: empty description + empty globs + `alwaysApply: false`
|
|
254
|
+
- Conversion: Imported with empty globs patterns (manual application rule)
|
|
255
|
+
|
|
256
|
+
3. **specificFiles** (`cursorRuleType: specificFiles`)
|
|
257
|
+
- Condition: globs specified (regardless of description)
|
|
258
|
+
- Conversion: Specified globs patterns preserved as array, description set to empty string
|
|
259
|
+
|
|
260
|
+
4. **intelligently** (`cursorRuleType: intelligently`)
|
|
261
|
+
- Condition: description specified + empty globs
|
|
262
|
+
- Conversion: Description preserved, empty globs patterns set
|
|
263
|
+
|
|
264
|
+
#### Edge Case Handling
|
|
265
|
+
- **Non-empty description + non-empty globs**: Processed as `specificFiles` (globs patterns take priority)
|
|
266
|
+
- **No matching conditions**: Processed as `manual` (default)
|
|
267
|
+
|
|
268
|
+
#### Supported Files
|
|
269
|
+
- `.cursorrules` (legacy format)
|
|
270
|
+
- `.cursor/rules/*.mdc` (modern MDC format)
|
|
271
|
+
- `.cursorignore` (ignore patterns)
|
|
272
|
+
- `.cursor/mcp.json` (MCP server configuration)
|
|
273
|
+
|
|
244
274
|
### 5. Other Commands
|
|
245
275
|
|
|
246
276
|
```bash
|
|
@@ -314,9 +344,19 @@ root: true | false # Required: Rule level (true for overview, fals
|
|
|
314
344
|
targets: ["*"] # Required: Target tools (* = all, or specific tools)
|
|
315
345
|
description: "Brief description" # Required: Rule description
|
|
316
346
|
globs: "**/*.ts,**/*.js" # Required: File patterns (comma-separated or empty string)
|
|
347
|
+
cursorRuleType: "always" # Optional: Cursor-specific rule type (always, manual, specificFiles, intelligently)
|
|
317
348
|
---
|
|
318
349
|
```
|
|
319
350
|
|
|
351
|
+
#### cursorRuleType Field (Optional)
|
|
352
|
+
|
|
353
|
+
Additional metadata field for Cursor tool:
|
|
354
|
+
|
|
355
|
+
- **`always`**: Rules applied to the entire project constantly
|
|
356
|
+
- **`manual`**: Rules applied manually (default)
|
|
357
|
+
- **`specificFiles`**: Rules automatically applied to specific file patterns
|
|
358
|
+
- **`intelligently`**: Rules applied by AI judgment
|
|
359
|
+
|
|
320
360
|
### Example Files
|
|
321
361
|
|
|
322
362
|
**Root file** (`.rulesync/overview.md`):
|
|
@@ -354,7 +394,7 @@ globs: "**/*.ts,**/*.tsx"
|
|
|
354
394
|
| Tool | Output Path | Format | Rule Level Handling |
|
|
355
395
|
|------|------------|--------|-------------------|
|
|
356
396
|
| **GitHub Copilot** | `.github/instructions/*.instructions.md` | Front Matter + Markdown | Both levels use same format |
|
|
357
|
-
| **Cursor** | `.cursor/rules/*.mdc` | MDC (YAML header + Markdown) | Root: `
|
|
397
|
+
| **Cursor** | `.cursor/rules/*.mdc` | MDC (YAML header + Markdown) | Root: `cursorRuleType: always`<br>Non-root: `cursorRuleType: specificFiles` (with globs)<br>Non-root: `cursorRuleType: intelligently` (with description)<br>Non-root: `cursorRuleType: manual` (default) |
|
|
358
398
|
| **Cline** | `.clinerules/*.md` | Plain Markdown | Both levels use same format |
|
|
359
399
|
| **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 |
|
|
360
400
|
| **Roo Code** | `.roo/rules/*.md` | Plain Markdown | Both levels use same format with description header |
|
|
@@ -402,7 +442,7 @@ Create a `.rulesync/.mcp.json` file in your project:
|
|
|
402
442
|
"ghcr.io/github/github-mcp-server"
|
|
403
443
|
],
|
|
404
444
|
"env": {},
|
|
405
|
-
"
|
|
445
|
+
"targets": ["*"]
|
|
406
446
|
}
|
|
407
447
|
}
|
|
408
448
|
}
|
|
@@ -415,7 +455,7 @@ Create a `.rulesync/.mcp.json` file in your project:
|
|
|
415
455
|
- **`args`**: Command arguments
|
|
416
456
|
- **`url`**: URL for HTTP/SSE-based servers
|
|
417
457
|
- **`env`**: Environment variables to pass to the server
|
|
418
|
-
- **`
|
|
458
|
+
- **`targets`**: Array of tool names to deploy this server to
|
|
419
459
|
- Use specific tool names: `["claude", "cursor", "copilot"]`
|
|
420
460
|
- Use `["*"]` to deploy to all supported tools
|
|
421
461
|
- If omitted, server is deployed to all tools by default
|
|
@@ -61,6 +61,10 @@ function generateRooMcpConfiguration(mcpServers, baseDir = "") {
|
|
|
61
61
|
continue;
|
|
62
62
|
}
|
|
63
63
|
const { targets: _targets, ...serverConfig } = serverObj;
|
|
64
|
+
if (serverConfig.httpUrl && serverConfig.url) {
|
|
65
|
+
serverConfig.url = serverConfig.httpUrl;
|
|
66
|
+
delete serverConfig.httpUrl;
|
|
67
|
+
}
|
|
64
68
|
config.mcpServers[serverName] = serverConfig;
|
|
65
69
|
}
|
|
66
70
|
return [
|
package/dist/index.cjs
CHANGED
|
@@ -773,24 +773,56 @@ async function generateCursorConfig(rules, config, baseDir) {
|
|
|
773
773
|
}
|
|
774
774
|
function generateCursorMarkdown(rule) {
|
|
775
775
|
const lines = [];
|
|
776
|
+
const ruleType = determineCursorRuleType(rule.frontmatter);
|
|
776
777
|
lines.push("---");
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
778
|
+
switch (ruleType) {
|
|
779
|
+
case "always":
|
|
780
|
+
lines.push("description:");
|
|
781
|
+
lines.push("globs:");
|
|
782
|
+
lines.push("alwaysApply: true");
|
|
783
|
+
break;
|
|
784
|
+
case "manual":
|
|
785
|
+
lines.push("description:");
|
|
786
|
+
lines.push("globs:");
|
|
787
|
+
lines.push("alwaysApply: false");
|
|
788
|
+
break;
|
|
789
|
+
case "specificFiles":
|
|
790
|
+
lines.push("description:");
|
|
791
|
+
lines.push(`globs: ${rule.frontmatter.globs.join(",")}`);
|
|
792
|
+
lines.push("alwaysApply: false");
|
|
793
|
+
break;
|
|
794
|
+
case "intelligently":
|
|
795
|
+
lines.push(`description: ${rule.frontmatter.description}`);
|
|
796
|
+
lines.push("globs:");
|
|
797
|
+
lines.push("alwaysApply: false");
|
|
798
|
+
break;
|
|
788
799
|
}
|
|
789
|
-
lines.push(`ruletype: ${ruletype}`);
|
|
790
800
|
lines.push("---");
|
|
801
|
+
lines.push("");
|
|
791
802
|
lines.push(rule.content);
|
|
792
803
|
return lines.join("\n");
|
|
793
804
|
}
|
|
805
|
+
function determineCursorRuleType(frontmatter) {
|
|
806
|
+
if (frontmatter.cursorRuleType) {
|
|
807
|
+
return frontmatter.cursorRuleType;
|
|
808
|
+
}
|
|
809
|
+
const isDescriptionEmpty = !frontmatter.description || frontmatter.description.trim() === "";
|
|
810
|
+
const isGlobsEmpty = frontmatter.globs.length === 0;
|
|
811
|
+
const isGlobsExactlyAllFiles = frontmatter.globs.length === 1 && frontmatter.globs[0] === "**/*";
|
|
812
|
+
if (isGlobsExactlyAllFiles) {
|
|
813
|
+
return "always";
|
|
814
|
+
}
|
|
815
|
+
if (isDescriptionEmpty && isGlobsEmpty) {
|
|
816
|
+
return "manual";
|
|
817
|
+
}
|
|
818
|
+
if (isDescriptionEmpty && !isGlobsEmpty) {
|
|
819
|
+
return "specificFiles";
|
|
820
|
+
}
|
|
821
|
+
if (!isDescriptionEmpty && isGlobsEmpty) {
|
|
822
|
+
return "intelligently";
|
|
823
|
+
}
|
|
824
|
+
return "intelligently";
|
|
825
|
+
}
|
|
794
826
|
function generateCursorIgnore(patterns) {
|
|
795
827
|
const lines = [
|
|
796
828
|
"# Generated by rulesync from .rulesyncignore",
|
|
@@ -1053,9 +1085,9 @@ function validateFrontmatter(data, filepath) {
|
|
|
1053
1085
|
`Missing required field "description" in ${filepath}: must be a descriptive string`
|
|
1054
1086
|
);
|
|
1055
1087
|
}
|
|
1056
|
-
if (
|
|
1088
|
+
if (typeof obj.description !== "string") {
|
|
1057
1089
|
throw new Error(
|
|
1058
|
-
`Invalid "description" field in ${filepath}: must be a
|
|
1090
|
+
`Invalid "description" field in ${filepath}: must be a string, got ${typeof obj.description}`
|
|
1059
1091
|
);
|
|
1060
1092
|
}
|
|
1061
1093
|
if (obj.globs === void 0) {
|
|
@@ -1419,7 +1451,7 @@ var gitignoreCommand = async () => {
|
|
|
1419
1451
|
}
|
|
1420
1452
|
}
|
|
1421
1453
|
if (linesToAdd.length === 0) {
|
|
1422
|
-
console.log("\u2705 .gitignore
|
|
1454
|
+
console.log("\u2705 .gitignore is already up to date");
|
|
1423
1455
|
return;
|
|
1424
1456
|
}
|
|
1425
1457
|
const newContent = gitignoreContent ? `${gitignoreContent.trimEnd()}
|
|
@@ -1428,7 +1460,7 @@ ${linesToAdd.join("\n")}
|
|
|
1428
1460
|
` : `${linesToAdd.join("\n")}
|
|
1429
1461
|
`;
|
|
1430
1462
|
(0, import_node_fs2.writeFileSync)(gitignorePath, newContent);
|
|
1431
|
-
console.log(`\u2705
|
|
1463
|
+
console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
|
|
1432
1464
|
for (const line of linesToAdd) {
|
|
1433
1465
|
if (!line.startsWith("#")) {
|
|
1434
1466
|
console.log(` ${line}`);
|
|
@@ -1732,7 +1764,7 @@ var customMatterOptions = {
|
|
|
1732
1764
|
yaml: {
|
|
1733
1765
|
parse: (str) => {
|
|
1734
1766
|
try {
|
|
1735
|
-
|
|
1767
|
+
let preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"').replace(/^(\s*globs:\s*)([^\s"'[\n][^"'[\n]*?)(\s*)$/gm, '$1"$2"$3');
|
|
1736
1768
|
return (0, import_js_yaml.load)(preprocessed, { schema: import_js_yaml.DEFAULT_SCHEMA });
|
|
1737
1769
|
} catch (error) {
|
|
1738
1770
|
try {
|
|
@@ -1745,6 +1777,85 @@ var customMatterOptions = {
|
|
|
1745
1777
|
}
|
|
1746
1778
|
}
|
|
1747
1779
|
};
|
|
1780
|
+
function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
1781
|
+
const frontmatter = cursorFrontmatter;
|
|
1782
|
+
const description = normalizeValue(frontmatter?.description);
|
|
1783
|
+
const globs = normalizeGlobsValue(frontmatter?.globs);
|
|
1784
|
+
const alwaysApply = frontmatter?.alwaysApply === true || frontmatter?.alwaysApply === "true";
|
|
1785
|
+
if (alwaysApply) {
|
|
1786
|
+
return {
|
|
1787
|
+
root: false,
|
|
1788
|
+
targets: ["*"],
|
|
1789
|
+
description: description || "",
|
|
1790
|
+
globs: ["**/*"],
|
|
1791
|
+
cursorRuleType: "always"
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
if (isEmpty(description) && isEmpty(globs)) {
|
|
1795
|
+
return {
|
|
1796
|
+
root: false,
|
|
1797
|
+
targets: ["*"],
|
|
1798
|
+
description: "",
|
|
1799
|
+
globs: [],
|
|
1800
|
+
cursorRuleType: "manual"
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
if (!isEmpty(globs)) {
|
|
1804
|
+
return {
|
|
1805
|
+
root: false,
|
|
1806
|
+
targets: ["*"],
|
|
1807
|
+
description: "",
|
|
1808
|
+
globs: convertGlobsToArray(globs),
|
|
1809
|
+
cursorRuleType: "specificFiles"
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
if (!isEmpty(description)) {
|
|
1813
|
+
return {
|
|
1814
|
+
root: false,
|
|
1815
|
+
targets: ["*"],
|
|
1816
|
+
description,
|
|
1817
|
+
globs: [],
|
|
1818
|
+
cursorRuleType: "intelligently"
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
return {
|
|
1822
|
+
root: false,
|
|
1823
|
+
targets: ["*"],
|
|
1824
|
+
description: "",
|
|
1825
|
+
globs: [],
|
|
1826
|
+
cursorRuleType: "manual"
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
function normalizeValue(value) {
|
|
1830
|
+
if (value === void 0 || value === null || value === "") {
|
|
1831
|
+
return void 0;
|
|
1832
|
+
}
|
|
1833
|
+
return String(value);
|
|
1834
|
+
}
|
|
1835
|
+
function normalizeGlobsValue(value) {
|
|
1836
|
+
if (value === void 0 || value === null || value === "") {
|
|
1837
|
+
return void 0;
|
|
1838
|
+
}
|
|
1839
|
+
if (Array.isArray(value)) {
|
|
1840
|
+
return value.length === 0 ? void 0 : value;
|
|
1841
|
+
}
|
|
1842
|
+
return String(value);
|
|
1843
|
+
}
|
|
1844
|
+
function isEmpty(value) {
|
|
1845
|
+
return value === void 0 || value === null || value === "";
|
|
1846
|
+
}
|
|
1847
|
+
function convertGlobsToArray(globs) {
|
|
1848
|
+
if (!globs) {
|
|
1849
|
+
return [];
|
|
1850
|
+
}
|
|
1851
|
+
if (Array.isArray(globs)) {
|
|
1852
|
+
return globs;
|
|
1853
|
+
}
|
|
1854
|
+
if (typeof globs === "string") {
|
|
1855
|
+
return globs.split(",").map((g) => g.trim()).filter((g) => g.length > 0);
|
|
1856
|
+
}
|
|
1857
|
+
return [];
|
|
1858
|
+
}
|
|
1748
1859
|
async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
1749
1860
|
const errors = [];
|
|
1750
1861
|
const rules = [];
|
|
@@ -1757,12 +1868,8 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1757
1868
|
const parsed = (0, import_gray_matter3.default)(rawContent, customMatterOptions);
|
|
1758
1869
|
const content = parsed.content.trim();
|
|
1759
1870
|
if (content) {
|
|
1760
|
-
const frontmatter =
|
|
1761
|
-
|
|
1762
|
-
targets: ["cursor"],
|
|
1763
|
-
description: "Cursor IDE configuration rules",
|
|
1764
|
-
globs: ["**/*"]
|
|
1765
|
-
};
|
|
1871
|
+
const frontmatter = convertCursorMdcFrontmatter(parsed.data, "cursorrules");
|
|
1872
|
+
frontmatter.targets = ["cursor"];
|
|
1766
1873
|
rules.push({
|
|
1767
1874
|
frontmatter,
|
|
1768
1875
|
content,
|
|
@@ -1789,12 +1896,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1789
1896
|
const content = parsed.content.trim();
|
|
1790
1897
|
if (content) {
|
|
1791
1898
|
const filename = (0, import_node_path18.basename)(file, ".mdc");
|
|
1792
|
-
const frontmatter =
|
|
1793
|
-
root: false,
|
|
1794
|
-
targets: ["cursor"],
|
|
1795
|
-
description: `Cursor rule: ${filename}`,
|
|
1796
|
-
globs: ["**/*"]
|
|
1797
|
-
};
|
|
1899
|
+
const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
|
|
1798
1900
|
rules.push({
|
|
1799
1901
|
frontmatter,
|
|
1800
1902
|
content,
|
|
@@ -2535,7 +2637,7 @@ async function watchCommand() {
|
|
|
2535
2637
|
|
|
2536
2638
|
// src/cli/index.ts
|
|
2537
2639
|
var program = new import_commander.Command();
|
|
2538
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
2640
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.41.0");
|
|
2539
2641
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2540
2642
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2541
2643
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "./chunk-QR656A7R.js";
|
|
17
17
|
import {
|
|
18
18
|
generateRooMcp
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-PPX47BRK.js";
|
|
20
20
|
import "./chunk-6YNGMPAL.js";
|
|
21
21
|
|
|
22
22
|
// src/cli/index.ts
|
|
@@ -427,24 +427,56 @@ async function generateCursorConfig(rules, config, baseDir) {
|
|
|
427
427
|
}
|
|
428
428
|
function generateCursorMarkdown(rule) {
|
|
429
429
|
const lines = [];
|
|
430
|
+
const ruleType = determineCursorRuleType(rule.frontmatter);
|
|
430
431
|
lines.push("---");
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
432
|
+
switch (ruleType) {
|
|
433
|
+
case "always":
|
|
434
|
+
lines.push("description:");
|
|
435
|
+
lines.push("globs:");
|
|
436
|
+
lines.push("alwaysApply: true");
|
|
437
|
+
break;
|
|
438
|
+
case "manual":
|
|
439
|
+
lines.push("description:");
|
|
440
|
+
lines.push("globs:");
|
|
441
|
+
lines.push("alwaysApply: false");
|
|
442
|
+
break;
|
|
443
|
+
case "specificFiles":
|
|
444
|
+
lines.push("description:");
|
|
445
|
+
lines.push(`globs: ${rule.frontmatter.globs.join(",")}`);
|
|
446
|
+
lines.push("alwaysApply: false");
|
|
447
|
+
break;
|
|
448
|
+
case "intelligently":
|
|
449
|
+
lines.push(`description: ${rule.frontmatter.description}`);
|
|
450
|
+
lines.push("globs:");
|
|
451
|
+
lines.push("alwaysApply: false");
|
|
452
|
+
break;
|
|
442
453
|
}
|
|
443
|
-
lines.push(`ruletype: ${ruletype}`);
|
|
444
454
|
lines.push("---");
|
|
455
|
+
lines.push("");
|
|
445
456
|
lines.push(rule.content);
|
|
446
457
|
return lines.join("\n");
|
|
447
458
|
}
|
|
459
|
+
function determineCursorRuleType(frontmatter) {
|
|
460
|
+
if (frontmatter.cursorRuleType) {
|
|
461
|
+
return frontmatter.cursorRuleType;
|
|
462
|
+
}
|
|
463
|
+
const isDescriptionEmpty = !frontmatter.description || frontmatter.description.trim() === "";
|
|
464
|
+
const isGlobsEmpty = frontmatter.globs.length === 0;
|
|
465
|
+
const isGlobsExactlyAllFiles = frontmatter.globs.length === 1 && frontmatter.globs[0] === "**/*";
|
|
466
|
+
if (isGlobsExactlyAllFiles) {
|
|
467
|
+
return "always";
|
|
468
|
+
}
|
|
469
|
+
if (isDescriptionEmpty && isGlobsEmpty) {
|
|
470
|
+
return "manual";
|
|
471
|
+
}
|
|
472
|
+
if (isDescriptionEmpty && !isGlobsEmpty) {
|
|
473
|
+
return "specificFiles";
|
|
474
|
+
}
|
|
475
|
+
if (!isDescriptionEmpty && isGlobsEmpty) {
|
|
476
|
+
return "intelligently";
|
|
477
|
+
}
|
|
478
|
+
return "intelligently";
|
|
479
|
+
}
|
|
448
480
|
function generateCursorIgnore(patterns) {
|
|
449
481
|
const lines = [
|
|
450
482
|
"# Generated by rulesync from .rulesyncignore",
|
|
@@ -707,9 +739,9 @@ function validateFrontmatter(data, filepath) {
|
|
|
707
739
|
`Missing required field "description" in ${filepath}: must be a descriptive string`
|
|
708
740
|
);
|
|
709
741
|
}
|
|
710
|
-
if (
|
|
742
|
+
if (typeof obj.description !== "string") {
|
|
711
743
|
throw new Error(
|
|
712
|
-
`Invalid "description" field in ${filepath}: must be a
|
|
744
|
+
`Invalid "description" field in ${filepath}: must be a string, got ${typeof obj.description}`
|
|
713
745
|
);
|
|
714
746
|
}
|
|
715
747
|
if (obj.globs === void 0) {
|
|
@@ -1065,7 +1097,7 @@ var gitignoreCommand = async () => {
|
|
|
1065
1097
|
}
|
|
1066
1098
|
}
|
|
1067
1099
|
if (linesToAdd.length === 0) {
|
|
1068
|
-
console.log("\u2705 .gitignore
|
|
1100
|
+
console.log("\u2705 .gitignore is already up to date");
|
|
1069
1101
|
return;
|
|
1070
1102
|
}
|
|
1071
1103
|
const newContent = gitignoreContent ? `${gitignoreContent.trimEnd()}
|
|
@@ -1074,7 +1106,7 @@ ${linesToAdd.join("\n")}
|
|
|
1074
1106
|
` : `${linesToAdd.join("\n")}
|
|
1075
1107
|
`;
|
|
1076
1108
|
writeFileSync(gitignorePath, newContent);
|
|
1077
|
-
console.log(`\u2705
|
|
1109
|
+
console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
|
|
1078
1110
|
for (const line of linesToAdd) {
|
|
1079
1111
|
if (!line.startsWith("#")) {
|
|
1080
1112
|
console.log(` ${line}`);
|
|
@@ -1378,7 +1410,7 @@ var customMatterOptions = {
|
|
|
1378
1410
|
yaml: {
|
|
1379
1411
|
parse: (str) => {
|
|
1380
1412
|
try {
|
|
1381
|
-
|
|
1413
|
+
let preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"').replace(/^(\s*globs:\s*)([^\s"'[\n][^"'[\n]*?)(\s*)$/gm, '$1"$2"$3');
|
|
1382
1414
|
return load(preprocessed, { schema: DEFAULT_SCHEMA });
|
|
1383
1415
|
} catch (error) {
|
|
1384
1416
|
try {
|
|
@@ -1391,6 +1423,85 @@ var customMatterOptions = {
|
|
|
1391
1423
|
}
|
|
1392
1424
|
}
|
|
1393
1425
|
};
|
|
1426
|
+
function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
|
|
1427
|
+
const frontmatter = cursorFrontmatter;
|
|
1428
|
+
const description = normalizeValue(frontmatter?.description);
|
|
1429
|
+
const globs = normalizeGlobsValue(frontmatter?.globs);
|
|
1430
|
+
const alwaysApply = frontmatter?.alwaysApply === true || frontmatter?.alwaysApply === "true";
|
|
1431
|
+
if (alwaysApply) {
|
|
1432
|
+
return {
|
|
1433
|
+
root: false,
|
|
1434
|
+
targets: ["*"],
|
|
1435
|
+
description: description || "",
|
|
1436
|
+
globs: ["**/*"],
|
|
1437
|
+
cursorRuleType: "always"
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
if (isEmpty(description) && isEmpty(globs)) {
|
|
1441
|
+
return {
|
|
1442
|
+
root: false,
|
|
1443
|
+
targets: ["*"],
|
|
1444
|
+
description: "",
|
|
1445
|
+
globs: [],
|
|
1446
|
+
cursorRuleType: "manual"
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
if (!isEmpty(globs)) {
|
|
1450
|
+
return {
|
|
1451
|
+
root: false,
|
|
1452
|
+
targets: ["*"],
|
|
1453
|
+
description: "",
|
|
1454
|
+
globs: convertGlobsToArray(globs),
|
|
1455
|
+
cursorRuleType: "specificFiles"
|
|
1456
|
+
};
|
|
1457
|
+
}
|
|
1458
|
+
if (!isEmpty(description)) {
|
|
1459
|
+
return {
|
|
1460
|
+
root: false,
|
|
1461
|
+
targets: ["*"],
|
|
1462
|
+
description,
|
|
1463
|
+
globs: [],
|
|
1464
|
+
cursorRuleType: "intelligently"
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1467
|
+
return {
|
|
1468
|
+
root: false,
|
|
1469
|
+
targets: ["*"],
|
|
1470
|
+
description: "",
|
|
1471
|
+
globs: [],
|
|
1472
|
+
cursorRuleType: "manual"
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
function normalizeValue(value) {
|
|
1476
|
+
if (value === void 0 || value === null || value === "") {
|
|
1477
|
+
return void 0;
|
|
1478
|
+
}
|
|
1479
|
+
return String(value);
|
|
1480
|
+
}
|
|
1481
|
+
function normalizeGlobsValue(value) {
|
|
1482
|
+
if (value === void 0 || value === null || value === "") {
|
|
1483
|
+
return void 0;
|
|
1484
|
+
}
|
|
1485
|
+
if (Array.isArray(value)) {
|
|
1486
|
+
return value.length === 0 ? void 0 : value;
|
|
1487
|
+
}
|
|
1488
|
+
return String(value);
|
|
1489
|
+
}
|
|
1490
|
+
function isEmpty(value) {
|
|
1491
|
+
return value === void 0 || value === null || value === "";
|
|
1492
|
+
}
|
|
1493
|
+
function convertGlobsToArray(globs) {
|
|
1494
|
+
if (!globs) {
|
|
1495
|
+
return [];
|
|
1496
|
+
}
|
|
1497
|
+
if (Array.isArray(globs)) {
|
|
1498
|
+
return globs;
|
|
1499
|
+
}
|
|
1500
|
+
if (typeof globs === "string") {
|
|
1501
|
+
return globs.split(",").map((g) => g.trim()).filter((g) => g.length > 0);
|
|
1502
|
+
}
|
|
1503
|
+
return [];
|
|
1504
|
+
}
|
|
1394
1505
|
async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
1395
1506
|
const errors = [];
|
|
1396
1507
|
const rules = [];
|
|
@@ -1403,12 +1514,8 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1403
1514
|
const parsed = matter3(rawContent, customMatterOptions);
|
|
1404
1515
|
const content = parsed.content.trim();
|
|
1405
1516
|
if (content) {
|
|
1406
|
-
const frontmatter =
|
|
1407
|
-
|
|
1408
|
-
targets: ["cursor"],
|
|
1409
|
-
description: "Cursor IDE configuration rules",
|
|
1410
|
-
globs: ["**/*"]
|
|
1411
|
-
};
|
|
1517
|
+
const frontmatter = convertCursorMdcFrontmatter(parsed.data, "cursorrules");
|
|
1518
|
+
frontmatter.targets = ["cursor"];
|
|
1412
1519
|
rules.push({
|
|
1413
1520
|
frontmatter,
|
|
1414
1521
|
content,
|
|
@@ -1435,12 +1542,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
|
1435
1542
|
const content = parsed.content.trim();
|
|
1436
1543
|
if (content) {
|
|
1437
1544
|
const filename = basename4(file, ".mdc");
|
|
1438
|
-
const frontmatter =
|
|
1439
|
-
root: false,
|
|
1440
|
-
targets: ["cursor"],
|
|
1441
|
-
description: `Cursor rule: ${filename}`,
|
|
1442
|
-
globs: ["**/*"]
|
|
1443
|
-
};
|
|
1545
|
+
const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
|
|
1444
1546
|
rules.push({
|
|
1445
1547
|
frontmatter,
|
|
1446
1548
|
content,
|
|
@@ -2181,7 +2283,7 @@ async function watchCommand() {
|
|
|
2181
2283
|
|
|
2182
2284
|
// src/cli/index.ts
|
|
2183
2285
|
var program = new Command();
|
|
2184
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
2286
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.41.0");
|
|
2185
2287
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
2186
2288
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
2187
2289
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|