rulesync 0.12.0 → 0.14.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 +290 -0
- package/README.md +40 -91
- package/dist/index.js +98 -57
- package/dist/index.mjs +72 -31
- package/package.json +1 -1
package/README.ja.md
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# rulesync
|
|
2
|
+
|
|
3
|
+
[](https://github.com/dyoshikawa/rulesync/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/rulesync)
|
|
5
|
+
|
|
6
|
+
統一されたAIルールファイル(`.rulesync/*.md`)から、様々なAI開発ツール用の設定ファイルを自動生成するNode.js CLIツールです。
|
|
7
|
+
|
|
8
|
+
[English](./README.md) | **日本語**
|
|
9
|
+
|
|
10
|
+
## 対応ツール
|
|
11
|
+
|
|
12
|
+
- **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
|
+
- **Claude Code Memory** (`./CLAUDE.md` + `.claude/memories/*.md`)
|
|
16
|
+
- **Roo Code Rules** (`.roo/rules/*.md`)
|
|
17
|
+
|
|
18
|
+
## インストール
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install -g rulesync
|
|
22
|
+
# または
|
|
23
|
+
pnpm add -g rulesync
|
|
24
|
+
# または
|
|
25
|
+
yarn global add rulesync
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 使い始める
|
|
29
|
+
|
|
30
|
+
1. **プロジェクトを初期化:**
|
|
31
|
+
```bash
|
|
32
|
+
npx rulesync init
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
2. **`.rulesync/`ディレクトリの生成されたルールファイル**をプロジェクトのニーズに合わせて編集します
|
|
36
|
+
|
|
37
|
+
または新しいルールファイルを追加:
|
|
38
|
+
```bash
|
|
39
|
+
npx rulesync add my-custom-rules
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
3. **ツール固有の設定ファイルを生成:**
|
|
43
|
+
```bash
|
|
44
|
+
npx rulesync generate
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
4. **オプション: 生成されたファイルを.gitignoreに追加:**
|
|
48
|
+
```bash
|
|
49
|
+
npx rulesync gitignore
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
以上です!AIコーディングアシスタントが生成された設定ファイルを自動的に使用するようになります。
|
|
53
|
+
|
|
54
|
+
## rulesyncを使う理由
|
|
55
|
+
|
|
56
|
+
### 🔧 **ツールの柔軟性**
|
|
57
|
+
チームメンバーは好みのAIコーディングツールを自由に選択できます。GitHub Copilot、Cursor、Cline、Claude Codeのいずれであっても、各開発者は生産性を最大化するツールを使用できます。
|
|
58
|
+
|
|
59
|
+
### 📈 **将来を見据えた開発**
|
|
60
|
+
AI開発ツールは新しいツールが頻繁に登場し、急速に進化しています。rulesyncがあれば、ツールを切り替える際にルールを一から再定義する必要がありません。
|
|
61
|
+
|
|
62
|
+
### 🎯 **マルチツールワークフロー**
|
|
63
|
+
複数のAIツールを組み合わせたハイブリッド開発ワークフローを可能にします:
|
|
64
|
+
- GitHub Copilot:コード補完
|
|
65
|
+
- Cursor:リファクタリング
|
|
66
|
+
- Claude Code:アーキテクチャ設計
|
|
67
|
+
- Cline:デバッグ支援
|
|
68
|
+
|
|
69
|
+
### 🔓 **ベンダーロックインなし**
|
|
70
|
+
ベンダーロックインを完全に回避できます。rulesyncの使用を停止することを決定した場合でも、生成されたルールファイル(`.github/instructions/`、`.cursor/rules/`、`.clinerules/`、`CLAUDE.md`など)をそのまま使い続けることができます。
|
|
71
|
+
|
|
72
|
+
### 🎯 **ツール間の一貫性**
|
|
73
|
+
すべてのAIツールに一貫したルールを適用し、チーム全体のコード品質と開発体験を向上させます。
|
|
74
|
+
|
|
75
|
+
## Claude Code統合
|
|
76
|
+
|
|
77
|
+
### カスタムスラッシュコマンドの作成
|
|
78
|
+
|
|
79
|
+
Claude Codeの組み込み`/init`コマンドを使用する代わりに、rulesync専用のカスタムスラッシュコマンドを作成することをお勧めします。
|
|
80
|
+
|
|
81
|
+
[Claude Codeスラッシュコマンドドキュメント](https://docs.anthropic.com/en/docs/claude-code/slash-commands)を参照し、以下のカスタムコマンドを追加してください:
|
|
82
|
+
|
|
83
|
+
**`.claude/commands/init-rulesync.md`**
|
|
84
|
+
|
|
85
|
+
```markdown
|
|
86
|
+
このプロジェクトの内容を確認し、必要に応じて.rulesync/*.mdファイルを更新してください。
|
|
87
|
+
|
|
88
|
+
手順:
|
|
89
|
+
1. プロジェクト構造とコードベースを分析
|
|
90
|
+
2. 既存の.rulesync/ファイルを確認
|
|
91
|
+
3. プロジェクトの技術スタック、アーキテクチャ、コーディング規約を考慮
|
|
92
|
+
4. 不足している要素や改善点が見つかった場合は.rulesync/*.mdファイルを更新
|
|
93
|
+
5. 必要に応じてrulesync generateを実行
|
|
94
|
+
|
|
95
|
+
考慮すべきプロジェクトの特徴:
|
|
96
|
+
- 技術スタック
|
|
97
|
+
- アーキテクチャパターン
|
|
98
|
+
- コーディング規約
|
|
99
|
+
- セキュリティ要件
|
|
100
|
+
- パフォーマンスの考慮事項
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 統合のメリット
|
|
104
|
+
|
|
105
|
+
- **プロジェクト固有の初期化**: 各プロジェクトに最適化されたルール設定
|
|
106
|
+
- **自動ルール更新**: プロジェクトの変更に応じてルールが自動的に適応
|
|
107
|
+
- **チーム標準化**: すべてのメンバーが同じルールセットを使用
|
|
108
|
+
- **継続的改善**: プロジェクトの成長とともにルールが進化
|
|
109
|
+
|
|
110
|
+
## 使用方法
|
|
111
|
+
|
|
112
|
+
### 1. 初期化
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npx rulesync init
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
これにより、サンプルルールファイルを含む`.rulesync/`ディレクトリが作成されます。
|
|
119
|
+
|
|
120
|
+
### 2. ルールファイルの編集
|
|
121
|
+
|
|
122
|
+
`.rulesync/`ディレクトリ内の各Markdownファイルでメタデータをフロントマターで定義します。詳細な例については、下記の[ファイル例](#ファイル例)セクションを参照してください。
|
|
123
|
+
|
|
124
|
+
### ルールレベル
|
|
125
|
+
|
|
126
|
+
rulesyncは2レベルのルールシステムを使用します:
|
|
127
|
+
|
|
128
|
+
- **root: true**: プロジェクト全体の概要とポリシー
|
|
129
|
+
- プロジェクトごとに**1つ**のrootファイルのみ許可
|
|
130
|
+
- 高レベルのガイドラインとプロジェクトコンテキストを含む
|
|
131
|
+
- **root: false**: 具体的な実装ルールと詳細なガイドライン
|
|
132
|
+
- 複数の非rootファイルが許可
|
|
133
|
+
- 具体的なコーディングルール、命名規約などを含む
|
|
134
|
+
|
|
135
|
+
#### ツール固有の動作
|
|
136
|
+
|
|
137
|
+
各AIツールはルールレベルを異なって処理します:
|
|
138
|
+
|
|
139
|
+
| ツール | ルートルール | 非ルートルール | 特別な動作 |
|
|
140
|
+
|------|------------|----------------|------------------|
|
|
141
|
+
| **Claude Code** | `./CLAUDE.md` | `.claude/memories/*.md` | CLAUDE.mdが詳細ファイルへの`@filename`参照を含む |
|
|
142
|
+
| **Cursor** | `ruletype: always` | `ruletype: autoattached` | globsのない詳細ルールは`ruletype: agentrequested`を使用 |
|
|
143
|
+
| **GitHub Copilot** | 標準フォーマット | 標準フォーマット | すべてのルールがフロントマター付きの同じフォーマットを使用 |
|
|
144
|
+
| **Cline** | 標準フォーマット | 標準フォーマット | すべてのルールがプレーンMarkdownフォーマットを使用 |
|
|
145
|
+
| **Roo Code** | 標準フォーマット | 標準フォーマット | すべてのルールが説明ヘッダー付きのプレーンMarkdownフォーマットを使用 |
|
|
146
|
+
|
|
147
|
+
### 3. 設定ファイルの生成
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# すべてのツール用に生成
|
|
151
|
+
npx rulesync generate
|
|
152
|
+
|
|
153
|
+
# 特定のツール用に生成
|
|
154
|
+
npx rulesync generate --copilot
|
|
155
|
+
npx rulesync generate --cursor
|
|
156
|
+
npx rulesync generate --cline
|
|
157
|
+
npx rulesync generate --claude
|
|
158
|
+
npx rulesync generate --roo
|
|
159
|
+
|
|
160
|
+
# クリーンビルド(既存ファイルを最初に削除)
|
|
161
|
+
npx rulesync generate --delete
|
|
162
|
+
|
|
163
|
+
# 特定ツール用のクリーンビルド
|
|
164
|
+
npx rulesync generate --copilot --cursor --delete
|
|
165
|
+
|
|
166
|
+
# 詳細出力
|
|
167
|
+
npx rulesync generate --verbose
|
|
168
|
+
npx rulesync generate --delete --verbose
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
#### 生成オプション
|
|
172
|
+
|
|
173
|
+
- `--delete`: 新しいファイルを作成する前に既存の生成済みファイルをすべて削除
|
|
174
|
+
- `--verbose`: 生成プロセス中に詳細出力を表示
|
|
175
|
+
- `--copilot`, `--cursor`, `--cline`, `--claude`, `--roo`: 指定されたツールのみ生成
|
|
176
|
+
|
|
177
|
+
### 4. その他のコマンド
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# サンプルファイルでプロジェクトを初期化
|
|
181
|
+
npx rulesync init
|
|
182
|
+
|
|
183
|
+
# 新しいルールファイルを追加
|
|
184
|
+
npx rulesync add <filename>
|
|
185
|
+
npx rulesync add typescript-rules
|
|
186
|
+
npx rulesync add security.md # .md拡張子は自動的に処理される
|
|
187
|
+
|
|
188
|
+
# ルールファイルを検証
|
|
189
|
+
npx rulesync validate
|
|
190
|
+
|
|
191
|
+
# 現在のステータスを確認
|
|
192
|
+
npx rulesync status
|
|
193
|
+
|
|
194
|
+
# ファイルを監視して自動生成
|
|
195
|
+
npx rulesync watch
|
|
196
|
+
|
|
197
|
+
# 生成されたファイルを.gitignoreに追加
|
|
198
|
+
npx rulesync gitignore
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## 設定ファイル構造
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
.rulesync/
|
|
205
|
+
├── overview.md # プロジェクト概要 (root: true, 1つのみ)
|
|
206
|
+
├── coding-rules.md # コーディングルール (root: false)
|
|
207
|
+
├── naming-conventions.md # 命名規約 (root: false)
|
|
208
|
+
├── architecture.md # アーキテクチャガイドライン (root: false)
|
|
209
|
+
├── security.md # セキュリティルール (root: false)
|
|
210
|
+
└── custom.md # プロジェクト固有ルール (root: false)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### フロントマタースキーマ
|
|
214
|
+
|
|
215
|
+
各ルールファイルには以下のフィールドを含むフロントマターが必要です:
|
|
216
|
+
|
|
217
|
+
```yaml
|
|
218
|
+
---
|
|
219
|
+
root: true | false # 必須: ルールレベル (概要の場合true、詳細の場合false)
|
|
220
|
+
targets: ["*"] # 必須: ターゲットツール (* = すべて、または特定のツール)
|
|
221
|
+
description: "簡潔な説明" # 必須: ルールの説明
|
|
222
|
+
globs: "**/*.ts,**/*.js" # 必須: ファイルパターン (カンマ区切りまたは空文字列)
|
|
223
|
+
---
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### ファイル例
|
|
227
|
+
|
|
228
|
+
**ルートファイル** (`.rulesync/overview.md`):
|
|
229
|
+
```markdown
|
|
230
|
+
---
|
|
231
|
+
root: true
|
|
232
|
+
targets: ["*"]
|
|
233
|
+
description: "プロジェクト概要と開発思想"
|
|
234
|
+
globs: "src/**/*.ts"
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
# プロジェクト開発ガイドライン
|
|
238
|
+
|
|
239
|
+
このプロジェクトはクリーンアーキテクチャの原則に従ったTypeScript-firstの開発を行っています。
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**非ルートファイル** (`.rulesync/coding-rules.md`):
|
|
243
|
+
```markdown
|
|
244
|
+
---
|
|
245
|
+
root: false
|
|
246
|
+
targets: ["copilot", "cursor", "roo"]
|
|
247
|
+
description: "TypeScriptコーディング標準"
|
|
248
|
+
globs: "**/*.ts,**/*.tsx"
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
# TypeScriptコーディングルール
|
|
252
|
+
|
|
253
|
+
- 厳密なTypeScript設定を使用
|
|
254
|
+
- オブジェクト形状にはtypeよりinterfaceを優先
|
|
255
|
+
- 意味のある変数名を使用
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## 生成される設定ファイル
|
|
259
|
+
|
|
260
|
+
| ツール | 出力パス | フォーマット | ルールレベル処理 |
|
|
261
|
+
|------|------------|--------|-------------------|
|
|
262
|
+
| **GitHub Copilot** | `.github/instructions/*.instructions.md` | フロントマター + Markdown | 両レベルとも同じフォーマットを使用 |
|
|
263
|
+
| **Cursor** | `.cursor/rules/*.mdc` | MDC (YAMLヘッダー + Markdown) | ルート: `ruletype: always`<br>非ルート: `ruletype: autoattached`<br>globsなしの非ルート: `ruletype: agentrequested` |
|
|
264
|
+
| **Cline** | `.clinerules/*.md` | プレーンMarkdown | 両レベルとも同じフォーマットを使用 |
|
|
265
|
+
| **Claude Code** | `./CLAUDE.md` (ルート)<br>`.claude/memories/*.md` (非ルート) | プレーンMarkdown | ルートはCLAUDE.mdに移動<br>非ルートは別メモリファイルに移動<br>CLAUDE.mdは`@filename`参照を含む |
|
|
266
|
+
| **Roo Code** | `.roo/rules/*.md` | プレーンMarkdown | 両レベルとも説明ヘッダー付きの同じフォーマットを使用 |
|
|
267
|
+
|
|
268
|
+
## バリデーション
|
|
269
|
+
|
|
270
|
+
rulesyncはルールファイルを検証し、有用なエラーメッセージを提供します:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
npx rulesync validate
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
一般的なバリデーションルール:
|
|
277
|
+
- プロジェクトごとに1つのルートファイル(root: true)のみ許可
|
|
278
|
+
- すべてのフロントマターフィールドが必須で適切にフォーマットされている
|
|
279
|
+
- ファイルパターン(globs)が有効な構文を使用
|
|
280
|
+
- ターゲットツールが認識される値である
|
|
281
|
+
|
|
282
|
+
## ライセンス
|
|
283
|
+
|
|
284
|
+
MIT License
|
|
285
|
+
|
|
286
|
+
## 貢献
|
|
287
|
+
|
|
288
|
+
Issues と Pull Requests を歓迎します!
|
|
289
|
+
|
|
290
|
+
開発環境の設定と貢献ガイドラインについては、[CONTRIBUTING.ja.md](./CONTRIBUTING.ja.md)を参照してください。
|
package/README.md
CHANGED
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
A Node.js CLI tool that automatically generates configuration files for various AI development tools from unified AI rule files (`.rulesync/*.md`).
|
|
7
7
|
|
|
8
|
+
**English** | [日本語](./README.ja.md)
|
|
9
|
+
|
|
8
10
|
## Supported Tools
|
|
9
11
|
|
|
10
|
-
- **GitHub Copilot Custom Instructions** (`.github/instructions/*.instructions.md`)
|
|
12
|
+
- **GitHub Copilot Custom Instructions** (`.github/copilot-instructions.md` + `.github/instructions/*.instructions.md`)
|
|
11
13
|
- **Cursor Project Rules** (`.cursor/rules/*.mdc`)
|
|
12
14
|
- **Cline Rules** (`.clinerules/*.md`)
|
|
13
15
|
- **Claude Code Memory** (`./CLAUDE.md` + `.claude/memories/*.md`)
|
|
@@ -25,73 +27,29 @@ yarn global add rulesync
|
|
|
25
27
|
|
|
26
28
|
## Getting Started
|
|
27
29
|
|
|
28
|
-
### Quick Start Example
|
|
29
|
-
|
|
30
30
|
1. **Initialize your project:**
|
|
31
31
|
```bash
|
|
32
|
-
rulesync init
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
2. **Create an overview file** (`.rulesync/overview.md`):
|
|
36
|
-
```markdown
|
|
37
|
-
---
|
|
38
|
-
root: true
|
|
39
|
-
targets: ["*"]
|
|
40
|
-
description: "Project overview and development philosophy"
|
|
41
|
-
globs: ["src/**/*.ts", "src/**/*.js"]
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
# Project Development Guidelines
|
|
45
|
-
|
|
46
|
-
This is a TypeScript/JavaScript project following clean architecture principles.
|
|
47
|
-
We prioritize code readability, maintainability, and type safety.
|
|
48
|
-
|
|
49
|
-
## Tech Stack
|
|
50
|
-
- TypeScript for type safety
|
|
51
|
-
- Node.js runtime
|
|
52
|
-
- Modern ES6+ features
|
|
32
|
+
npx rulesync init
|
|
53
33
|
```
|
|
54
34
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
description: "TypeScript coding standards and best practices"
|
|
61
|
-
globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
# TypeScript Coding Rules
|
|
65
|
-
|
|
66
|
-
## Code Style
|
|
67
|
-
- Use strict TypeScript configuration
|
|
68
|
-
- Prefer `const` over `let` when possible
|
|
69
|
-
- Use meaningful, descriptive variable names
|
|
70
|
-
- Write JSDoc comments for public APIs
|
|
71
|
-
|
|
72
|
-
## Type Definitions
|
|
73
|
-
- Prefer interfaces over types for object shapes
|
|
74
|
-
- Use union types for controlled values
|
|
75
|
-
- Avoid `any` type - use `unknown` instead
|
|
76
|
-
- Define return types for functions explicitly
|
|
77
|
-
|
|
78
|
-
## Error Handling
|
|
79
|
-
- Use Result pattern for error handling
|
|
80
|
-
- Throw errors only for unexpected conditions
|
|
81
|
-
- Validate input parameters at function boundaries
|
|
35
|
+
2. **Edit the generated rule files** in `.rulesync/` directory to match your project needs
|
|
36
|
+
|
|
37
|
+
Or add new rule files:
|
|
38
|
+
```bash
|
|
39
|
+
npx rulesync add my-custom-rules
|
|
82
40
|
```
|
|
83
41
|
|
|
84
|
-
|
|
42
|
+
3. **Generate tool-specific configuration files:**
|
|
85
43
|
```bash
|
|
86
|
-
rulesync generate
|
|
44
|
+
npx rulesync generate
|
|
87
45
|
```
|
|
88
46
|
|
|
89
|
-
|
|
47
|
+
4. **Optional: Add generated files to .gitignore:**
|
|
90
48
|
```bash
|
|
91
|
-
rulesync gitignore
|
|
49
|
+
npx rulesync gitignore
|
|
92
50
|
```
|
|
93
51
|
|
|
94
|
-
|
|
52
|
+
That's it! Your AI coding assistants will now use the generated configuration files automatically.
|
|
95
53
|
|
|
96
54
|
## Why rulesync?
|
|
97
55
|
|
|
@@ -154,28 +112,14 @@ Project characteristics to consider:
|
|
|
154
112
|
### 1. Initialize
|
|
155
113
|
|
|
156
114
|
```bash
|
|
157
|
-
rulesync init
|
|
115
|
+
npx rulesync init
|
|
158
116
|
```
|
|
159
117
|
|
|
160
118
|
This creates a `.rulesync/` directory with sample rule files.
|
|
161
119
|
|
|
162
120
|
### 2. Edit Rule Files
|
|
163
121
|
|
|
164
|
-
Define metadata in front matter for each Markdown file
|
|
165
|
-
|
|
166
|
-
```markdown
|
|
167
|
-
---
|
|
168
|
-
root: true # or false
|
|
169
|
-
targets: ["*"] # or [copilot, cursor, cline, claude, roo]
|
|
170
|
-
description: "TypeScript coding rules"
|
|
171
|
-
globs: ["**/*.ts", "**/*.tsx"]
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
# TypeScript Rules
|
|
175
|
-
|
|
176
|
-
- Use TypeScript
|
|
177
|
-
- Write clear type annotations
|
|
178
|
-
```
|
|
122
|
+
Define metadata in front matter for each Markdown file in the `.rulesync/` directory. See the [Example Files](#example-files) section below for detailed examples.
|
|
179
123
|
|
|
180
124
|
### Rule Levels
|
|
181
125
|
|
|
@@ -204,24 +148,24 @@ Each AI tool handles rule levels differently:
|
|
|
204
148
|
|
|
205
149
|
```bash
|
|
206
150
|
# Generate for all tools
|
|
207
|
-
rulesync generate
|
|
151
|
+
npx rulesync generate
|
|
208
152
|
|
|
209
153
|
# Generate for specific tools
|
|
210
|
-
rulesync generate --copilot
|
|
211
|
-
rulesync generate --cursor
|
|
212
|
-
rulesync generate --cline
|
|
213
|
-
rulesync generate --claude
|
|
214
|
-
rulesync generate --roo
|
|
154
|
+
npx rulesync generate --copilot
|
|
155
|
+
npx rulesync generate --cursor
|
|
156
|
+
npx rulesync generate --cline
|
|
157
|
+
npx rulesync generate --claude
|
|
158
|
+
npx rulesync generate --roo
|
|
215
159
|
|
|
216
160
|
# Clean build (delete existing files first)
|
|
217
|
-
rulesync generate --delete
|
|
161
|
+
npx rulesync generate --delete
|
|
218
162
|
|
|
219
163
|
# Clean build for specific tools
|
|
220
|
-
rulesync generate --copilot --cursor --delete
|
|
164
|
+
npx rulesync generate --copilot --cursor --delete
|
|
221
165
|
|
|
222
166
|
# Verbose output
|
|
223
|
-
rulesync generate --verbose
|
|
224
|
-
rulesync generate --delete --verbose
|
|
167
|
+
npx rulesync generate --verbose
|
|
168
|
+
npx rulesync generate --delete --verbose
|
|
225
169
|
```
|
|
226
170
|
|
|
227
171
|
#### Generate Options
|
|
@@ -234,19 +178,24 @@ rulesync generate --delete --verbose
|
|
|
234
178
|
|
|
235
179
|
```bash
|
|
236
180
|
# Initialize project with sample files
|
|
237
|
-
rulesync init
|
|
181
|
+
npx rulesync init
|
|
182
|
+
|
|
183
|
+
# Add a new rule file
|
|
184
|
+
npx rulesync add <filename>
|
|
185
|
+
npx rulesync add typescript-rules
|
|
186
|
+
npx rulesync add security.md # .md extension is automatically handled
|
|
238
187
|
|
|
239
188
|
# Validate rule files
|
|
240
|
-
rulesync validate
|
|
189
|
+
npx rulesync validate
|
|
241
190
|
|
|
242
191
|
# Check current status
|
|
243
|
-
rulesync status
|
|
192
|
+
npx rulesync status
|
|
244
193
|
|
|
245
194
|
# Watch files and auto-generate
|
|
246
|
-
rulesync watch
|
|
195
|
+
npx rulesync watch
|
|
247
196
|
|
|
248
197
|
# Add generated files to .gitignore
|
|
249
|
-
rulesync gitignore
|
|
198
|
+
npx rulesync gitignore
|
|
250
199
|
```
|
|
251
200
|
|
|
252
201
|
## Configuration File Structure
|
|
@@ -270,7 +219,7 @@ Each rule file must include frontmatter with the following fields:
|
|
|
270
219
|
root: true | false # Required: Rule level (true for overview, false for details)
|
|
271
220
|
targets: ["*"] # Required: Target tools (* = all, or specific tools)
|
|
272
221
|
description: "Brief description" # Required: Rule description
|
|
273
|
-
globs:
|
|
222
|
+
globs: "**/*.ts,**/*.js" # Required: File patterns (comma-separated or empty string)
|
|
274
223
|
---
|
|
275
224
|
```
|
|
276
225
|
|
|
@@ -282,7 +231,7 @@ globs: ["**/*.ts", "**/*.js"] # Required: File patterns (can be empty array)
|
|
|
282
231
|
root: true
|
|
283
232
|
targets: ["*"]
|
|
284
233
|
description: "Project overview and development philosophy"
|
|
285
|
-
globs:
|
|
234
|
+
globs: "src/**/*.ts"
|
|
286
235
|
---
|
|
287
236
|
|
|
288
237
|
# Project Development Guidelines
|
|
@@ -296,7 +245,7 @@ This project follows TypeScript-first development with clean architecture princi
|
|
|
296
245
|
root: false
|
|
297
246
|
targets: ["copilot", "cursor", "roo"]
|
|
298
247
|
description: "TypeScript coding standards"
|
|
299
|
-
globs:
|
|
248
|
+
globs: "**/*.ts,**/*.tsx"
|
|
300
249
|
---
|
|
301
250
|
|
|
302
251
|
# TypeScript Coding Rules
|
|
@@ -321,7 +270,7 @@ globs: ["**/*.ts", "**/*.tsx"]
|
|
|
321
270
|
rulesync validates your rule files and provides helpful error messages:
|
|
322
271
|
|
|
323
272
|
```bash
|
|
324
|
-
rulesync validate
|
|
273
|
+
npx rulesync validate
|
|
325
274
|
```
|
|
326
275
|
|
|
327
276
|
Common validation rules:
|
package/dist/index.js
CHANGED
|
@@ -26,8 +26,70 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
// src/cli/index.ts
|
|
27
27
|
var import_commander = require("commander");
|
|
28
28
|
|
|
29
|
+
// src/cli/commands/add.ts
|
|
30
|
+
var import_promises = require("fs/promises");
|
|
31
|
+
var import_node_path = __toESM(require("path"));
|
|
32
|
+
|
|
33
|
+
// src/utils/config.ts
|
|
34
|
+
function getDefaultConfig() {
|
|
35
|
+
return {
|
|
36
|
+
aiRulesDir: ".rulesync",
|
|
37
|
+
outputPaths: {
|
|
38
|
+
copilot: ".github/instructions",
|
|
39
|
+
cursor: ".cursor/rules",
|
|
40
|
+
cline: ".clinerules",
|
|
41
|
+
claude: ".",
|
|
42
|
+
roo: ".roo/rules"
|
|
43
|
+
},
|
|
44
|
+
watchEnabled: false,
|
|
45
|
+
defaultTargets: ["copilot", "cursor", "cline", "claude", "roo"]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function resolveTargets(targets, config) {
|
|
49
|
+
if (targets.includes("*")) {
|
|
50
|
+
return config.defaultTargets;
|
|
51
|
+
}
|
|
52
|
+
return targets;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/cli/commands/add.ts
|
|
56
|
+
function sanitizeFilename(filename) {
|
|
57
|
+
return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
|
|
58
|
+
}
|
|
59
|
+
function generateRuleTemplate(filename) {
|
|
60
|
+
return `---
|
|
61
|
+
root: false
|
|
62
|
+
targets: ["*"]
|
|
63
|
+
description: "Rules for ${filename}"
|
|
64
|
+
globs: []
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
# ${filename.charAt(0).toUpperCase() + filename.slice(1)} Rules
|
|
68
|
+
|
|
69
|
+
Add your rules here.
|
|
70
|
+
`;
|
|
71
|
+
}
|
|
72
|
+
async function addCommand(filename) {
|
|
73
|
+
try {
|
|
74
|
+
const config = getDefaultConfig();
|
|
75
|
+
const sanitizedFilename = sanitizeFilename(filename);
|
|
76
|
+
const rulesDir = config.aiRulesDir;
|
|
77
|
+
const filePath = import_node_path.default.join(rulesDir, `${sanitizedFilename}.md`);
|
|
78
|
+
await (0, import_promises.mkdir)(rulesDir, { recursive: true });
|
|
79
|
+
const template = generateRuleTemplate(sanitizedFilename);
|
|
80
|
+
await (0, import_promises.writeFile)(filePath, template, "utf8");
|
|
81
|
+
console.log(`\u2705 Created rule file: ${filePath}`);
|
|
82
|
+
console.log(`\u{1F4DD} Edit the file to customize your rules.`);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.error(
|
|
85
|
+
`\u274C Failed to create rule file: ${error instanceof Error ? error.message : String(error)}`
|
|
86
|
+
);
|
|
87
|
+
process.exit(3);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
29
91
|
// src/generators/claude.ts
|
|
30
|
-
var
|
|
92
|
+
var import_node_path2 = require("path");
|
|
31
93
|
async function generateClaudeConfig(rules, config) {
|
|
32
94
|
const outputs = [];
|
|
33
95
|
const rootRules = rules.filter((r) => r.frontmatter.root === true);
|
|
@@ -35,14 +97,14 @@ async function generateClaudeConfig(rules, config) {
|
|
|
35
97
|
const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
|
|
36
98
|
outputs.push({
|
|
37
99
|
tool: "claude",
|
|
38
|
-
filepath: (0,
|
|
100
|
+
filepath: (0, import_node_path2.join)(config.outputPaths.claude, "CLAUDE.md"),
|
|
39
101
|
content: claudeMdContent
|
|
40
102
|
});
|
|
41
103
|
for (const rule of detailRules) {
|
|
42
104
|
const memoryContent = generateMemoryFile(rule);
|
|
43
105
|
outputs.push({
|
|
44
106
|
tool: "claude",
|
|
45
|
-
filepath: (0,
|
|
107
|
+
filepath: (0, import_node_path2.join)(config.outputPaths.claude, ".claude", "memories", `${rule.filename}.md`),
|
|
46
108
|
content: memoryContent
|
|
47
109
|
});
|
|
48
110
|
}
|
|
@@ -84,12 +146,12 @@ function generateMemoryFile(rule) {
|
|
|
84
146
|
}
|
|
85
147
|
|
|
86
148
|
// src/generators/cline.ts
|
|
87
|
-
var
|
|
149
|
+
var import_node_path3 = require("path");
|
|
88
150
|
async function generateClineConfig(rules, config) {
|
|
89
151
|
const outputs = [];
|
|
90
152
|
for (const rule of rules) {
|
|
91
153
|
const content = generateClineMarkdown(rule);
|
|
92
|
-
const filepath = (0,
|
|
154
|
+
const filepath = (0, import_node_path3.join)(config.outputPaths.cline, `${rule.filename}.md`);
|
|
93
155
|
outputs.push({
|
|
94
156
|
tool: "cline",
|
|
95
157
|
filepath,
|
|
@@ -103,13 +165,13 @@ function generateClineMarkdown(rule) {
|
|
|
103
165
|
}
|
|
104
166
|
|
|
105
167
|
// src/generators/copilot.ts
|
|
106
|
-
var
|
|
168
|
+
var import_node_path4 = require("path");
|
|
107
169
|
async function generateCopilotConfig(rules, config) {
|
|
108
170
|
const outputs = [];
|
|
109
171
|
for (const rule of rules) {
|
|
110
172
|
const content = generateCopilotMarkdown(rule);
|
|
111
173
|
const baseFilename = rule.filename.replace(/\.md$/, "");
|
|
112
|
-
const filepath = (0,
|
|
174
|
+
const filepath = (0, import_node_path4.join)(config.outputPaths.copilot, `${baseFilename}.instructions.md`);
|
|
113
175
|
outputs.push({
|
|
114
176
|
tool: "copilot",
|
|
115
177
|
filepath,
|
|
@@ -133,12 +195,12 @@ function generateCopilotMarkdown(rule) {
|
|
|
133
195
|
}
|
|
134
196
|
|
|
135
197
|
// src/generators/cursor.ts
|
|
136
|
-
var
|
|
198
|
+
var import_node_path5 = require("path");
|
|
137
199
|
async function generateCursorConfig(rules, config) {
|
|
138
200
|
const outputs = [];
|
|
139
201
|
for (const rule of rules) {
|
|
140
202
|
const content = generateCursorMarkdown(rule);
|
|
141
|
-
const filepath = (0,
|
|
203
|
+
const filepath = (0, import_node_path5.join)(config.outputPaths.cursor, `${rule.filename}.mdc`);
|
|
142
204
|
outputs.push({
|
|
143
205
|
tool: "cursor",
|
|
144
206
|
filepath,
|
|
@@ -152,7 +214,7 @@ function generateCursorMarkdown(rule) {
|
|
|
152
214
|
lines.push("---");
|
|
153
215
|
lines.push(`description: ${rule.frontmatter.description}`);
|
|
154
216
|
if (rule.frontmatter.globs.length > 0) {
|
|
155
|
-
lines.push(`globs:
|
|
217
|
+
lines.push(`globs: ${rule.frontmatter.globs.join(",")}`);
|
|
156
218
|
}
|
|
157
219
|
let ruletype;
|
|
158
220
|
if (rule.frontmatter.root === true) {
|
|
@@ -169,12 +231,12 @@ function generateCursorMarkdown(rule) {
|
|
|
169
231
|
}
|
|
170
232
|
|
|
171
233
|
// src/generators/roo.ts
|
|
172
|
-
var
|
|
234
|
+
var import_node_path6 = require("path");
|
|
173
235
|
async function generateRooConfig(rules, config) {
|
|
174
236
|
const outputs = [];
|
|
175
237
|
for (const rule of rules) {
|
|
176
238
|
const content = generateRooMarkdown(rule);
|
|
177
|
-
const filepath = (0,
|
|
239
|
+
const filepath = (0, import_node_path6.join)(config.outputPaths.roo, `${rule.filename}.md`);
|
|
178
240
|
outputs.push({
|
|
179
241
|
tool: "roo",
|
|
180
242
|
filepath,
|
|
@@ -187,56 +249,34 @@ function generateRooMarkdown(rule) {
|
|
|
187
249
|
return rule.content.trim();
|
|
188
250
|
}
|
|
189
251
|
|
|
190
|
-
// src/utils/config.ts
|
|
191
|
-
function getDefaultConfig() {
|
|
192
|
-
return {
|
|
193
|
-
aiRulesDir: ".rulesync",
|
|
194
|
-
outputPaths: {
|
|
195
|
-
copilot: ".github/instructions",
|
|
196
|
-
cursor: ".cursor/rules",
|
|
197
|
-
cline: ".clinerules",
|
|
198
|
-
claude: ".",
|
|
199
|
-
roo: ".roo/rules"
|
|
200
|
-
},
|
|
201
|
-
watchEnabled: false,
|
|
202
|
-
defaultTargets: ["copilot", "cursor", "cline", "claude", "roo"]
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
function resolveTargets(targets, config) {
|
|
206
|
-
if (targets.includes("*")) {
|
|
207
|
-
return config.defaultTargets;
|
|
208
|
-
}
|
|
209
|
-
return targets;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
252
|
// src/utils/file.ts
|
|
213
|
-
var
|
|
214
|
-
var
|
|
253
|
+
var import_promises2 = require("fs/promises");
|
|
254
|
+
var import_node_path7 = require("path");
|
|
215
255
|
async function ensureDir(dirPath) {
|
|
216
256
|
try {
|
|
217
|
-
await (0,
|
|
257
|
+
await (0, import_promises2.stat)(dirPath);
|
|
218
258
|
} catch {
|
|
219
|
-
await (0,
|
|
259
|
+
await (0, import_promises2.mkdir)(dirPath, { recursive: true });
|
|
220
260
|
}
|
|
221
261
|
}
|
|
222
262
|
async function readFileContent(filepath) {
|
|
223
|
-
return (0,
|
|
263
|
+
return (0, import_promises2.readFile)(filepath, "utf-8");
|
|
224
264
|
}
|
|
225
265
|
async function writeFileContent(filepath, content) {
|
|
226
|
-
await ensureDir((0,
|
|
227
|
-
await (0,
|
|
266
|
+
await ensureDir((0, import_node_path7.dirname)(filepath));
|
|
267
|
+
await (0, import_promises2.writeFile)(filepath, content, "utf-8");
|
|
228
268
|
}
|
|
229
269
|
async function findFiles(dir, extension = ".md") {
|
|
230
270
|
try {
|
|
231
|
-
const files = await (0,
|
|
232
|
-
return files.filter((file) => file.endsWith(extension)).map((file) => (0,
|
|
271
|
+
const files = await (0, import_promises2.readdir)(dir);
|
|
272
|
+
return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path7.join)(dir, file));
|
|
233
273
|
} catch {
|
|
234
274
|
return [];
|
|
235
275
|
}
|
|
236
276
|
}
|
|
237
277
|
async function fileExists(filepath) {
|
|
238
278
|
try {
|
|
239
|
-
await (0,
|
|
279
|
+
await (0, import_promises2.stat)(filepath);
|
|
240
280
|
return true;
|
|
241
281
|
} catch {
|
|
242
282
|
return false;
|
|
@@ -250,7 +290,7 @@ async function removeDirectory(dirPath) {
|
|
|
250
290
|
}
|
|
251
291
|
try {
|
|
252
292
|
if (await fileExists(dirPath)) {
|
|
253
|
-
await (0,
|
|
293
|
+
await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
|
|
254
294
|
}
|
|
255
295
|
} catch (error) {
|
|
256
296
|
console.warn(`Failed to remove directory ${dirPath}:`, error);
|
|
@@ -259,7 +299,7 @@ async function removeDirectory(dirPath) {
|
|
|
259
299
|
async function removeFile(filepath) {
|
|
260
300
|
try {
|
|
261
301
|
if (await fileExists(filepath)) {
|
|
262
|
-
await (0,
|
|
302
|
+
await (0, import_promises2.rm)(filepath);
|
|
263
303
|
}
|
|
264
304
|
} catch (error) {
|
|
265
305
|
console.warn(`Failed to remove file ${filepath}:`, error);
|
|
@@ -318,7 +358,7 @@ async function generateForTool(tool, rules, config) {
|
|
|
318
358
|
}
|
|
319
359
|
|
|
320
360
|
// src/core/parser.ts
|
|
321
|
-
var
|
|
361
|
+
var import_node_path8 = require("path");
|
|
322
362
|
var import_gray_matter = __toESM(require("gray-matter"));
|
|
323
363
|
async function parseRulesFromDirectory(aiRulesDir) {
|
|
324
364
|
const ruleFiles = await findFiles(aiRulesDir);
|
|
@@ -338,7 +378,7 @@ async function parseRuleFile(filepath) {
|
|
|
338
378
|
const parsed = (0, import_gray_matter.default)(content);
|
|
339
379
|
validateFrontmatter(parsed.data, filepath);
|
|
340
380
|
const frontmatter = parsed.data;
|
|
341
|
-
const filename = (0,
|
|
381
|
+
const filename = (0, import_node_path8.basename)(filepath, ".md");
|
|
342
382
|
return {
|
|
343
383
|
frontmatter,
|
|
344
384
|
content: parsed.content,
|
|
@@ -497,9 +537,9 @@ async function generateCommand(options = {}) {
|
|
|
497
537
|
|
|
498
538
|
// src/cli/commands/gitignore.ts
|
|
499
539
|
var import_node_fs = require("fs");
|
|
500
|
-
var
|
|
540
|
+
var import_node_path9 = require("path");
|
|
501
541
|
var gitignoreCommand = async () => {
|
|
502
|
-
const gitignorePath = (0,
|
|
542
|
+
const gitignorePath = (0, import_node_path9.join)(process.cwd(), ".gitignore");
|
|
503
543
|
const rulesFilesToIgnore = [
|
|
504
544
|
"# Generated by rulesync - AI tool configuration files",
|
|
505
545
|
".github/copilot-instructions.md",
|
|
@@ -539,7 +579,7 @@ ${linesToAdd.join("\n")}
|
|
|
539
579
|
};
|
|
540
580
|
|
|
541
581
|
// src/cli/commands/init.ts
|
|
542
|
-
var
|
|
582
|
+
var import_node_path10 = require("path");
|
|
543
583
|
async function initCommand() {
|
|
544
584
|
const aiRulesDir = ".rulesync";
|
|
545
585
|
console.log("Initializing rulesync...");
|
|
@@ -629,7 +669,7 @@ globs: ["src/**/*.ts"]
|
|
|
629
669
|
}
|
|
630
670
|
];
|
|
631
671
|
for (const file of sampleFiles) {
|
|
632
|
-
const filepath = (0,
|
|
672
|
+
const filepath = (0, import_node_path10.join)(aiRulesDir, file.filename);
|
|
633
673
|
if (!await fileExists(filepath)) {
|
|
634
674
|
await writeFileContent(filepath, file.content);
|
|
635
675
|
console.log(`Created ${filepath}`);
|
|
@@ -740,11 +780,11 @@ async function watchCommand() {
|
|
|
740
780
|
persistent: true
|
|
741
781
|
});
|
|
742
782
|
let isGenerating = false;
|
|
743
|
-
const handleChange = async (
|
|
783
|
+
const handleChange = async (path2) => {
|
|
744
784
|
if (isGenerating) return;
|
|
745
785
|
isGenerating = true;
|
|
746
786
|
console.log(`
|
|
747
|
-
\u{1F4DD} Detected change in ${
|
|
787
|
+
\u{1F4DD} Detected change in ${path2}`);
|
|
748
788
|
try {
|
|
749
789
|
await generateCommand({ verbose: false });
|
|
750
790
|
console.log("\u2705 Regenerated configuration files");
|
|
@@ -754,10 +794,10 @@ async function watchCommand() {
|
|
|
754
794
|
isGenerating = false;
|
|
755
795
|
}
|
|
756
796
|
};
|
|
757
|
-
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (
|
|
797
|
+
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path2) => {
|
|
758
798
|
console.log(`
|
|
759
|
-
\u{1F5D1}\uFE0F Removed ${
|
|
760
|
-
handleChange(
|
|
799
|
+
\u{1F5D1}\uFE0F Removed ${path2}`);
|
|
800
|
+
handleChange(path2);
|
|
761
801
|
}).on("error", (error) => {
|
|
762
802
|
console.error("\u274C Watcher error:", error);
|
|
763
803
|
});
|
|
@@ -772,6 +812,7 @@ async function watchCommand() {
|
|
|
772
812
|
var program = new import_commander.Command();
|
|
773
813
|
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.1.0");
|
|
774
814
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
815
|
+
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
775
816
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
776
817
|
program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claude", "Generate only for Claude Code").option("--delete", "Delete all existing files in output directories before generating").option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
777
818
|
const tools = [];
|
package/dist/index.mjs
CHANGED
|
@@ -3,6 +3,68 @@
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
|
+
// src/cli/commands/add.ts
|
|
7
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
8
|
+
import path from "path";
|
|
9
|
+
|
|
10
|
+
// src/utils/config.ts
|
|
11
|
+
function getDefaultConfig() {
|
|
12
|
+
return {
|
|
13
|
+
aiRulesDir: ".rulesync",
|
|
14
|
+
outputPaths: {
|
|
15
|
+
copilot: ".github/instructions",
|
|
16
|
+
cursor: ".cursor/rules",
|
|
17
|
+
cline: ".clinerules",
|
|
18
|
+
claude: ".",
|
|
19
|
+
roo: ".roo/rules"
|
|
20
|
+
},
|
|
21
|
+
watchEnabled: false,
|
|
22
|
+
defaultTargets: ["copilot", "cursor", "cline", "claude", "roo"]
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function resolveTargets(targets, config) {
|
|
26
|
+
if (targets.includes("*")) {
|
|
27
|
+
return config.defaultTargets;
|
|
28
|
+
}
|
|
29
|
+
return targets;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/cli/commands/add.ts
|
|
33
|
+
function sanitizeFilename(filename) {
|
|
34
|
+
return filename.endsWith(".md") ? filename.slice(0, -3) : filename;
|
|
35
|
+
}
|
|
36
|
+
function generateRuleTemplate(filename) {
|
|
37
|
+
return `---
|
|
38
|
+
root: false
|
|
39
|
+
targets: ["*"]
|
|
40
|
+
description: "Rules for ${filename}"
|
|
41
|
+
globs: []
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# ${filename.charAt(0).toUpperCase() + filename.slice(1)} Rules
|
|
45
|
+
|
|
46
|
+
Add your rules here.
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
async function addCommand(filename) {
|
|
50
|
+
try {
|
|
51
|
+
const config = getDefaultConfig();
|
|
52
|
+
const sanitizedFilename = sanitizeFilename(filename);
|
|
53
|
+
const rulesDir = config.aiRulesDir;
|
|
54
|
+
const filePath = path.join(rulesDir, `${sanitizedFilename}.md`);
|
|
55
|
+
await mkdir(rulesDir, { recursive: true });
|
|
56
|
+
const template = generateRuleTemplate(sanitizedFilename);
|
|
57
|
+
await writeFile(filePath, template, "utf8");
|
|
58
|
+
console.log(`\u2705 Created rule file: ${filePath}`);
|
|
59
|
+
console.log(`\u{1F4DD} Edit the file to customize your rules.`);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(
|
|
62
|
+
`\u274C Failed to create rule file: ${error instanceof Error ? error.message : String(error)}`
|
|
63
|
+
);
|
|
64
|
+
process.exit(3);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
6
68
|
// src/generators/claude.ts
|
|
7
69
|
import { join } from "path";
|
|
8
70
|
async function generateClaudeConfig(rules, config) {
|
|
@@ -129,7 +191,7 @@ function generateCursorMarkdown(rule) {
|
|
|
129
191
|
lines.push("---");
|
|
130
192
|
lines.push(`description: ${rule.frontmatter.description}`);
|
|
131
193
|
if (rule.frontmatter.globs.length > 0) {
|
|
132
|
-
lines.push(`globs:
|
|
194
|
+
lines.push(`globs: ${rule.frontmatter.globs.join(",")}`);
|
|
133
195
|
}
|
|
134
196
|
let ruletype;
|
|
135
197
|
if (rule.frontmatter.root === true) {
|
|
@@ -164,36 +226,14 @@ function generateRooMarkdown(rule) {
|
|
|
164
226
|
return rule.content.trim();
|
|
165
227
|
}
|
|
166
228
|
|
|
167
|
-
// src/utils/config.ts
|
|
168
|
-
function getDefaultConfig() {
|
|
169
|
-
return {
|
|
170
|
-
aiRulesDir: ".rulesync",
|
|
171
|
-
outputPaths: {
|
|
172
|
-
copilot: ".github/instructions",
|
|
173
|
-
cursor: ".cursor/rules",
|
|
174
|
-
cline: ".clinerules",
|
|
175
|
-
claude: ".",
|
|
176
|
-
roo: ".roo/rules"
|
|
177
|
-
},
|
|
178
|
-
watchEnabled: false,
|
|
179
|
-
defaultTargets: ["copilot", "cursor", "cline", "claude", "roo"]
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
function resolveTargets(targets, config) {
|
|
183
|
-
if (targets.includes("*")) {
|
|
184
|
-
return config.defaultTargets;
|
|
185
|
-
}
|
|
186
|
-
return targets;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
229
|
// src/utils/file.ts
|
|
190
|
-
import { mkdir, readdir, readFile, rm, stat, writeFile } from "fs/promises";
|
|
230
|
+
import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
|
|
191
231
|
import { dirname, join as join6 } from "path";
|
|
192
232
|
async function ensureDir(dirPath) {
|
|
193
233
|
try {
|
|
194
234
|
await stat(dirPath);
|
|
195
235
|
} catch {
|
|
196
|
-
await
|
|
236
|
+
await mkdir2(dirPath, { recursive: true });
|
|
197
237
|
}
|
|
198
238
|
}
|
|
199
239
|
async function readFileContent(filepath) {
|
|
@@ -201,7 +241,7 @@ async function readFileContent(filepath) {
|
|
|
201
241
|
}
|
|
202
242
|
async function writeFileContent(filepath, content) {
|
|
203
243
|
await ensureDir(dirname(filepath));
|
|
204
|
-
await
|
|
244
|
+
await writeFile2(filepath, content, "utf-8");
|
|
205
245
|
}
|
|
206
246
|
async function findFiles(dir, extension = ".md") {
|
|
207
247
|
try {
|
|
@@ -717,11 +757,11 @@ async function watchCommand() {
|
|
|
717
757
|
persistent: true
|
|
718
758
|
});
|
|
719
759
|
let isGenerating = false;
|
|
720
|
-
const handleChange = async (
|
|
760
|
+
const handleChange = async (path2) => {
|
|
721
761
|
if (isGenerating) return;
|
|
722
762
|
isGenerating = true;
|
|
723
763
|
console.log(`
|
|
724
|
-
\u{1F4DD} Detected change in ${
|
|
764
|
+
\u{1F4DD} Detected change in ${path2}`);
|
|
725
765
|
try {
|
|
726
766
|
await generateCommand({ verbose: false });
|
|
727
767
|
console.log("\u2705 Regenerated configuration files");
|
|
@@ -731,10 +771,10 @@ async function watchCommand() {
|
|
|
731
771
|
isGenerating = false;
|
|
732
772
|
}
|
|
733
773
|
};
|
|
734
|
-
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (
|
|
774
|
+
watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path2) => {
|
|
735
775
|
console.log(`
|
|
736
|
-
\u{1F5D1}\uFE0F Removed ${
|
|
737
|
-
handleChange(
|
|
776
|
+
\u{1F5D1}\uFE0F Removed ${path2}`);
|
|
777
|
+
handleChange(path2);
|
|
738
778
|
}).on("error", (error) => {
|
|
739
779
|
console.error("\u274C Watcher error:", error);
|
|
740
780
|
});
|
|
@@ -749,6 +789,7 @@ async function watchCommand() {
|
|
|
749
789
|
var program = new Command();
|
|
750
790
|
program.name("rulesync").description("Unified AI rules management CLI tool").version("0.1.0");
|
|
751
791
|
program.command("init").description("Initialize rulesync in current directory").action(initCommand);
|
|
792
|
+
program.command("add <filename>").description("Add a new rule file").action(addCommand);
|
|
752
793
|
program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
|
|
753
794
|
program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claude", "Generate only for Claude Code").option("--delete", "Delete all existing files in output directories before generating").option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
754
795
|
const tools = [];
|
package/package.json
CHANGED