rulesync 0.12.0 → 0.13.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 ADDED
@@ -0,0 +1,290 @@
1
+ # rulesync
2
+
3
+ [![CI](https://github.com/dyoshikawa/rulesync/actions/workflows/ci.yml/badge.svg)](https://github.com/dyoshikawa/rulesync/actions/workflows/ci.yml)
4
+ [![npm version](https://badge.fury.io/js/rulesync.svg)](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
32
+ npx rulesync init
33
33
  ```
34
34
 
35
- 2. **Create an overview file** (`.rulesync/overview.md`):
36
- ```markdown
37
- ---
38
- root: true
39
- targets: ["*"]
40
- description: "Project overview and development philosophy"
41
- globs: ["src/**/*.ts", "src/**/*.js"]
42
- ---
43
-
44
- # Project Development Guidelines
45
-
46
- This is a TypeScript/JavaScript project following clean architecture principles.
47
- We prioritize code readability, maintainability, and type safety.
48
-
49
- ## Tech Stack
50
- - TypeScript for type safety
51
- - Node.js runtime
52
- - Modern ES6+ features
53
- ```
54
-
55
- 3. **Create detail rules** (`.rulesync/coding-rules.md`):
56
- ```markdown
57
- ---
58
- root: false
59
- targets: ["copilot", "cursor", "cline"]
60
- description: "TypeScript coding standards and best practices"
61
- globs: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
62
- ---
63
-
64
- # TypeScript Coding Rules
65
-
66
- ## Code Style
67
- - Use strict TypeScript configuration
68
- - Prefer `const` over `let` when possible
69
- - Use meaningful, descriptive variable names
70
- - Write JSDoc comments for public APIs
71
-
72
- ## Type Definitions
73
- - Prefer interfaces over types for object shapes
74
- - Use union types for controlled values
75
- - Avoid `any` type - use `unknown` instead
76
- - Define return types for functions explicitly
77
-
78
- ## Error Handling
79
- - Use Result pattern for error handling
80
- - Throw errors only for unexpected conditions
81
- - Validate input parameters at function boundaries
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
- 4. **Generate configuration files:**
42
+ 3. **Generate tool-specific configuration files:**
85
43
  ```bash
86
- rulesync generate
44
+ npx rulesync generate
87
45
  ```
88
46
 
89
- 5. **Optional: Add generated files to .gitignore:**
47
+ 4. **Optional: Add generated files to .gitignore:**
90
48
  ```bash
91
- rulesync gitignore
49
+ npx rulesync gitignore
92
50
  ```
93
51
 
94
- This will create tool-specific configuration files that your AI coding assistants can use automatically.
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
@@ -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 import_node_path = require("path");
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, import_node_path.join)(config.outputPaths.claude, "CLAUDE.md"),
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, import_node_path.join)(config.outputPaths.claude, ".claude", "memories", `${rule.filename}.md`),
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 import_node_path2 = require("path");
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, import_node_path2.join)(config.outputPaths.cline, `${rule.filename}.md`);
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 import_node_path3 = require("path");
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, import_node_path3.join)(config.outputPaths.copilot, `${baseFilename}.instructions.md`);
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 import_node_path4 = require("path");
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, import_node_path4.join)(config.outputPaths.cursor, `${rule.filename}.mdc`);
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,
@@ -169,12 +231,12 @@ function generateCursorMarkdown(rule) {
169
231
  }
170
232
 
171
233
  // src/generators/roo.ts
172
- var import_node_path5 = require("path");
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, import_node_path5.join)(config.outputPaths.roo, `${rule.filename}.md`);
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 import_promises = require("fs/promises");
214
- var import_node_path6 = require("path");
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, import_promises.stat)(dirPath);
257
+ await (0, import_promises2.stat)(dirPath);
218
258
  } catch {
219
- await (0, import_promises.mkdir)(dirPath, { recursive: true });
259
+ await (0, import_promises2.mkdir)(dirPath, { recursive: true });
220
260
  }
221
261
  }
222
262
  async function readFileContent(filepath) {
223
- return (0, import_promises.readFile)(filepath, "utf-8");
263
+ return (0, import_promises2.readFile)(filepath, "utf-8");
224
264
  }
225
265
  async function writeFileContent(filepath, content) {
226
- await ensureDir((0, import_node_path6.dirname)(filepath));
227
- await (0, import_promises.writeFile)(filepath, content, "utf-8");
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, import_promises.readdir)(dir);
232
- return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path6.join)(dir, file));
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, import_promises.stat)(filepath);
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, import_promises.rm)(dirPath, { recursive: true, force: true });
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, import_promises.rm)(filepath);
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 import_node_path7 = require("path");
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, import_node_path7.basename)(filepath, ".md");
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 import_node_path8 = require("path");
540
+ var import_node_path9 = require("path");
501
541
  var gitignoreCommand = async () => {
502
- const gitignorePath = (0, import_node_path8.join)(process.cwd(), ".gitignore");
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 import_node_path9 = require("path");
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, import_node_path9.join)(aiRulesDir, file.filename);
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 (path) => {
783
+ const handleChange = async (path2) => {
744
784
  if (isGenerating) return;
745
785
  isGenerating = true;
746
786
  console.log(`
747
- \u{1F4DD} Detected change in ${path}`);
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", (path) => {
797
+ watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path2) => {
758
798
  console.log(`
759
- \u{1F5D1}\uFE0F Removed ${path}`);
760
- handleChange(path);
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) {
@@ -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 mkdir(dirPath, { recursive: true });
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 writeFile(filepath, content, "utf-8");
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 (path) => {
760
+ const handleChange = async (path2) => {
721
761
  if (isGenerating) return;
722
762
  isGenerating = true;
723
763
  console.log(`
724
- \u{1F4DD} Detected change in ${path}`);
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", (path) => {
774
+ watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path2) => {
735
775
  console.log(`
736
- \u{1F5D1}\uFE0F Removed ${path}`);
737
- handleChange(path);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rulesync",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",