rulesync 0.28.0 → 0.31.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 +31 -19
- package/README.md +31 -19
- package/dist/index.js +166 -74
- package/dist/index.mjs +168 -76
- package/package.json +3 -1
package/README.ja.md
CHANGED
|
@@ -3,17 +3,19 @@
|
|
|
3
3
|
[](https://github.com/dyoshikawa/rulesync/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/rulesync)
|
|
5
5
|
|
|
6
|
-
統一されたAIルールファイル(`.rulesync/*.md`)から、様々なAI開発ツール用の設定ファイルを自動生成するNode.js CLI
|
|
6
|
+
統一されたAIルールファイル(`.rulesync/*.md`)から、様々なAI開発ツール用の設定ファイルを自動生成するNode.js CLIツールです。既存のAIツール設定を統一形式にインポートすることも可能です。
|
|
7
7
|
|
|
8
8
|
[English](./README.md) | **日本語**
|
|
9
9
|
|
|
10
10
|
## 対応ツール
|
|
11
11
|
|
|
12
|
+
rulesyncは以下のAI開発ツールの**生成**と**インポート**の両方をサポートしています:
|
|
13
|
+
|
|
12
14
|
- **GitHub Copilot Custom Instructions** (`.github/copilot-instructions.md` + `.github/instructions/*.instructions.md`)
|
|
13
|
-
- **Cursor Project Rules** (`.cursor/rules/*.mdc`)
|
|
14
|
-
- **Cline Rules** (`.clinerules/*.md`)
|
|
15
|
+
- **Cursor Project Rules** (`.cursor/rules/*.mdc` + `.cursorrules`)
|
|
16
|
+
- **Cline Rules** (`.clinerules/*.md` + `.cline/instructions.md`)
|
|
15
17
|
- **Claude Code Memory** (`./CLAUDE.md` + `.claude/memories/*.md`)
|
|
16
|
-
- **Roo Code Rules** (`.roo/rules/*.md`)
|
|
18
|
+
- **Roo Code Rules** (`.roo/rules/*.md` + `.roo/instructions.md`)
|
|
17
19
|
|
|
18
20
|
## インストール
|
|
19
21
|
|
|
@@ -53,11 +55,19 @@ yarn global add rulesync
|
|
|
53
55
|
|
|
54
56
|
### AIツール設定を持つ既存プロジェクト
|
|
55
57
|
|
|
56
|
-
既にAI
|
|
58
|
+
既にAIツールの設定がある場合、それらをrulesync形式にインポートできます:
|
|
57
59
|
|
|
58
60
|
1. **既存設定をインポート:**
|
|
59
61
|
```bash
|
|
60
|
-
|
|
62
|
+
# 複数のツールから一度にインポート
|
|
63
|
+
npx rulesync import --claudecode --cursor --copilot
|
|
64
|
+
|
|
65
|
+
# または特定のツールからインポート
|
|
66
|
+
npx rulesync import --claudecode # CLAUDE.mdと.claude/memories/*.mdから
|
|
67
|
+
npx rulesync import --cursor # .cursorrulesと.cursor/rules/*.mdcから
|
|
68
|
+
npx rulesync import --copilot # .github/copilot-instructions.mdから
|
|
69
|
+
npx rulesync import --cline # .cline/instructions.mdから
|
|
70
|
+
npx rulesync import --roo # .roo/instructions.mdから
|
|
61
71
|
```
|
|
62
72
|
|
|
63
73
|
2. **`.rulesync/`ディレクトリのインポートされたルールを確認・編集**
|
|
@@ -172,7 +182,7 @@ npx rulesync generate
|
|
|
172
182
|
npx rulesync generate --copilot
|
|
173
183
|
npx rulesync generate --cursor
|
|
174
184
|
npx rulesync generate --cline
|
|
175
|
-
npx rulesync generate --
|
|
185
|
+
npx rulesync generate --claudecode
|
|
176
186
|
npx rulesync generate --roo
|
|
177
187
|
|
|
178
188
|
# クリーンビルド(既存ファイルを最初に削除)
|
|
@@ -195,7 +205,7 @@ npx rulesync generate --base-dir ./apps/web,./apps/api,./packages/shared
|
|
|
195
205
|
|
|
196
206
|
- `--delete`: 新しいファイルを作成する前に既存の生成済みファイルをすべて削除
|
|
197
207
|
- `--verbose`: 生成プロセス中に詳細出力を表示
|
|
198
|
-
- `--copilot`, `--cursor`, `--cline`, `--
|
|
208
|
+
- `--copilot`, `--cursor`, `--cline`, `--claudecode`, `--roo`: 指定されたツールのみ生成
|
|
199
209
|
- `--base-dir <paths>`: 指定されたベースディレクトリに設定ファイルを生成(複数パスの場合はカンマ区切り)。異なるプロジェクトディレクトリにツール固有の設定を生成したいmonorepoセットアップに便利。
|
|
200
210
|
|
|
201
211
|
### 4. 既存設定のインポート
|
|
@@ -204,25 +214,27 @@ npx rulesync generate --base-dir ./apps/web,./apps/api,./packages/shared
|
|
|
204
214
|
|
|
205
215
|
```bash
|
|
206
216
|
# 既存のAIツール設定からインポート
|
|
207
|
-
npx rulesync import --
|
|
208
|
-
npx rulesync import --cursor
|
|
209
|
-
npx rulesync import --copilot
|
|
210
|
-
npx rulesync import --cline
|
|
211
|
-
npx rulesync import --roo
|
|
217
|
+
npx rulesync import --claudecode # CLAUDE.mdと.claude/memories/*.mdからインポート
|
|
218
|
+
npx rulesync import --cursor # .cursorrulesと.cursor/rules/*.mdからインポート
|
|
219
|
+
npx rulesync import --copilot # .github/copilot-instructions.mdと.github/instructions/*.instructions.mdからインポート
|
|
220
|
+
npx rulesync import --cline # .cline/instructions.mdからインポート
|
|
221
|
+
npx rulesync import --roo # .roo/instructions.mdからインポート
|
|
212
222
|
|
|
213
223
|
# 複数のツールからインポート
|
|
214
|
-
npx rulesync import --
|
|
224
|
+
npx rulesync import --claudecode --cursor --copilot
|
|
215
225
|
|
|
216
226
|
# インポート時の詳細出力
|
|
217
|
-
npx rulesync import --
|
|
227
|
+
npx rulesync import --claudecode --verbose
|
|
218
228
|
```
|
|
219
229
|
|
|
220
230
|
importコマンドの動作:
|
|
221
|
-
-
|
|
222
|
-
-
|
|
223
|
-
-
|
|
224
|
-
- ファイル名の競合を避けるためツール固有のプレフィックスを使用(例:`
|
|
231
|
+
- カスタムパーサーを使用して各AIツールの既存設定ファイルをパース
|
|
232
|
+
- 適切なフロントマターメタデータを付けてrulesync形式に変換
|
|
233
|
+
- インポートしたコンテンツと適切なルール分類で新しい`.rulesync/*.md`ファイルを作成
|
|
234
|
+
- ファイル名の競合を避けるためツール固有のプレフィックスを使用(例:`claudecode-overview.md`、`cursor-custom-rules.md`)
|
|
225
235
|
- 競合が発生した場合はユニークなファイル名を生成
|
|
236
|
+
- YAMLフロントマター付きのCursorのMDCファイルなど複雑なフォーマットをサポート
|
|
237
|
+
- 複数ファイルのインポート(例:`.claude/memories/`ディレクトリのすべてのファイル)に対応
|
|
226
238
|
|
|
227
239
|
### 5. その他のコマンド
|
|
228
240
|
|
package/README.md
CHANGED
|
@@ -3,17 +3,19 @@
|
|
|
3
3
|
[](https://github.com/dyoshikawa/rulesync/actions/workflows/ci.yml)
|
|
4
4
|
[](https://www.npmjs.com/package/rulesync)
|
|
5
5
|
|
|
6
|
-
A Node.js CLI tool that automatically generates configuration files for various AI development tools from unified AI rule files (`.rulesync/*.md`).
|
|
6
|
+
A Node.js CLI tool that automatically generates configuration files for various AI development tools from unified AI rule files (`.rulesync/*.md`). Also imports existing AI tool configurations into the unified format.
|
|
7
7
|
|
|
8
8
|
**English** | [日本語](./README.ja.md)
|
|
9
9
|
|
|
10
10
|
## Supported Tools
|
|
11
11
|
|
|
12
|
+
rulesync supports both **generation** and **import** for the following AI development tools:
|
|
13
|
+
|
|
12
14
|
- **GitHub Copilot Custom Instructions** (`.github/copilot-instructions.md` + `.github/instructions/*.instructions.md`)
|
|
13
|
-
- **Cursor Project Rules** (`.cursor/rules/*.mdc`)
|
|
14
|
-
- **Cline Rules** (`.clinerules/*.md`)
|
|
15
|
+
- **Cursor Project Rules** (`.cursor/rules/*.mdc` + `.cursorrules`)
|
|
16
|
+
- **Cline Rules** (`.clinerules/*.md` + `.cline/instructions.md`)
|
|
15
17
|
- **Claude Code Memory** (`./CLAUDE.md` + `.claude/memories/*.md`)
|
|
16
|
-
- **Roo Code Rules** (`.roo/rules/*.md`)
|
|
18
|
+
- **Roo Code Rules** (`.roo/rules/*.md` + `.roo/instructions.md`)
|
|
17
19
|
|
|
18
20
|
## Installation
|
|
19
21
|
|
|
@@ -53,11 +55,19 @@ yarn global add rulesync
|
|
|
53
55
|
|
|
54
56
|
### Existing Project with AI Tool Configurations
|
|
55
57
|
|
|
56
|
-
If you already have AI tool configurations, you can import them:
|
|
58
|
+
If you already have AI tool configurations, you can import them into rulesync format:
|
|
57
59
|
|
|
58
60
|
1. **Import existing configurations:**
|
|
59
61
|
```bash
|
|
60
|
-
|
|
62
|
+
# Import from multiple tools at once
|
|
63
|
+
npx rulesync import --claudecode --cursor --copilot
|
|
64
|
+
|
|
65
|
+
# Or import from specific tools
|
|
66
|
+
npx rulesync import --claudecode # From CLAUDE.md and .claude/memories/*.md
|
|
67
|
+
npx rulesync import --cursor # From .cursorrules and .cursor/rules/*.mdc
|
|
68
|
+
npx rulesync import --copilot # From .github/copilot-instructions.md
|
|
69
|
+
npx rulesync import --cline # From .cline/instructions.md
|
|
70
|
+
npx rulesync import --roo # From .roo/instructions.md
|
|
61
71
|
```
|
|
62
72
|
|
|
63
73
|
2. **Review and edit** the imported rules in `.rulesync/` directory
|
|
@@ -172,7 +182,7 @@ npx rulesync generate
|
|
|
172
182
|
npx rulesync generate --copilot
|
|
173
183
|
npx rulesync generate --cursor
|
|
174
184
|
npx rulesync generate --cline
|
|
175
|
-
npx rulesync generate --
|
|
185
|
+
npx rulesync generate --claudecode
|
|
176
186
|
npx rulesync generate --roo
|
|
177
187
|
|
|
178
188
|
# Clean build (delete existing files first)
|
|
@@ -195,7 +205,7 @@ npx rulesync generate --base-dir ./apps/web,./apps/api,./packages/shared
|
|
|
195
205
|
|
|
196
206
|
- `--delete`: Remove all existing generated files before creating new ones
|
|
197
207
|
- `--verbose`: Show detailed output during generation process
|
|
198
|
-
- `--copilot`, `--cursor`, `--cline`, `--
|
|
208
|
+
- `--copilot`, `--cursor`, `--cline`, `--claudecode`, `--roo`: Generate only for specified tools
|
|
199
209
|
- `--base-dir <paths>`: Generate configuration files in specified base directories (comma-separated for multiple paths). Useful for monorepo setups where you want to generate tool-specific configurations in different project directories.
|
|
200
210
|
|
|
201
211
|
### 4. Import Existing Configurations
|
|
@@ -204,25 +214,27 @@ If you already have AI tool configurations in your project, you can import them
|
|
|
204
214
|
|
|
205
215
|
```bash
|
|
206
216
|
# Import from existing AI tool configurations
|
|
207
|
-
npx rulesync import --
|
|
208
|
-
npx rulesync import --cursor
|
|
209
|
-
npx rulesync import --copilot
|
|
210
|
-
npx rulesync import --cline
|
|
211
|
-
npx rulesync import --roo
|
|
217
|
+
npx rulesync import --claudecode # Import from CLAUDE.md and .claude/memories/*.md
|
|
218
|
+
npx rulesync import --cursor # Import from .cursorrules and .cursor/rules/*.md
|
|
219
|
+
npx rulesync import --copilot # Import from .github/copilot-instructions.md and .github/instructions/*.instructions.md
|
|
220
|
+
npx rulesync import --cline # Import from .cline/instructions.md
|
|
221
|
+
npx rulesync import --roo # Import from .roo/instructions.md
|
|
212
222
|
|
|
213
223
|
# Import from multiple tools
|
|
214
|
-
npx rulesync import --
|
|
224
|
+
npx rulesync import --claudecode --cursor --copilot
|
|
215
225
|
|
|
216
226
|
# Verbose output during import
|
|
217
|
-
npx rulesync import --
|
|
227
|
+
npx rulesync import --claudecode --verbose
|
|
218
228
|
```
|
|
219
229
|
|
|
220
230
|
The import command will:
|
|
221
|
-
- Parse existing configuration files from each AI tool
|
|
222
|
-
- Convert them to rulesync format with appropriate frontmatter
|
|
223
|
-
- Create new `.rulesync/*.md` files with imported content
|
|
224
|
-
- Use tool-specific prefixes to avoid filename conflicts (e.g., `
|
|
231
|
+
- Parse existing configuration files from each AI tool using custom parsers
|
|
232
|
+
- Convert them to rulesync format with appropriate frontmatter metadata
|
|
233
|
+
- Create new `.rulesync/*.md` files with imported content and proper rule categorization
|
|
234
|
+
- Use tool-specific prefixes to avoid filename conflicts (e.g., `claudecode-overview.md`, `cursor-custom-rules.md`)
|
|
225
235
|
- Generate unique filenames if conflicts occur
|
|
236
|
+
- Support complex formats like Cursor's MDC files with YAML frontmatter
|
|
237
|
+
- Handle multiple file imports (e.g., all files from `.claude/memories/` directory)
|
|
226
238
|
|
|
227
239
|
### 5. Other Commands
|
|
228
240
|
|
package/dist/index.js
CHANGED
|
@@ -38,11 +38,11 @@ function getDefaultConfig() {
|
|
|
38
38
|
copilot: ".github/instructions",
|
|
39
39
|
cursor: ".cursor/rules",
|
|
40
40
|
cline: ".clinerules",
|
|
41
|
-
|
|
41
|
+
claudecode: ".",
|
|
42
42
|
roo: ".roo/rules"
|
|
43
43
|
},
|
|
44
44
|
watchEnabled: false,
|
|
45
|
-
defaultTargets: ["copilot", "cursor", "cline", "
|
|
45
|
+
defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo"]
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
function resolveTargets(targets, config) {
|
|
@@ -88,23 +88,23 @@ async function addCommand(filename) {
|
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
// src/generators/
|
|
91
|
+
// src/generators/claudecode.ts
|
|
92
92
|
var import_node_path2 = require("path");
|
|
93
|
-
async function
|
|
93
|
+
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
94
94
|
const outputs = [];
|
|
95
95
|
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
96
96
|
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
97
97
|
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
98
|
-
const claudeOutputDir = baseDir ? (0, import_node_path2.join)(baseDir, config.outputPaths.
|
|
98
|
+
const claudeOutputDir = baseDir ? (0, import_node_path2.join)(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
|
|
99
99
|
outputs.push({
|
|
100
|
-
tool: "
|
|
100
|
+
tool: "claudecode",
|
|
101
101
|
filepath: (0, import_node_path2.join)(claudeOutputDir, "CLAUDE.md"),
|
|
102
102
|
content: claudeMdContent
|
|
103
103
|
});
|
|
104
104
|
for (const rule of detailRules) {
|
|
105
105
|
const memoryContent = generateMemoryFile(rule);
|
|
106
106
|
outputs.push({
|
|
107
|
-
tool: "
|
|
107
|
+
tool: "claudecode",
|
|
108
108
|
filepath: (0, import_node_path2.join)(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
|
|
109
109
|
content: memoryContent
|
|
110
110
|
});
|
|
@@ -350,8 +350,8 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
350
350
|
return generateCursorConfig(rules, config, baseDir);
|
|
351
351
|
case "cline":
|
|
352
352
|
return generateClineConfig(rules, config, baseDir);
|
|
353
|
-
case "
|
|
354
|
-
return await
|
|
353
|
+
case "claudecode":
|
|
354
|
+
return await generateClaudecodeConfig(rules, config, baseDir);
|
|
355
355
|
case "roo":
|
|
356
356
|
return generateRooConfig(rules, config, baseDir);
|
|
357
357
|
default:
|
|
@@ -561,7 +561,7 @@ async function generateCommand(options = {}) {
|
|
|
561
561
|
case "cline":
|
|
562
562
|
deleteTasks.push(removeDirectory(config.outputPaths.cline));
|
|
563
563
|
break;
|
|
564
|
-
case "
|
|
564
|
+
case "claudecode":
|
|
565
565
|
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
566
566
|
break;
|
|
567
567
|
case "roo":
|
|
@@ -612,14 +612,6 @@ var gitignoreCommand = async () => {
|
|
|
612
612
|
const gitignorePath = (0, import_node_path9.join)(process.cwd(), ".gitignore");
|
|
613
613
|
const rulesFilesToIgnore = [
|
|
614
614
|
"# Generated by rulesync - AI tool configuration files",
|
|
615
|
-
".github/copilot-instructions.md",
|
|
616
|
-
".github/instructions/",
|
|
617
|
-
".cursor/rules/",
|
|
618
|
-
".clinerules/",
|
|
619
|
-
"CLAUDE.md",
|
|
620
|
-
".claude/memories/",
|
|
621
|
-
".roo/rules/",
|
|
622
|
-
"# Support for --base-dir option (any depth)",
|
|
623
615
|
"**/.github/copilot-instructions.md",
|
|
624
616
|
"**/.github/instructions/",
|
|
625
617
|
"**/.cursor/rules/",
|
|
@@ -658,9 +650,9 @@ ${linesToAdd.join("\n")}
|
|
|
658
650
|
|
|
659
651
|
// src/core/importer.ts
|
|
660
652
|
var import_node_path15 = require("path");
|
|
661
|
-
var
|
|
653
|
+
var import_gray_matter4 = __toESM(require("gray-matter"));
|
|
662
654
|
|
|
663
|
-
// src/parsers/
|
|
655
|
+
// src/parsers/claudecode.ts
|
|
664
656
|
var import_node_path10 = require("path");
|
|
665
657
|
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
666
658
|
const errors = [];
|
|
@@ -704,7 +696,7 @@ function parseClaudeMainFile(content, filepath) {
|
|
|
704
696
|
}
|
|
705
697
|
const frontmatter = {
|
|
706
698
|
root: false,
|
|
707
|
-
targets: ["
|
|
699
|
+
targets: ["claudecode"],
|
|
708
700
|
description: "Main Claude Code configuration",
|
|
709
701
|
globs: ["**/*"]
|
|
710
702
|
};
|
|
@@ -728,7 +720,7 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
728
720
|
const filename = (0, import_node_path10.basename)(file, ".md");
|
|
729
721
|
const frontmatter = {
|
|
730
722
|
root: false,
|
|
731
|
-
targets: ["
|
|
723
|
+
targets: ["claudecode"],
|
|
732
724
|
description: `Memory file: ${filename}`,
|
|
733
725
|
globs: ["**/*"]
|
|
734
726
|
};
|
|
@@ -781,66 +773,166 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
781
773
|
|
|
782
774
|
// src/parsers/copilot.ts
|
|
783
775
|
var import_node_path12 = require("path");
|
|
776
|
+
var import_gray_matter2 = __toESM(require("gray-matter"));
|
|
784
777
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
785
778
|
const errors = [];
|
|
786
779
|
const rules = [];
|
|
787
780
|
const copilotFilePath = (0, import_node_path12.join)(baseDir, ".github", "copilot-instructions.md");
|
|
788
|
-
if (
|
|
789
|
-
|
|
790
|
-
|
|
781
|
+
if (await fileExists(copilotFilePath)) {
|
|
782
|
+
try {
|
|
783
|
+
const rawContent = await readFileContent(copilotFilePath);
|
|
784
|
+
const parsed = (0, import_gray_matter2.default)(rawContent);
|
|
785
|
+
const content = parsed.content.trim();
|
|
786
|
+
if (content) {
|
|
787
|
+
const frontmatter = {
|
|
788
|
+
root: false,
|
|
789
|
+
targets: ["copilot"],
|
|
790
|
+
description: "GitHub Copilot instructions",
|
|
791
|
+
globs: ["**/*"]
|
|
792
|
+
};
|
|
793
|
+
rules.push({
|
|
794
|
+
frontmatter,
|
|
795
|
+
content,
|
|
796
|
+
filename: "copilot-instructions",
|
|
797
|
+
filepath: copilotFilePath
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
} catch (error) {
|
|
801
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
802
|
+
errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
|
|
803
|
+
}
|
|
791
804
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
const
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
805
|
+
const instructionsDir = (0, import_node_path12.join)(baseDir, ".github", "instructions");
|
|
806
|
+
if (await fileExists(instructionsDir)) {
|
|
807
|
+
try {
|
|
808
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
809
|
+
const files = await readdir2(instructionsDir);
|
|
810
|
+
for (const file of files) {
|
|
811
|
+
if (file.endsWith(".instructions.md")) {
|
|
812
|
+
const filePath = (0, import_node_path12.join)(instructionsDir, file);
|
|
813
|
+
const rawContent = await readFileContent(filePath);
|
|
814
|
+
const parsed = (0, import_gray_matter2.default)(rawContent);
|
|
815
|
+
const content = parsed.content.trim();
|
|
816
|
+
if (content) {
|
|
817
|
+
const filename = (0, import_node_path12.basename)(file, ".instructions.md");
|
|
818
|
+
const frontmatter = {
|
|
819
|
+
root: false,
|
|
820
|
+
targets: ["copilot"],
|
|
821
|
+
description: `Copilot instruction: ${filename}`,
|
|
822
|
+
globs: ["**/*"]
|
|
823
|
+
};
|
|
824
|
+
rules.push({
|
|
825
|
+
frontmatter,
|
|
826
|
+
content,
|
|
827
|
+
filename: `copilot-${filename}`,
|
|
828
|
+
filepath: filePath
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
} catch (error) {
|
|
834
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
835
|
+
errors.push(`Failed to parse .github/instructions files: ${errorMessage}`);
|
|
807
836
|
}
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
errors.push(
|
|
837
|
+
}
|
|
838
|
+
if (rules.length === 0) {
|
|
839
|
+
errors.push(
|
|
840
|
+
"No Copilot configuration files found (.github/copilot-instructions.md or .github/instructions/*.instructions.md)"
|
|
841
|
+
);
|
|
811
842
|
}
|
|
812
843
|
return { rules, errors };
|
|
813
844
|
}
|
|
814
845
|
|
|
815
846
|
// src/parsers/cursor.ts
|
|
816
847
|
var import_node_path13 = require("path");
|
|
848
|
+
var import_gray_matter3 = __toESM(require("gray-matter"));
|
|
849
|
+
var import_js_yaml = __toESM(require("js-yaml"));
|
|
850
|
+
var customMatterOptions = {
|
|
851
|
+
engines: {
|
|
852
|
+
yaml: {
|
|
853
|
+
parse: (str) => {
|
|
854
|
+
try {
|
|
855
|
+
const preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"');
|
|
856
|
+
return import_js_yaml.default.load(preprocessed, { schema: import_js_yaml.default.DEFAULT_SCHEMA });
|
|
857
|
+
} catch (error) {
|
|
858
|
+
try {
|
|
859
|
+
return import_js_yaml.default.load(str, { schema: import_js_yaml.default.FAILSAFE_SCHEMA });
|
|
860
|
+
} catch {
|
|
861
|
+
throw error;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
};
|
|
817
868
|
async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
818
869
|
const errors = [];
|
|
819
870
|
const rules = [];
|
|
820
871
|
const cursorFilePath = (0, import_node_path13.join)(baseDir, ".cursorrules");
|
|
821
|
-
if (
|
|
822
|
-
|
|
823
|
-
|
|
872
|
+
if (await fileExists(cursorFilePath)) {
|
|
873
|
+
try {
|
|
874
|
+
const rawContent = await readFileContent(cursorFilePath);
|
|
875
|
+
const parsed = (0, import_gray_matter3.default)(rawContent, customMatterOptions);
|
|
876
|
+
const content = parsed.content.trim();
|
|
877
|
+
if (content) {
|
|
878
|
+
const frontmatter = {
|
|
879
|
+
root: false,
|
|
880
|
+
targets: ["cursor"],
|
|
881
|
+
description: "Cursor IDE configuration rules",
|
|
882
|
+
globs: ["**/*"]
|
|
883
|
+
};
|
|
884
|
+
rules.push({
|
|
885
|
+
frontmatter,
|
|
886
|
+
content,
|
|
887
|
+
filename: "cursor-rules",
|
|
888
|
+
filepath: cursorFilePath
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
} catch (error) {
|
|
892
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
893
|
+
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
894
|
+
}
|
|
824
895
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
896
|
+
const cursorRulesDir = (0, import_node_path13.join)(baseDir, ".cursor", "rules");
|
|
897
|
+
if (await fileExists(cursorRulesDir)) {
|
|
898
|
+
try {
|
|
899
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
900
|
+
const files = await readdir2(cursorRulesDir);
|
|
901
|
+
for (const file of files) {
|
|
902
|
+
if (file.endsWith(".mdc")) {
|
|
903
|
+
const filePath = (0, import_node_path13.join)(cursorRulesDir, file);
|
|
904
|
+
try {
|
|
905
|
+
const rawContent = await readFileContent(filePath);
|
|
906
|
+
const parsed = (0, import_gray_matter3.default)(rawContent, customMatterOptions);
|
|
907
|
+
const content = parsed.content.trim();
|
|
908
|
+
if (content) {
|
|
909
|
+
const filename = (0, import_node_path13.basename)(file, ".mdc");
|
|
910
|
+
const frontmatter = {
|
|
911
|
+
root: false,
|
|
912
|
+
targets: ["cursor"],
|
|
913
|
+
description: `Cursor rule: ${filename}`,
|
|
914
|
+
globs: ["**/*"]
|
|
915
|
+
};
|
|
916
|
+
rules.push({
|
|
917
|
+
frontmatter,
|
|
918
|
+
content,
|
|
919
|
+
filename: `cursor-${filename}`,
|
|
920
|
+
filepath: filePath
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
} catch (error) {
|
|
924
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
925
|
+
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
} catch (error) {
|
|
930
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
931
|
+
errors.push(`Failed to parse .cursor/rules files: ${errorMessage}`);
|
|
840
932
|
}
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
errors.push(
|
|
933
|
+
}
|
|
934
|
+
if (rules.length === 0) {
|
|
935
|
+
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
844
936
|
}
|
|
845
937
|
return { rules, errors };
|
|
846
938
|
}
|
|
@@ -888,7 +980,7 @@ async function importConfiguration(options) {
|
|
|
888
980
|
}
|
|
889
981
|
try {
|
|
890
982
|
switch (tool) {
|
|
891
|
-
case "
|
|
983
|
+
case "claudecode": {
|
|
892
984
|
const claudeResult = await parseClaudeConfiguration(baseDir);
|
|
893
985
|
rules = claudeResult.rules;
|
|
894
986
|
errors.push(...claudeResult.errors);
|
|
@@ -963,7 +1055,7 @@ async function importConfiguration(options) {
|
|
|
963
1055
|
};
|
|
964
1056
|
}
|
|
965
1057
|
function generateRuleFileContent(rule) {
|
|
966
|
-
const frontmatter =
|
|
1058
|
+
const frontmatter = import_gray_matter4.default.stringify("", rule.frontmatter);
|
|
967
1059
|
return frontmatter + rule.content;
|
|
968
1060
|
}
|
|
969
1061
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
@@ -979,14 +1071,14 @@ async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
|
979
1071
|
// src/cli/commands/import.ts
|
|
980
1072
|
async function importCommand(options = {}) {
|
|
981
1073
|
const tools = [];
|
|
982
|
-
if (options.
|
|
1074
|
+
if (options.claudecode) tools.push("claudecode");
|
|
983
1075
|
if (options.cursor) tools.push("cursor");
|
|
984
1076
|
if (options.copilot) tools.push("copilot");
|
|
985
1077
|
if (options.cline) tools.push("cline");
|
|
986
1078
|
if (options.roo) tools.push("roo");
|
|
987
1079
|
if (tools.length === 0) {
|
|
988
1080
|
console.error(
|
|
989
|
-
"\u274C Please specify at least one tool to import from (--
|
|
1081
|
+
"\u274C Please specify at least one tool to import from (--claudecode, --cursor, --copilot, --cline, --roo)"
|
|
990
1082
|
);
|
|
991
1083
|
process.exit(1);
|
|
992
1084
|
}
|
|
@@ -1197,7 +1289,7 @@ async function statusCommand() {
|
|
|
1197
1289
|
const nonRootRules = rules.length - rootRules;
|
|
1198
1290
|
console.log(` - Root rules: ${rootRules}`);
|
|
1199
1291
|
console.log(` - Non-root rules: ${nonRootRules}`);
|
|
1200
|
-
const targetCounts = { copilot: 0, cursor: 0, cline: 0,
|
|
1292
|
+
const targetCounts = { copilot: 0, cursor: 0, cline: 0, claudecode: 0, roo: 0 };
|
|
1201
1293
|
for (const rule of rules) {
|
|
1202
1294
|
const targets = rule.frontmatter.targets[0] === "*" ? config.defaultTargets : rule.frontmatter.targets;
|
|
1203
1295
|
for (const target of targets) {
|
|
@@ -1210,7 +1302,7 @@ async function statusCommand() {
|
|
|
1210
1302
|
console.log(` - Copilot: ${targetCounts.copilot} rules`);
|
|
1211
1303
|
console.log(` - Cursor: ${targetCounts.cursor} rules`);
|
|
1212
1304
|
console.log(` - Cline: ${targetCounts.cline} rules`);
|
|
1213
|
-
console.log(` - Claude: ${targetCounts.
|
|
1305
|
+
console.log(` - Claude Code: ${targetCounts.claudecode} rules`);
|
|
1214
1306
|
console.log(` - Roo: ${targetCounts.roo} rules`);
|
|
1215
1307
|
}
|
|
1216
1308
|
console.log("\n\u{1F4E4} Generated files:");
|
|
@@ -1309,12 +1401,12 @@ async function watchCommand() {
|
|
|
1309
1401
|
|
|
1310
1402
|
// src/cli/index.ts
|
|
1311
1403
|
var program = new import_commander.Command();
|
|
1312
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
1404
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.31.0");
|
|
1313
1405
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
1314
1406
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
1315
1407
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
1316
|
-
program.command("import").description("Import configurations from AI tools to rulesync format").option("--
|
|
1317
|
-
program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--
|
|
1408
|
+
program.command("import").description("Import configurations from AI tools to rulesync format").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("-v, --verbose", "Verbose output").action(importCommand);
|
|
1409
|
+
program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--delete", "Delete all existing files in output directories before generating").option(
|
|
1318
1410
|
"-b, --base-dir <paths>",
|
|
1319
1411
|
"Base directories to generate files (comma-separated for multiple paths)"
|
|
1320
1412
|
).option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
@@ -1322,7 +1414,7 @@ program.command("generate").description("Generate configuration files for AI too
|
|
|
1322
1414
|
if (options.copilot) tools.push("copilot");
|
|
1323
1415
|
if (options.cursor) tools.push("cursor");
|
|
1324
1416
|
if (options.cline) tools.push("cline");
|
|
1325
|
-
if (options.
|
|
1417
|
+
if (options.claudecode) tools.push("claudecode");
|
|
1326
1418
|
if (options.roo) tools.push("roo");
|
|
1327
1419
|
const generateOptions = {
|
|
1328
1420
|
verbose: options.verbose,
|
package/dist/index.mjs
CHANGED
|
@@ -15,11 +15,11 @@ function getDefaultConfig() {
|
|
|
15
15
|
copilot: ".github/instructions",
|
|
16
16
|
cursor: ".cursor/rules",
|
|
17
17
|
cline: ".clinerules",
|
|
18
|
-
|
|
18
|
+
claudecode: ".",
|
|
19
19
|
roo: ".roo/rules"
|
|
20
20
|
},
|
|
21
21
|
watchEnabled: false,
|
|
22
|
-
defaultTargets: ["copilot", "cursor", "cline", "
|
|
22
|
+
defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo"]
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
function resolveTargets(targets, config) {
|
|
@@ -65,23 +65,23 @@ async function addCommand(filename) {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
// src/generators/
|
|
68
|
+
// src/generators/claudecode.ts
|
|
69
69
|
import { join } from "path";
|
|
70
|
-
async function
|
|
70
|
+
async function generateClaudecodeConfig(rules, config, baseDir) {
|
|
71
71
|
const outputs = [];
|
|
72
72
|
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
73
73
|
const detailRules = rules.filter((r) => r.frontmatter.root === false);
|
|
74
74
|
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
75
|
-
const claudeOutputDir = baseDir ? join(baseDir, config.outputPaths.
|
|
75
|
+
const claudeOutputDir = baseDir ? join(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
|
|
76
76
|
outputs.push({
|
|
77
|
-
tool: "
|
|
77
|
+
tool: "claudecode",
|
|
78
78
|
filepath: join(claudeOutputDir, "CLAUDE.md"),
|
|
79
79
|
content: claudeMdContent
|
|
80
80
|
});
|
|
81
81
|
for (const rule of detailRules) {
|
|
82
82
|
const memoryContent = generateMemoryFile(rule);
|
|
83
83
|
outputs.push({
|
|
84
|
-
tool: "
|
|
84
|
+
tool: "claudecode",
|
|
85
85
|
filepath: join(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
|
|
86
86
|
content: memoryContent
|
|
87
87
|
});
|
|
@@ -327,8 +327,8 @@ async function generateForTool(tool, rules, config, baseDir) {
|
|
|
327
327
|
return generateCursorConfig(rules, config, baseDir);
|
|
328
328
|
case "cline":
|
|
329
329
|
return generateClineConfig(rules, config, baseDir);
|
|
330
|
-
case "
|
|
331
|
-
return await
|
|
330
|
+
case "claudecode":
|
|
331
|
+
return await generateClaudecodeConfig(rules, config, baseDir);
|
|
332
332
|
case "roo":
|
|
333
333
|
return generateRooConfig(rules, config, baseDir);
|
|
334
334
|
default:
|
|
@@ -538,7 +538,7 @@ async function generateCommand(options = {}) {
|
|
|
538
538
|
case "cline":
|
|
539
539
|
deleteTasks.push(removeDirectory(config.outputPaths.cline));
|
|
540
540
|
break;
|
|
541
|
-
case "
|
|
541
|
+
case "claudecode":
|
|
542
542
|
deleteTasks.push(removeClaudeGeneratedFiles());
|
|
543
543
|
break;
|
|
544
544
|
case "roo":
|
|
@@ -589,14 +589,6 @@ var gitignoreCommand = async () => {
|
|
|
589
589
|
const gitignorePath = join7(process.cwd(), ".gitignore");
|
|
590
590
|
const rulesFilesToIgnore = [
|
|
591
591
|
"# Generated by rulesync - AI tool configuration files",
|
|
592
|
-
".github/copilot-instructions.md",
|
|
593
|
-
".github/instructions/",
|
|
594
|
-
".cursor/rules/",
|
|
595
|
-
".clinerules/",
|
|
596
|
-
"CLAUDE.md",
|
|
597
|
-
".claude/memories/",
|
|
598
|
-
".roo/rules/",
|
|
599
|
-
"# Support for --base-dir option (any depth)",
|
|
600
592
|
"**/.github/copilot-instructions.md",
|
|
601
593
|
"**/.github/instructions/",
|
|
602
594
|
"**/.cursor/rules/",
|
|
@@ -635,9 +627,9 @@ ${linesToAdd.join("\n")}
|
|
|
635
627
|
|
|
636
628
|
// src/core/importer.ts
|
|
637
629
|
import { join as join13 } from "path";
|
|
638
|
-
import
|
|
630
|
+
import matter4 from "gray-matter";
|
|
639
631
|
|
|
640
|
-
// src/parsers/
|
|
632
|
+
// src/parsers/claudecode.ts
|
|
641
633
|
import { basename as basename2, join as join8 } from "path";
|
|
642
634
|
async function parseClaudeConfiguration(baseDir = process.cwd()) {
|
|
643
635
|
const errors = [];
|
|
@@ -681,7 +673,7 @@ function parseClaudeMainFile(content, filepath) {
|
|
|
681
673
|
}
|
|
682
674
|
const frontmatter = {
|
|
683
675
|
root: false,
|
|
684
|
-
targets: ["
|
|
676
|
+
targets: ["claudecode"],
|
|
685
677
|
description: "Main Claude Code configuration",
|
|
686
678
|
globs: ["**/*"]
|
|
687
679
|
};
|
|
@@ -705,7 +697,7 @@ async function parseClaudeMemoryFiles(memoryDir) {
|
|
|
705
697
|
const filename = basename2(file, ".md");
|
|
706
698
|
const frontmatter = {
|
|
707
699
|
root: false,
|
|
708
|
-
targets: ["
|
|
700
|
+
targets: ["claudecode"],
|
|
709
701
|
description: `Memory file: ${filename}`,
|
|
710
702
|
globs: ["**/*"]
|
|
711
703
|
};
|
|
@@ -757,67 +749,167 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
|
|
|
757
749
|
}
|
|
758
750
|
|
|
759
751
|
// src/parsers/copilot.ts
|
|
760
|
-
import { join as join10 } from "path";
|
|
752
|
+
import { basename as basename3, join as join10 } from "path";
|
|
753
|
+
import matter2 from "gray-matter";
|
|
761
754
|
async function parseCopilotConfiguration(baseDir = process.cwd()) {
|
|
762
755
|
const errors = [];
|
|
763
756
|
const rules = [];
|
|
764
757
|
const copilotFilePath = join10(baseDir, ".github", "copilot-instructions.md");
|
|
765
|
-
if (
|
|
766
|
-
|
|
767
|
-
|
|
758
|
+
if (await fileExists(copilotFilePath)) {
|
|
759
|
+
try {
|
|
760
|
+
const rawContent = await readFileContent(copilotFilePath);
|
|
761
|
+
const parsed = matter2(rawContent);
|
|
762
|
+
const content = parsed.content.trim();
|
|
763
|
+
if (content) {
|
|
764
|
+
const frontmatter = {
|
|
765
|
+
root: false,
|
|
766
|
+
targets: ["copilot"],
|
|
767
|
+
description: "GitHub Copilot instructions",
|
|
768
|
+
globs: ["**/*"]
|
|
769
|
+
};
|
|
770
|
+
rules.push({
|
|
771
|
+
frontmatter,
|
|
772
|
+
content,
|
|
773
|
+
filename: "copilot-instructions",
|
|
774
|
+
filepath: copilotFilePath
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
} catch (error) {
|
|
778
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
779
|
+
errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
|
|
780
|
+
}
|
|
768
781
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
782
|
+
const instructionsDir = join10(baseDir, ".github", "instructions");
|
|
783
|
+
if (await fileExists(instructionsDir)) {
|
|
784
|
+
try {
|
|
785
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
786
|
+
const files = await readdir2(instructionsDir);
|
|
787
|
+
for (const file of files) {
|
|
788
|
+
if (file.endsWith(".instructions.md")) {
|
|
789
|
+
const filePath = join10(instructionsDir, file);
|
|
790
|
+
const rawContent = await readFileContent(filePath);
|
|
791
|
+
const parsed = matter2(rawContent);
|
|
792
|
+
const content = parsed.content.trim();
|
|
793
|
+
if (content) {
|
|
794
|
+
const filename = basename3(file, ".instructions.md");
|
|
795
|
+
const frontmatter = {
|
|
796
|
+
root: false,
|
|
797
|
+
targets: ["copilot"],
|
|
798
|
+
description: `Copilot instruction: ${filename}`,
|
|
799
|
+
globs: ["**/*"]
|
|
800
|
+
};
|
|
801
|
+
rules.push({
|
|
802
|
+
frontmatter,
|
|
803
|
+
content,
|
|
804
|
+
filename: `copilot-${filename}`,
|
|
805
|
+
filepath: filePath
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
} catch (error) {
|
|
811
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
812
|
+
errors.push(`Failed to parse .github/instructions files: ${errorMessage}`);
|
|
784
813
|
}
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
errors.push(
|
|
814
|
+
}
|
|
815
|
+
if (rules.length === 0) {
|
|
816
|
+
errors.push(
|
|
817
|
+
"No Copilot configuration files found (.github/copilot-instructions.md or .github/instructions/*.instructions.md)"
|
|
818
|
+
);
|
|
788
819
|
}
|
|
789
820
|
return { rules, errors };
|
|
790
821
|
}
|
|
791
822
|
|
|
792
823
|
// src/parsers/cursor.ts
|
|
793
|
-
import { join as join11 } from "path";
|
|
824
|
+
import { basename as basename4, join as join11 } from "path";
|
|
825
|
+
import matter3 from "gray-matter";
|
|
826
|
+
import yaml from "js-yaml";
|
|
827
|
+
var customMatterOptions = {
|
|
828
|
+
engines: {
|
|
829
|
+
yaml: {
|
|
830
|
+
parse: (str) => {
|
|
831
|
+
try {
|
|
832
|
+
const preprocessed = str.replace(/^(\s*globs:\s*)\*\s*$/gm, '$1"*"');
|
|
833
|
+
return yaml.load(preprocessed, { schema: yaml.DEFAULT_SCHEMA });
|
|
834
|
+
} catch (error) {
|
|
835
|
+
try {
|
|
836
|
+
return yaml.load(str, { schema: yaml.FAILSAFE_SCHEMA });
|
|
837
|
+
} catch {
|
|
838
|
+
throw error;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
};
|
|
794
845
|
async function parseCursorConfiguration(baseDir = process.cwd()) {
|
|
795
846
|
const errors = [];
|
|
796
847
|
const rules = [];
|
|
797
848
|
const cursorFilePath = join11(baseDir, ".cursorrules");
|
|
798
|
-
if (
|
|
799
|
-
|
|
800
|
-
|
|
849
|
+
if (await fileExists(cursorFilePath)) {
|
|
850
|
+
try {
|
|
851
|
+
const rawContent = await readFileContent(cursorFilePath);
|
|
852
|
+
const parsed = matter3(rawContent, customMatterOptions);
|
|
853
|
+
const content = parsed.content.trim();
|
|
854
|
+
if (content) {
|
|
855
|
+
const frontmatter = {
|
|
856
|
+
root: false,
|
|
857
|
+
targets: ["cursor"],
|
|
858
|
+
description: "Cursor IDE configuration rules",
|
|
859
|
+
globs: ["**/*"]
|
|
860
|
+
};
|
|
861
|
+
rules.push({
|
|
862
|
+
frontmatter,
|
|
863
|
+
content,
|
|
864
|
+
filename: "cursor-rules",
|
|
865
|
+
filepath: cursorFilePath
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
} catch (error) {
|
|
869
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
870
|
+
errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
|
|
871
|
+
}
|
|
801
872
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
873
|
+
const cursorRulesDir = join11(baseDir, ".cursor", "rules");
|
|
874
|
+
if (await fileExists(cursorRulesDir)) {
|
|
875
|
+
try {
|
|
876
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
877
|
+
const files = await readdir2(cursorRulesDir);
|
|
878
|
+
for (const file of files) {
|
|
879
|
+
if (file.endsWith(".mdc")) {
|
|
880
|
+
const filePath = join11(cursorRulesDir, file);
|
|
881
|
+
try {
|
|
882
|
+
const rawContent = await readFileContent(filePath);
|
|
883
|
+
const parsed = matter3(rawContent, customMatterOptions);
|
|
884
|
+
const content = parsed.content.trim();
|
|
885
|
+
if (content) {
|
|
886
|
+
const filename = basename4(file, ".mdc");
|
|
887
|
+
const frontmatter = {
|
|
888
|
+
root: false,
|
|
889
|
+
targets: ["cursor"],
|
|
890
|
+
description: `Cursor rule: ${filename}`,
|
|
891
|
+
globs: ["**/*"]
|
|
892
|
+
};
|
|
893
|
+
rules.push({
|
|
894
|
+
frontmatter,
|
|
895
|
+
content,
|
|
896
|
+
filename: `cursor-${filename}`,
|
|
897
|
+
filepath: filePath
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
} catch (error) {
|
|
901
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
902
|
+
errors.push(`Failed to parse ${filePath}: ${errorMessage}`);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
} catch (error) {
|
|
907
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
908
|
+
errors.push(`Failed to parse .cursor/rules files: ${errorMessage}`);
|
|
817
909
|
}
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
errors.push(
|
|
910
|
+
}
|
|
911
|
+
if (rules.length === 0) {
|
|
912
|
+
errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
|
|
821
913
|
}
|
|
822
914
|
return { rules, errors };
|
|
823
915
|
}
|
|
@@ -865,7 +957,7 @@ async function importConfiguration(options) {
|
|
|
865
957
|
}
|
|
866
958
|
try {
|
|
867
959
|
switch (tool) {
|
|
868
|
-
case "
|
|
960
|
+
case "claudecode": {
|
|
869
961
|
const claudeResult = await parseClaudeConfiguration(baseDir);
|
|
870
962
|
rules = claudeResult.rules;
|
|
871
963
|
errors.push(...claudeResult.errors);
|
|
@@ -940,7 +1032,7 @@ async function importConfiguration(options) {
|
|
|
940
1032
|
};
|
|
941
1033
|
}
|
|
942
1034
|
function generateRuleFileContent(rule) {
|
|
943
|
-
const frontmatter =
|
|
1035
|
+
const frontmatter = matter4.stringify("", rule.frontmatter);
|
|
944
1036
|
return frontmatter + rule.content;
|
|
945
1037
|
}
|
|
946
1038
|
async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
@@ -956,14 +1048,14 @@ async function generateUniqueFilename(rulesDir, baseFilename) {
|
|
|
956
1048
|
// src/cli/commands/import.ts
|
|
957
1049
|
async function importCommand(options = {}) {
|
|
958
1050
|
const tools = [];
|
|
959
|
-
if (options.
|
|
1051
|
+
if (options.claudecode) tools.push("claudecode");
|
|
960
1052
|
if (options.cursor) tools.push("cursor");
|
|
961
1053
|
if (options.copilot) tools.push("copilot");
|
|
962
1054
|
if (options.cline) tools.push("cline");
|
|
963
1055
|
if (options.roo) tools.push("roo");
|
|
964
1056
|
if (tools.length === 0) {
|
|
965
1057
|
console.error(
|
|
966
|
-
"\u274C Please specify at least one tool to import from (--
|
|
1058
|
+
"\u274C Please specify at least one tool to import from (--claudecode, --cursor, --copilot, --cline, --roo)"
|
|
967
1059
|
);
|
|
968
1060
|
process.exit(1);
|
|
969
1061
|
}
|
|
@@ -1174,7 +1266,7 @@ async function statusCommand() {
|
|
|
1174
1266
|
const nonRootRules = rules.length - rootRules;
|
|
1175
1267
|
console.log(` - Root rules: ${rootRules}`);
|
|
1176
1268
|
console.log(` - Non-root rules: ${nonRootRules}`);
|
|
1177
|
-
const targetCounts = { copilot: 0, cursor: 0, cline: 0,
|
|
1269
|
+
const targetCounts = { copilot: 0, cursor: 0, cline: 0, claudecode: 0, roo: 0 };
|
|
1178
1270
|
for (const rule of rules) {
|
|
1179
1271
|
const targets = rule.frontmatter.targets[0] === "*" ? config.defaultTargets : rule.frontmatter.targets;
|
|
1180
1272
|
for (const target of targets) {
|
|
@@ -1187,7 +1279,7 @@ async function statusCommand() {
|
|
|
1187
1279
|
console.log(` - Copilot: ${targetCounts.copilot} rules`);
|
|
1188
1280
|
console.log(` - Cursor: ${targetCounts.cursor} rules`);
|
|
1189
1281
|
console.log(` - Cline: ${targetCounts.cline} rules`);
|
|
1190
|
-
console.log(` - Claude: ${targetCounts.
|
|
1282
|
+
console.log(` - Claude Code: ${targetCounts.claudecode} rules`);
|
|
1191
1283
|
console.log(` - Roo: ${targetCounts.roo} rules`);
|
|
1192
1284
|
}
|
|
1193
1285
|
console.log("\n\u{1F4E4} Generated files:");
|
|
@@ -1286,12 +1378,12 @@ async function watchCommand() {
|
|
|
1286
1378
|
|
|
1287
1379
|
// src/cli/index.ts
|
|
1288
1380
|
var program = new Command();
|
|
1289
|
-
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.
|
|
1381
|
+
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.31.0");
|
|
1290
1382
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
1291
1383
|
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
1292
1384
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
1293
|
-
program.command("import").description("Import configurations from AI tools to rulesync format").option("--
|
|
1294
|
-
program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--
|
|
1385
|
+
program.command("import").description("Import configurations from AI tools to rulesync format").option("--claudecode", "Import from Claude Code (CLAUDE.md)").option("--cursor", "Import from Cursor (.cursorrules)").option("--copilot", "Import from GitHub Copilot (.github/copilot-instructions.md)").option("--cline", "Import from Cline (.cline/instructions.md)").option("--roo", "Import from Roo Code (.roo/instructions.md)").option("-v, --verbose", "Verbose output").action(importCommand);
|
|
1386
|
+
program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claudecode", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--delete", "Delete all existing files in output directories before generating").option(
|
|
1295
1387
|
"-b, --base-dir <paths>",
|
|
1296
1388
|
"Base directories to generate files (comma-separated for multiple paths)"
|
|
1297
1389
|
).option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
@@ -1299,7 +1391,7 @@ program.command("generate").description("Generate configuration files for AI too
|
|
|
1299
1391
|
if (options.copilot) tools.push("copilot");
|
|
1300
1392
|
if (options.cursor) tools.push("cursor");
|
|
1301
1393
|
if (options.cline) tools.push("cline");
|
|
1302
|
-
if (options.
|
|
1394
|
+
if (options.claudecode) tools.push("claudecode");
|
|
1303
1395
|
if (options.roo) tools.push("roo");
|
|
1304
1396
|
const generateOptions = {
|
|
1305
1397
|
verbose: options.verbose,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rulesync",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -35,12 +35,14 @@
|
|
|
35
35
|
"chokidar": "4.0.3",
|
|
36
36
|
"commander": "14.0.0",
|
|
37
37
|
"gray-matter": "4.0.3",
|
|
38
|
+
"js-yaml": "4.1.0",
|
|
38
39
|
"marked": "15.0.12"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"@biomejs/biome": "2.0.0",
|
|
42
43
|
"@secretlint/secretlint-rule-preset-recommend": "10.1.0",
|
|
43
44
|
"@tsconfig/node24": "24.0.1",
|
|
45
|
+
"@types/js-yaml": "4.0.9",
|
|
44
46
|
"@types/node": "24.0.3",
|
|
45
47
|
"@typescript/native-preview": "7.0.0-dev.20250623.1",
|
|
46
48
|
"@vitest/coverage-v8": "3.2.4",
|