rulesync 0.33.0 → 0.34.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 CHANGED
@@ -16,6 +16,7 @@ rulesyncは以下のAI開発ツールの**生成**と**インポート**の両
16
16
  - **Cline Rules** (`.clinerules/*.md` + `.cline/instructions.md`)
17
17
  - **Claude Code Memory** (`./CLAUDE.md` + `.claude/memories/*.md`)
18
18
  - **Roo Code Rules** (`.roo/rules/*.md` + `.roo/instructions.md`)
19
+ - **Gemini CLI** (`GEMINI.md` + `.gemini/memories/*.md`)
19
20
 
20
21
  ## インストール
21
22
 
@@ -68,6 +69,7 @@ yarn global add rulesync
68
69
  npx rulesync import --copilot # .github/copilot-instructions.mdから
69
70
  npx rulesync import --cline # .cline/instructions.mdから
70
71
  npx rulesync import --roo # .roo/instructions.mdから
72
+ npx rulesync import --geminicli # GEMINI.mdと.gemini/memories/*.mdから
71
73
  ```
72
74
 
73
75
  2. **`.rulesync/`ディレクトリのインポートされたルールを確認・編集**
@@ -93,9 +95,10 @@ AI開発ツールは新しいツールが頻繁に登場し、急速に進化し
93
95
  - Cursor:リファクタリング
94
96
  - Claude Code:アーキテクチャ設計
95
97
  - Cline:デバッグ支援
98
+ - Gemini CLI:知的コード解析
96
99
 
97
100
  ### 🔓 **ベンダーロックインなし**
98
- ベンダーロックインを完全に回避できます。rulesyncの使用を停止することを決定した場合でも、生成されたルールファイル(`.github/instructions/`、`.cursor/rules/`、`.clinerules/`、`CLAUDE.md`など)をそのまま使い続けることができます。
101
+ ベンダーロックインを完全に回避できます。rulesyncの使用を停止することを決定した場合でも、生成されたルールファイル(`.github/instructions/`、`.cursor/rules/`、`.clinerules/`、`CLAUDE.md`、`GEMINI.md`など)をそのまま使い続けることができます。
99
102
 
100
103
  ### 🎯 **ツール間の一貫性**
101
104
  すべてのAIツールに一貫したルールを適用し、チーム全体のコード品質と開発体験を向上させます。
@@ -171,6 +174,7 @@ rulesyncは2レベルのルールシステムを使用します:
171
174
  | **GitHub Copilot** | 標準フォーマット | 標準フォーマット | すべてのルールがフロントマター付きの同じフォーマットを使用 |
172
175
  | **Cline** | 標準フォーマット | 標準フォーマット | すべてのルールがプレーンMarkdownフォーマットを使用 |
173
176
  | **Roo Code** | 標準フォーマット | 標準フォーマット | すべてのルールが説明ヘッダー付きのプレーンMarkdownフォーマットを使用 |
177
+ | **Gemini CLI** | `GEMINI.md` | `.gemini/memories/*.md` | GEMINI.mdがメモリファイルへの`@filename`参照を含む |
174
178
 
175
179
  ### 3. 設定ファイルの生成
176
180
 
@@ -184,6 +188,7 @@ npx rulesync generate --cursor
184
188
  npx rulesync generate --cline
185
189
  npx rulesync generate --claudecode
186
190
  npx rulesync generate --roo
191
+ npx rulesync generate --geminicli
187
192
 
188
193
  # クリーンビルド(既存ファイルを最初に削除)
189
194
  npx rulesync generate --delete
@@ -205,7 +210,7 @@ npx rulesync generate --base-dir ./apps/web,./apps/api,./packages/shared
205
210
 
206
211
  - `--delete`: 新しいファイルを作成する前に既存の生成済みファイルをすべて削除
207
212
  - `--verbose`: 生成プロセス中に詳細出力を表示
208
- - `--copilot`, `--cursor`, `--cline`, `--claudecode`, `--roo`: 指定されたツールのみ生成
213
+ - `--copilot`, `--cursor`, `--cline`, `--claudecode`, `--roo`, `--geminicli`: 指定されたツールのみ生成
209
214
  - `--base-dir <paths>`: 指定されたベースディレクトリに設定ファイルを生成(複数パスの場合はカンマ区切り)。異なるプロジェクトディレクトリにツール固有の設定を生成したいmonorepoセットアップに便利。
210
215
 
211
216
  ### 4. 既存設定のインポート
@@ -219,6 +224,7 @@ npx rulesync import --cursor # .cursorrulesと.cursor/rules/*.mdからイン
219
224
  npx rulesync import --copilot # .github/copilot-instructions.mdと.github/instructions/*.instructions.mdからインポート
220
225
  npx rulesync import --cline # .cline/instructions.mdからインポート
221
226
  npx rulesync import --roo # .roo/instructions.mdからインポート
227
+ npx rulesync import --geminicli # GEMINI.mdと.gemini/memories/*.mdからインポート
222
228
 
223
229
  # 複数のツールからインポート
224
230
  npx rulesync import --claudecode --cursor --copilot
@@ -326,6 +332,7 @@ globs: "**/*.ts,**/*.tsx"
326
332
  | **Cline** | `.clinerules/*.md` | プレーンMarkdown | 両レベルとも同じフォーマットを使用 |
327
333
  | **Claude Code** | `./CLAUDE.md` (ルート)<br>`.claude/memories/*.md` (非ルート) | プレーンMarkdown | ルートはCLAUDE.mdに移動<br>非ルートは別メモリファイルに移動<br>CLAUDE.mdは`@filename`参照を含む |
328
334
  | **Roo Code** | `.roo/rules/*.md` | プレーンMarkdown | 両レベルとも説明ヘッダー付きの同じフォーマットを使用 |
335
+ | **Gemini CLI** | `GEMINI.md` (ルート)<br>`.gemini/memories/*.md` (非ルート) | プレーンMarkdown | ルートはGEMINI.mdに移動<br>非ルートは別メモリファイルに移動<br>GEMINI.mdは`@filename`参照を含む |
329
336
 
330
337
  ## バリデーション
331
338
 
package/README.md CHANGED
@@ -16,6 +16,7 @@ rulesync supports both **generation** and **import** for the following AI develo
16
16
  - **Cline Rules** (`.clinerules/*.md` + `.cline/instructions.md`)
17
17
  - **Claude Code Memory** (`./CLAUDE.md` + `.claude/memories/*.md`)
18
18
  - **Roo Code Rules** (`.roo/rules/*.md` + `.roo/instructions.md`)
19
+ - **Gemini CLI** (`GEMINI.md` + `.gemini/memories/*.md`)
19
20
 
20
21
  ## Installation
21
22
 
@@ -68,6 +69,7 @@ If you already have AI tool configurations, you can import them into rulesync fo
68
69
  npx rulesync import --copilot # From .github/copilot-instructions.md
69
70
  npx rulesync import --cline # From .cline/instructions.md
70
71
  npx rulesync import --roo # From .roo/instructions.md
72
+ npx rulesync import --geminicli # From GEMINI.md and .gemini/memories/*.md
71
73
  ```
72
74
 
73
75
  2. **Review and edit** the imported rules in `.rulesync/` directory
@@ -93,9 +95,10 @@ Enable hybrid development workflows combining multiple AI tools:
93
95
  - Cursor for refactoring
94
96
  - Claude Code for architecture design
95
97
  - Cline for debugging assistance
98
+ - Gemini CLI for intelligent code analysis
96
99
 
97
100
  ### 🔓 **No Vendor Lock-in**
98
- Avoid vendor lock-in completely. If you decide to stop using rulesync, you can continue using the generated rule files (`.github/instructions/`, `.cursor/rules/`, `.clinerules/`, `CLAUDE.md`, etc.) as-is.
101
+ Avoid vendor lock-in completely. If you decide to stop using rulesync, you can continue using the generated rule files (`.github/instructions/`, `.cursor/rules/`, `.clinerules/`, `CLAUDE.md`, `GEMINI.md`, etc.) as-is.
99
102
 
100
103
  ### 🎯 **Consistency Across Tools**
101
104
  Apply consistent rules across all AI tools, improving code quality and development experience for the entire team.
@@ -171,6 +174,7 @@ Each AI tool handles rule levels differently:
171
174
  | **GitHub Copilot** | Standard format | Standard format | All rules use same format with frontmatter |
172
175
  | **Cline** | Standard format | Standard format | All rules use plain Markdown format |
173
176
  | **Roo Code** | Standard format | Standard format | All rules use plain Markdown format with description header |
177
+ | **Gemini CLI** | `GEMINI.md` | `.gemini/memories/*.md` | GEMINI.md includes `@filename` references to memory files |
174
178
 
175
179
  ### 3. Generate Configuration Files
176
180
 
@@ -184,6 +188,7 @@ npx rulesync generate --cursor
184
188
  npx rulesync generate --cline
185
189
  npx rulesync generate --claudecode
186
190
  npx rulesync generate --roo
191
+ npx rulesync generate --geminicli
187
192
 
188
193
  # Clean build (delete existing files first)
189
194
  npx rulesync generate --delete
@@ -205,7 +210,7 @@ npx rulesync generate --base-dir ./apps/web,./apps/api,./packages/shared
205
210
 
206
211
  - `--delete`: Remove all existing generated files before creating new ones
207
212
  - `--verbose`: Show detailed output during generation process
208
- - `--copilot`, `--cursor`, `--cline`, `--claudecode`, `--roo`: Generate only for specified tools
213
+ - `--copilot`, `--cursor`, `--cline`, `--claudecode`, `--roo`, `--geminicli`: Generate only for specified tools
209
214
  - `--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.
210
215
 
211
216
  ### 4. Import Existing Configurations
@@ -219,6 +224,7 @@ npx rulesync import --cursor # Import from .cursorrules and .cursor/rules/*.
219
224
  npx rulesync import --copilot # Import from .github/copilot-instructions.md and .github/instructions/*.instructions.md
220
225
  npx rulesync import --cline # Import from .cline/instructions.md
221
226
  npx rulesync import --roo # Import from .roo/instructions.md
227
+ npx rulesync import --geminicli # Import from GEMINI.md and .gemini/memories/*.md
222
228
 
223
229
  # Import from multiple tools
224
230
  npx rulesync import --claudecode --cursor --copilot
@@ -326,6 +332,7 @@ globs: "**/*.ts,**/*.tsx"
326
332
  | **Cline** | `.clinerules/*.md` | Plain Markdown | Both levels use same format |
327
333
  | **Claude Code** | `./CLAUDE.md` (root)<br>`.claude/memories/*.md` (non-root) | Plain Markdown | Root goes to CLAUDE.md<br>Non-root go to separate memory files<br>CLAUDE.md includes `@filename` references |
328
334
  | **Roo Code** | `.roo/rules/*.md` | Plain Markdown | Both levels use same format with description header |
335
+ | **Gemini CLI** | `GEMINI.md` (root)<br>`.gemini/memories/*.md` (non-root) | Plain Markdown | Root goes to GEMINI.md<br>Non-root go to separate memory files<br>GEMINI.md includes `@filename` references |
329
336
 
330
337
  ## Validation
331
338
 
package/dist/index.js CHANGED
@@ -39,10 +39,11 @@ function getDefaultConfig() {
39
39
  cursor: ".cursor/rules",
40
40
  cline: ".clinerules",
41
41
  claudecode: ".",
42
- roo: ".roo/rules"
42
+ roo: ".roo/rules",
43
+ geminicli: ".gemini/memories"
43
44
  },
44
45
  watchEnabled: false,
45
- defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo"]
46
+ defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli"]
46
47
  };
47
48
  }
48
49
  function resolveTargets(targets, config) {
@@ -226,14 +227,67 @@ function generateCursorMarkdown(rule) {
226
227
  return lines.join("\n");
227
228
  }
228
229
 
229
- // src/generators/roo.ts
230
+ // src/generators/geminicli.ts
230
231
  var import_node_path6 = require("path");
232
+ async function generateGeminiConfig(rules, config, baseDir) {
233
+ const outputs = [];
234
+ const rootRule = rules.find((rule) => rule.frontmatter.root === true);
235
+ const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
236
+ for (const rule of memoryRules) {
237
+ const content = generateGeminiMemoryMarkdown(rule);
238
+ const outputDir = baseDir ? (0, import_node_path6.join)(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
239
+ const filepath = (0, import_node_path6.join)(outputDir, `${rule.filename}.md`);
240
+ outputs.push({
241
+ tool: "geminicli",
242
+ filepath,
243
+ content
244
+ });
245
+ }
246
+ const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
247
+ const rootFilepath = baseDir ? (0, import_node_path6.join)(baseDir, "GEMINI.md") : "GEMINI.md";
248
+ outputs.push({
249
+ tool: "geminicli",
250
+ filepath: rootFilepath,
251
+ content: rootContent
252
+ });
253
+ return outputs;
254
+ }
255
+ function generateGeminiMemoryMarkdown(rule) {
256
+ return rule.content.trim();
257
+ }
258
+ function generateGeminiRootMarkdown(rootRule, memoryRules, _baseDir) {
259
+ const lines = [];
260
+ if (memoryRules.length > 0) {
261
+ lines.push("Please also reference the following documents as needed:");
262
+ lines.push("");
263
+ lines.push("| Document | Description | File Patterns |");
264
+ lines.push("|----------|-------------|---------------|");
265
+ for (const rule of memoryRules) {
266
+ const relativePath = `@.gemini/memories/${rule.filename}.md`;
267
+ const filePatterns = rule.frontmatter.globs.length > 0 ? rule.frontmatter.globs.join(", ") : "-";
268
+ lines.push(`| ${relativePath} | ${rule.frontmatter.description} | ${filePatterns} |`);
269
+ }
270
+ lines.push("");
271
+ lines.push("");
272
+ }
273
+ if (rootRule) {
274
+ lines.push(rootRule.content.trim());
275
+ } else if (memoryRules.length === 0) {
276
+ lines.push("# Gemini CLI Configuration");
277
+ lines.push("");
278
+ lines.push("No configuration rules have been defined yet.");
279
+ }
280
+ return lines.join("\n");
281
+ }
282
+
283
+ // src/generators/roo.ts
284
+ var import_node_path7 = require("path");
231
285
  async function generateRooConfig(rules, config, baseDir) {
232
286
  const outputs = [];
233
287
  for (const rule of rules) {
234
288
  const content = generateRooMarkdown(rule);
235
- const outputDir = baseDir ? (0, import_node_path6.join)(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
236
- const filepath = (0, import_node_path6.join)(outputDir, `${rule.filename}.md`);
289
+ const outputDir = baseDir ? (0, import_node_path7.join)(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
290
+ const filepath = (0, import_node_path7.join)(outputDir, `${rule.filename}.md`);
237
291
  outputs.push({
238
292
  tool: "roo",
239
293
  filepath,
@@ -248,7 +302,7 @@ function generateRooMarkdown(rule) {
248
302
 
249
303
  // src/utils/file.ts
250
304
  var import_promises2 = require("fs/promises");
251
- var import_node_path7 = require("path");
305
+ var import_node_path8 = require("path");
252
306
  async function ensureDir(dirPath) {
253
307
  try {
254
308
  await (0, import_promises2.stat)(dirPath);
@@ -260,13 +314,13 @@ async function readFileContent(filepath) {
260
314
  return (0, import_promises2.readFile)(filepath, "utf-8");
261
315
  }
262
316
  async function writeFileContent(filepath, content) {
263
- await ensureDir((0, import_node_path7.dirname)(filepath));
317
+ await ensureDir((0, import_node_path8.dirname)(filepath));
264
318
  await (0, import_promises2.writeFile)(filepath, content, "utf-8");
265
319
  }
266
320
  async function findFiles(dir, extension = ".md") {
267
321
  try {
268
322
  const files = await (0, import_promises2.readdir)(dir);
269
- return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path7.join)(dir, file));
323
+ return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path8.join)(dir, file));
270
324
  } catch {
271
325
  return [];
272
326
  }
@@ -354,6 +408,8 @@ async function generateForTool(tool, rules, config, baseDir) {
354
408
  return await generateClaudecodeConfig(rules, config, baseDir);
355
409
  case "roo":
356
410
  return generateRooConfig(rules, config, baseDir);
411
+ case "geminicli":
412
+ return generateGeminiConfig(rules, config, baseDir);
357
413
  default:
358
414
  console.warn(`Unknown tool: ${tool}`);
359
415
  return null;
@@ -361,7 +417,7 @@ async function generateForTool(tool, rules, config, baseDir) {
361
417
  }
362
418
 
363
419
  // src/core/parser.ts
364
- var import_node_path8 = require("path");
420
+ var import_node_path9 = require("path");
365
421
  var import_gray_matter = __toESM(require("gray-matter"));
366
422
  async function parseRulesFromDirectory(aiRulesDir) {
367
423
  const ruleFiles = await findFiles(aiRulesDir);
@@ -394,7 +450,7 @@ async function parseRuleFile(filepath) {
394
450
  const parsed = (0, import_gray_matter.default)(content);
395
451
  validateFrontmatter(parsed.data, filepath);
396
452
  const frontmatter = parsed.data;
397
- const filename = (0, import_node_path8.basename)(filepath, ".md");
453
+ const filename = (0, import_node_path9.basename)(filepath, ".md");
398
454
  return {
399
455
  frontmatter,
400
456
  content: parsed.content,
@@ -435,7 +491,7 @@ function validateFrontmatter(data, filepath) {
435
491
  `Invalid "targets" field in ${filepath}: must be an array, got ${typeof obj.targets}`
436
492
  );
437
493
  }
438
- const validTargets = ["copilot", "cursor", "cline", "claudecode", "roo", "*"];
494
+ const validTargets = ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli", "*"];
439
495
  for (const target of obj.targets) {
440
496
  if (typeof target !== "string" || !validTargets.includes(target)) {
441
497
  throw new Error(
@@ -567,6 +623,9 @@ async function generateCommand(options = {}) {
567
623
  case "roo":
568
624
  deleteTasks.push(removeDirectory(config.outputPaths.roo));
569
625
  break;
626
+ case "geminicli":
627
+ deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
628
+ break;
570
629
  }
571
630
  }
572
631
  await Promise.all(deleteTasks);
@@ -607,9 +666,9 @@ Generating configurations for base directory: ${baseDir}`);
607
666
 
608
667
  // src/cli/commands/gitignore.ts
609
668
  var import_node_fs = require("fs");
610
- var import_node_path9 = require("path");
669
+ var import_node_path10 = require("path");
611
670
  var gitignoreCommand = async () => {
612
- const gitignorePath = (0, import_node_path9.join)(process.cwd(), ".gitignore");
671
+ const gitignorePath = (0, import_node_path10.join)(process.cwd(), ".gitignore");
613
672
  const rulesFilesToIgnore = [
614
673
  "# Generated by rulesync - AI tool configuration files",
615
674
  "**/.github/copilot-instructions.md",
@@ -618,7 +677,9 @@ var gitignoreCommand = async () => {
618
677
  "**/.clinerules/",
619
678
  "**/CLAUDE.md",
620
679
  "**/.claude/memories/",
621
- "**/.roo/rules/"
680
+ "**/.roo/rules/",
681
+ "**/GEMINI.md",
682
+ "**/.gemini/memories/"
622
683
  ];
623
684
  let gitignoreContent = "";
624
685
  if ((0, import_node_fs.existsSync)(gitignorePath)) {
@@ -649,15 +710,15 @@ ${linesToAdd.join("\n")}
649
710
  };
650
711
 
651
712
  // src/core/importer.ts
652
- var import_node_path15 = require("path");
713
+ var import_node_path16 = require("path");
653
714
  var import_gray_matter4 = __toESM(require("gray-matter"));
654
715
 
655
716
  // src/parsers/claudecode.ts
656
- var import_node_path10 = require("path");
717
+ var import_node_path11 = require("path");
657
718
  async function parseClaudeConfiguration(baseDir = process.cwd()) {
658
719
  const errors = [];
659
720
  const rules = [];
660
- const claudeFilePath = (0, import_node_path10.join)(baseDir, "CLAUDE.md");
721
+ const claudeFilePath = (0, import_node_path11.join)(baseDir, "CLAUDE.md");
661
722
  if (!await fileExists(claudeFilePath)) {
662
723
  errors.push("CLAUDE.md file not found");
663
724
  return { rules, errors };
@@ -668,7 +729,7 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
668
729
  if (mainRule) {
669
730
  rules.push(mainRule);
670
731
  }
671
- const memoryDir = (0, import_node_path10.join)(baseDir, ".claude", "memories");
732
+ const memoryDir = (0, import_node_path11.join)(baseDir, ".claude", "memories");
672
733
  if (await fileExists(memoryDir)) {
673
734
  const memoryRules = await parseClaudeMemoryFiles(memoryDir);
674
735
  rules.push(...memoryRules);
@@ -714,10 +775,10 @@ async function parseClaudeMemoryFiles(memoryDir) {
714
775
  const files = await readdir2(memoryDir);
715
776
  for (const file of files) {
716
777
  if (file.endsWith(".md")) {
717
- const filePath = (0, import_node_path10.join)(memoryDir, file);
778
+ const filePath = (0, import_node_path11.join)(memoryDir, file);
718
779
  const content = await readFileContent(filePath);
719
780
  if (content.trim()) {
720
- const filename = (0, import_node_path10.basename)(file, ".md");
781
+ const filename = (0, import_node_path11.basename)(file, ".md");
721
782
  const frontmatter = {
722
783
  root: false,
723
784
  targets: ["claudecode"],
@@ -739,11 +800,11 @@ async function parseClaudeMemoryFiles(memoryDir) {
739
800
  }
740
801
 
741
802
  // src/parsers/cline.ts
742
- var import_node_path11 = require("path");
803
+ var import_node_path12 = require("path");
743
804
  async function parseClineConfiguration(baseDir = process.cwd()) {
744
805
  const errors = [];
745
806
  const rules = [];
746
- const clineFilePath = (0, import_node_path11.join)(baseDir, ".cline", "instructions.md");
807
+ const clineFilePath = (0, import_node_path12.join)(baseDir, ".cline", "instructions.md");
747
808
  if (!await fileExists(clineFilePath)) {
748
809
  errors.push(".cline/instructions.md file not found");
749
810
  return { rules, errors };
@@ -772,12 +833,12 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
772
833
  }
773
834
 
774
835
  // src/parsers/copilot.ts
775
- var import_node_path12 = require("path");
836
+ var import_node_path13 = require("path");
776
837
  var import_gray_matter2 = __toESM(require("gray-matter"));
777
838
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
778
839
  const errors = [];
779
840
  const rules = [];
780
- const copilotFilePath = (0, import_node_path12.join)(baseDir, ".github", "copilot-instructions.md");
841
+ const copilotFilePath = (0, import_node_path13.join)(baseDir, ".github", "copilot-instructions.md");
781
842
  if (await fileExists(copilotFilePath)) {
782
843
  try {
783
844
  const rawContent = await readFileContent(copilotFilePath);
@@ -802,19 +863,19 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
802
863
  errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
803
864
  }
804
865
  }
805
- const instructionsDir = (0, import_node_path12.join)(baseDir, ".github", "instructions");
866
+ const instructionsDir = (0, import_node_path13.join)(baseDir, ".github", "instructions");
806
867
  if (await fileExists(instructionsDir)) {
807
868
  try {
808
869
  const { readdir: readdir2 } = await import("fs/promises");
809
870
  const files = await readdir2(instructionsDir);
810
871
  for (const file of files) {
811
872
  if (file.endsWith(".instructions.md")) {
812
- const filePath = (0, import_node_path12.join)(instructionsDir, file);
873
+ const filePath = (0, import_node_path13.join)(instructionsDir, file);
813
874
  const rawContent = await readFileContent(filePath);
814
875
  const parsed = (0, import_gray_matter2.default)(rawContent);
815
876
  const content = parsed.content.trim();
816
877
  if (content) {
817
- const filename = (0, import_node_path12.basename)(file, ".instructions.md");
878
+ const filename = (0, import_node_path13.basename)(file, ".instructions.md");
818
879
  const frontmatter = {
819
880
  root: false,
820
881
  targets: ["copilot"],
@@ -844,7 +905,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
844
905
  }
845
906
 
846
907
  // src/parsers/cursor.ts
847
- var import_node_path13 = require("path");
908
+ var import_node_path14 = require("path");
848
909
  var import_gray_matter3 = __toESM(require("gray-matter"));
849
910
  var import_js_yaml = __toESM(require("js-yaml"));
850
911
  var customMatterOptions = {
@@ -868,7 +929,7 @@ var customMatterOptions = {
868
929
  async function parseCursorConfiguration(baseDir = process.cwd()) {
869
930
  const errors = [];
870
931
  const rules = [];
871
- const cursorFilePath = (0, import_node_path13.join)(baseDir, ".cursorrules");
932
+ const cursorFilePath = (0, import_node_path14.join)(baseDir, ".cursorrules");
872
933
  if (await fileExists(cursorFilePath)) {
873
934
  try {
874
935
  const rawContent = await readFileContent(cursorFilePath);
@@ -893,20 +954,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
893
954
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
894
955
  }
895
956
  }
896
- const cursorRulesDir = (0, import_node_path13.join)(baseDir, ".cursor", "rules");
957
+ const cursorRulesDir = (0, import_node_path14.join)(baseDir, ".cursor", "rules");
897
958
  if (await fileExists(cursorRulesDir)) {
898
959
  try {
899
960
  const { readdir: readdir2 } = await import("fs/promises");
900
961
  const files = await readdir2(cursorRulesDir);
901
962
  for (const file of files) {
902
963
  if (file.endsWith(".mdc")) {
903
- const filePath = (0, import_node_path13.join)(cursorRulesDir, file);
964
+ const filePath = (0, import_node_path14.join)(cursorRulesDir, file);
904
965
  try {
905
966
  const rawContent = await readFileContent(filePath);
906
967
  const parsed = (0, import_gray_matter3.default)(rawContent, customMatterOptions);
907
968
  const content = parsed.content.trim();
908
969
  if (content) {
909
- const filename = (0, import_node_path13.basename)(file, ".mdc");
970
+ const filename = (0, import_node_path14.basename)(file, ".mdc");
910
971
  const frontmatter = {
911
972
  root: false,
912
973
  targets: ["cursor"],
@@ -938,11 +999,11 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
938
999
  }
939
1000
 
940
1001
  // src/parsers/roo.ts
941
- var import_node_path14 = require("path");
1002
+ var import_node_path15 = require("path");
942
1003
  async function parseRooConfiguration(baseDir = process.cwd()) {
943
1004
  const errors = [];
944
1005
  const rules = [];
945
- const rooFilePath = (0, import_node_path14.join)(baseDir, ".roo", "instructions.md");
1006
+ const rooFilePath = (0, import_node_path15.join)(baseDir, ".roo", "instructions.md");
946
1007
  if (!await fileExists(rooFilePath)) {
947
1008
  errors.push(".roo/instructions.md file not found");
948
1009
  return { rules, errors };
@@ -1022,7 +1083,7 @@ async function importConfiguration(options) {
1022
1083
  if (rules.length === 0) {
1023
1084
  return { success: false, rulesCreated: 0, errors };
1024
1085
  }
1025
- const rulesDirPath = (0, import_node_path15.join)(baseDir, rulesDir);
1086
+ const rulesDirPath = (0, import_node_path16.join)(baseDir, rulesDir);
1026
1087
  try {
1027
1088
  const { mkdir: mkdir3 } = await import("fs/promises");
1028
1089
  await mkdir3(rulesDirPath, { recursive: true });
@@ -1036,7 +1097,7 @@ async function importConfiguration(options) {
1036
1097
  try {
1037
1098
  const baseFilename = `${tool}__${rule.filename}`;
1038
1099
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
1039
- const filePath = (0, import_node_path15.join)(rulesDirPath, `${filename}.md`);
1100
+ const filePath = (0, import_node_path16.join)(rulesDirPath, `${filename}.md`);
1040
1101
  const content = generateRuleFileContent(rule);
1041
1102
  await writeFileContent(filePath, content);
1042
1103
  rulesCreated++;
@@ -1061,7 +1122,7 @@ function generateRuleFileContent(rule) {
1061
1122
  async function generateUniqueFilename(rulesDir, baseFilename) {
1062
1123
  let filename = baseFilename;
1063
1124
  let counter = 1;
1064
- while (await fileExists((0, import_node_path15.join)(rulesDir, `${filename}.md`))) {
1125
+ while (await fileExists((0, import_node_path16.join)(rulesDir, `${filename}.md`))) {
1065
1126
  filename = `${baseFilename}-${counter}`;
1066
1127
  counter++;
1067
1128
  }
@@ -1076,9 +1137,10 @@ async function importCommand(options = {}) {
1076
1137
  if (options.copilot) tools.push("copilot");
1077
1138
  if (options.cline) tools.push("cline");
1078
1139
  if (options.roo) tools.push("roo");
1140
+ if (options.geminicli) tools.push("geminicli");
1079
1141
  if (tools.length === 0) {
1080
1142
  console.error(
1081
- "\u274C Please specify at least one tool to import from (--claudecode, --cursor, --copilot, --cline, --roo)"
1143
+ "\u274C Please specify at least one tool to import from (--claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
1082
1144
  );
1083
1145
  process.exit(1);
1084
1146
  }
@@ -1128,7 +1190,7 @@ Importing from ${tool}...`);
1128
1190
  }
1129
1191
 
1130
1192
  // src/cli/commands/init.ts
1131
- var import_node_path16 = require("path");
1193
+ var import_node_path17 = require("path");
1132
1194
  async function initCommand() {
1133
1195
  const aiRulesDir = ".rulesync";
1134
1196
  console.log("Initializing rulesync...");
@@ -1258,7 +1320,7 @@ globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
1258
1320
  }
1259
1321
  ];
1260
1322
  for (const file of sampleFiles) {
1261
- const filepath = (0, import_node_path16.join)(aiRulesDir, file.filename);
1323
+ const filepath = (0, import_node_path17.join)(aiRulesDir, file.filename);
1262
1324
  if (!await fileExists(filepath)) {
1263
1325
  await writeFileContent(filepath, file.content);
1264
1326
  console.log(`Created ${filepath}`);
@@ -1401,12 +1463,12 @@ async function watchCommand() {
1401
1463
 
1402
1464
  // src/cli/index.ts
1403
1465
  var program = new import_commander.Command();
1404
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.33.0");
1466
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.34.0");
1405
1467
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
1406
1468
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
1407
1469
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
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(
1470
+ 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("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("-v, --verbose", "Verbose output").action(importCommand);
1471
+ 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("--geminicli", "Generate only for Gemini CLI").option("--delete", "Delete all existing files in output directories before generating").option(
1410
1472
  "-b, --base-dir <paths>",
1411
1473
  "Base directories to generate files (comma-separated for multiple paths)"
1412
1474
  ).option("-v, --verbose", "Verbose output").action(async (options) => {
@@ -1416,6 +1478,7 @@ program.command("generate").description("Generate configuration files for AI too
1416
1478
  if (options.cline) tools.push("cline");
1417
1479
  if (options.claudecode) tools.push("claudecode");
1418
1480
  if (options.roo) tools.push("roo");
1481
+ if (options.geminicli) tools.push("geminicli");
1419
1482
  const generateOptions = {
1420
1483
  verbose: options.verbose,
1421
1484
  delete: options.delete
package/dist/index.mjs CHANGED
@@ -16,10 +16,11 @@ function getDefaultConfig() {
16
16
  cursor: ".cursor/rules",
17
17
  cline: ".clinerules",
18
18
  claudecode: ".",
19
- roo: ".roo/rules"
19
+ roo: ".roo/rules",
20
+ geminicli: ".gemini/memories"
20
21
  },
21
22
  watchEnabled: false,
22
- defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo"]
23
+ defaultTargets: ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli"]
23
24
  };
24
25
  }
25
26
  function resolveTargets(targets, config) {
@@ -203,14 +204,67 @@ function generateCursorMarkdown(rule) {
203
204
  return lines.join("\n");
204
205
  }
205
206
 
206
- // src/generators/roo.ts
207
+ // src/generators/geminicli.ts
207
208
  import { join as join5 } from "path";
209
+ async function generateGeminiConfig(rules, config, baseDir) {
210
+ const outputs = [];
211
+ const rootRule = rules.find((rule) => rule.frontmatter.root === true);
212
+ const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
213
+ for (const rule of memoryRules) {
214
+ const content = generateGeminiMemoryMarkdown(rule);
215
+ const outputDir = baseDir ? join5(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
216
+ const filepath = join5(outputDir, `${rule.filename}.md`);
217
+ outputs.push({
218
+ tool: "geminicli",
219
+ filepath,
220
+ content
221
+ });
222
+ }
223
+ const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
224
+ const rootFilepath = baseDir ? join5(baseDir, "GEMINI.md") : "GEMINI.md";
225
+ outputs.push({
226
+ tool: "geminicli",
227
+ filepath: rootFilepath,
228
+ content: rootContent
229
+ });
230
+ return outputs;
231
+ }
232
+ function generateGeminiMemoryMarkdown(rule) {
233
+ return rule.content.trim();
234
+ }
235
+ function generateGeminiRootMarkdown(rootRule, memoryRules, _baseDir) {
236
+ const lines = [];
237
+ if (memoryRules.length > 0) {
238
+ lines.push("Please also reference the following documents as needed:");
239
+ lines.push("");
240
+ lines.push("| Document | Description | File Patterns |");
241
+ lines.push("|----------|-------------|---------------|");
242
+ for (const rule of memoryRules) {
243
+ const relativePath = `@.gemini/memories/${rule.filename}.md`;
244
+ const filePatterns = rule.frontmatter.globs.length > 0 ? rule.frontmatter.globs.join(", ") : "-";
245
+ lines.push(`| ${relativePath} | ${rule.frontmatter.description} | ${filePatterns} |`);
246
+ }
247
+ lines.push("");
248
+ lines.push("");
249
+ }
250
+ if (rootRule) {
251
+ lines.push(rootRule.content.trim());
252
+ } else if (memoryRules.length === 0) {
253
+ lines.push("# Gemini CLI Configuration");
254
+ lines.push("");
255
+ lines.push("No configuration rules have been defined yet.");
256
+ }
257
+ return lines.join("\n");
258
+ }
259
+
260
+ // src/generators/roo.ts
261
+ import { join as join6 } from "path";
208
262
  async function generateRooConfig(rules, config, baseDir) {
209
263
  const outputs = [];
210
264
  for (const rule of rules) {
211
265
  const content = generateRooMarkdown(rule);
212
- const outputDir = baseDir ? join5(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
213
- const filepath = join5(outputDir, `${rule.filename}.md`);
266
+ const outputDir = baseDir ? join6(baseDir, config.outputPaths.roo) : config.outputPaths.roo;
267
+ const filepath = join6(outputDir, `${rule.filename}.md`);
214
268
  outputs.push({
215
269
  tool: "roo",
216
270
  filepath,
@@ -225,7 +279,7 @@ function generateRooMarkdown(rule) {
225
279
 
226
280
  // src/utils/file.ts
227
281
  import { mkdir as mkdir2, readdir, readFile, rm, stat, writeFile as writeFile2 } from "fs/promises";
228
- import { dirname, join as join6 } from "path";
282
+ import { dirname, join as join7 } from "path";
229
283
  async function ensureDir(dirPath) {
230
284
  try {
231
285
  await stat(dirPath);
@@ -243,7 +297,7 @@ async function writeFileContent(filepath, content) {
243
297
  async function findFiles(dir, extension = ".md") {
244
298
  try {
245
299
  const files = await readdir(dir);
246
- return files.filter((file) => file.endsWith(extension)).map((file) => join6(dir, file));
300
+ return files.filter((file) => file.endsWith(extension)).map((file) => join7(dir, file));
247
301
  } catch {
248
302
  return [];
249
303
  }
@@ -331,6 +385,8 @@ async function generateForTool(tool, rules, config, baseDir) {
331
385
  return await generateClaudecodeConfig(rules, config, baseDir);
332
386
  case "roo":
333
387
  return generateRooConfig(rules, config, baseDir);
388
+ case "geminicli":
389
+ return generateGeminiConfig(rules, config, baseDir);
334
390
  default:
335
391
  console.warn(`Unknown tool: ${tool}`);
336
392
  return null;
@@ -412,7 +468,7 @@ function validateFrontmatter(data, filepath) {
412
468
  `Invalid "targets" field in ${filepath}: must be an array, got ${typeof obj.targets}`
413
469
  );
414
470
  }
415
- const validTargets = ["copilot", "cursor", "cline", "claudecode", "roo", "*"];
471
+ const validTargets = ["copilot", "cursor", "cline", "claudecode", "roo", "geminicli", "*"];
416
472
  for (const target of obj.targets) {
417
473
  if (typeof target !== "string" || !validTargets.includes(target)) {
418
474
  throw new Error(
@@ -544,6 +600,9 @@ async function generateCommand(options = {}) {
544
600
  case "roo":
545
601
  deleteTasks.push(removeDirectory(config.outputPaths.roo));
546
602
  break;
603
+ case "geminicli":
604
+ deleteTasks.push(removeDirectory(config.outputPaths.geminicli));
605
+ break;
547
606
  }
548
607
  }
549
608
  await Promise.all(deleteTasks);
@@ -584,9 +643,9 @@ Generating configurations for base directory: ${baseDir}`);
584
643
 
585
644
  // src/cli/commands/gitignore.ts
586
645
  import { existsSync, readFileSync, writeFileSync } from "fs";
587
- import { join as join7 } from "path";
646
+ import { join as join8 } from "path";
588
647
  var gitignoreCommand = async () => {
589
- const gitignorePath = join7(process.cwd(), ".gitignore");
648
+ const gitignorePath = join8(process.cwd(), ".gitignore");
590
649
  const rulesFilesToIgnore = [
591
650
  "# Generated by rulesync - AI tool configuration files",
592
651
  "**/.github/copilot-instructions.md",
@@ -595,7 +654,9 @@ var gitignoreCommand = async () => {
595
654
  "**/.clinerules/",
596
655
  "**/CLAUDE.md",
597
656
  "**/.claude/memories/",
598
- "**/.roo/rules/"
657
+ "**/.roo/rules/",
658
+ "**/GEMINI.md",
659
+ "**/.gemini/memories/"
599
660
  ];
600
661
  let gitignoreContent = "";
601
662
  if (existsSync(gitignorePath)) {
@@ -626,15 +687,15 @@ ${linesToAdd.join("\n")}
626
687
  };
627
688
 
628
689
  // src/core/importer.ts
629
- import { join as join13 } from "path";
690
+ import { join as join14 } from "path";
630
691
  import matter4 from "gray-matter";
631
692
 
632
693
  // src/parsers/claudecode.ts
633
- import { basename as basename2, join as join8 } from "path";
694
+ import { basename as basename2, join as join9 } from "path";
634
695
  async function parseClaudeConfiguration(baseDir = process.cwd()) {
635
696
  const errors = [];
636
697
  const rules = [];
637
- const claudeFilePath = join8(baseDir, "CLAUDE.md");
698
+ const claudeFilePath = join9(baseDir, "CLAUDE.md");
638
699
  if (!await fileExists(claudeFilePath)) {
639
700
  errors.push("CLAUDE.md file not found");
640
701
  return { rules, errors };
@@ -645,7 +706,7 @@ async function parseClaudeConfiguration(baseDir = process.cwd()) {
645
706
  if (mainRule) {
646
707
  rules.push(mainRule);
647
708
  }
648
- const memoryDir = join8(baseDir, ".claude", "memories");
709
+ const memoryDir = join9(baseDir, ".claude", "memories");
649
710
  if (await fileExists(memoryDir)) {
650
711
  const memoryRules = await parseClaudeMemoryFiles(memoryDir);
651
712
  rules.push(...memoryRules);
@@ -691,7 +752,7 @@ async function parseClaudeMemoryFiles(memoryDir) {
691
752
  const files = await readdir2(memoryDir);
692
753
  for (const file of files) {
693
754
  if (file.endsWith(".md")) {
694
- const filePath = join8(memoryDir, file);
755
+ const filePath = join9(memoryDir, file);
695
756
  const content = await readFileContent(filePath);
696
757
  if (content.trim()) {
697
758
  const filename = basename2(file, ".md");
@@ -716,11 +777,11 @@ async function parseClaudeMemoryFiles(memoryDir) {
716
777
  }
717
778
 
718
779
  // src/parsers/cline.ts
719
- import { join as join9 } from "path";
780
+ import { join as join10 } from "path";
720
781
  async function parseClineConfiguration(baseDir = process.cwd()) {
721
782
  const errors = [];
722
783
  const rules = [];
723
- const clineFilePath = join9(baseDir, ".cline", "instructions.md");
784
+ const clineFilePath = join10(baseDir, ".cline", "instructions.md");
724
785
  if (!await fileExists(clineFilePath)) {
725
786
  errors.push(".cline/instructions.md file not found");
726
787
  return { rules, errors };
@@ -749,12 +810,12 @@ async function parseClineConfiguration(baseDir = process.cwd()) {
749
810
  }
750
811
 
751
812
  // src/parsers/copilot.ts
752
- import { basename as basename3, join as join10 } from "path";
813
+ import { basename as basename3, join as join11 } from "path";
753
814
  import matter2 from "gray-matter";
754
815
  async function parseCopilotConfiguration(baseDir = process.cwd()) {
755
816
  const errors = [];
756
817
  const rules = [];
757
- const copilotFilePath = join10(baseDir, ".github", "copilot-instructions.md");
818
+ const copilotFilePath = join11(baseDir, ".github", "copilot-instructions.md");
758
819
  if (await fileExists(copilotFilePath)) {
759
820
  try {
760
821
  const rawContent = await readFileContent(copilotFilePath);
@@ -779,14 +840,14 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
779
840
  errors.push(`Failed to parse copilot-instructions.md: ${errorMessage}`);
780
841
  }
781
842
  }
782
- const instructionsDir = join10(baseDir, ".github", "instructions");
843
+ const instructionsDir = join11(baseDir, ".github", "instructions");
783
844
  if (await fileExists(instructionsDir)) {
784
845
  try {
785
846
  const { readdir: readdir2 } = await import("fs/promises");
786
847
  const files = await readdir2(instructionsDir);
787
848
  for (const file of files) {
788
849
  if (file.endsWith(".instructions.md")) {
789
- const filePath = join10(instructionsDir, file);
850
+ const filePath = join11(instructionsDir, file);
790
851
  const rawContent = await readFileContent(filePath);
791
852
  const parsed = matter2(rawContent);
792
853
  const content = parsed.content.trim();
@@ -821,7 +882,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
821
882
  }
822
883
 
823
884
  // src/parsers/cursor.ts
824
- import { basename as basename4, join as join11 } from "path";
885
+ import { basename as basename4, join as join12 } from "path";
825
886
  import matter3 from "gray-matter";
826
887
  import yaml from "js-yaml";
827
888
  var customMatterOptions = {
@@ -845,7 +906,7 @@ var customMatterOptions = {
845
906
  async function parseCursorConfiguration(baseDir = process.cwd()) {
846
907
  const errors = [];
847
908
  const rules = [];
848
- const cursorFilePath = join11(baseDir, ".cursorrules");
909
+ const cursorFilePath = join12(baseDir, ".cursorrules");
849
910
  if (await fileExists(cursorFilePath)) {
850
911
  try {
851
912
  const rawContent = await readFileContent(cursorFilePath);
@@ -870,14 +931,14 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
870
931
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
871
932
  }
872
933
  }
873
- const cursorRulesDir = join11(baseDir, ".cursor", "rules");
934
+ const cursorRulesDir = join12(baseDir, ".cursor", "rules");
874
935
  if (await fileExists(cursorRulesDir)) {
875
936
  try {
876
937
  const { readdir: readdir2 } = await import("fs/promises");
877
938
  const files = await readdir2(cursorRulesDir);
878
939
  for (const file of files) {
879
940
  if (file.endsWith(".mdc")) {
880
- const filePath = join11(cursorRulesDir, file);
941
+ const filePath = join12(cursorRulesDir, file);
881
942
  try {
882
943
  const rawContent = await readFileContent(filePath);
883
944
  const parsed = matter3(rawContent, customMatterOptions);
@@ -915,11 +976,11 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
915
976
  }
916
977
 
917
978
  // src/parsers/roo.ts
918
- import { join as join12 } from "path";
979
+ import { join as join13 } from "path";
919
980
  async function parseRooConfiguration(baseDir = process.cwd()) {
920
981
  const errors = [];
921
982
  const rules = [];
922
- const rooFilePath = join12(baseDir, ".roo", "instructions.md");
983
+ const rooFilePath = join13(baseDir, ".roo", "instructions.md");
923
984
  if (!await fileExists(rooFilePath)) {
924
985
  errors.push(".roo/instructions.md file not found");
925
986
  return { rules, errors };
@@ -999,7 +1060,7 @@ async function importConfiguration(options) {
999
1060
  if (rules.length === 0) {
1000
1061
  return { success: false, rulesCreated: 0, errors };
1001
1062
  }
1002
- const rulesDirPath = join13(baseDir, rulesDir);
1063
+ const rulesDirPath = join14(baseDir, rulesDir);
1003
1064
  try {
1004
1065
  const { mkdir: mkdir3 } = await import("fs/promises");
1005
1066
  await mkdir3(rulesDirPath, { recursive: true });
@@ -1013,7 +1074,7 @@ async function importConfiguration(options) {
1013
1074
  try {
1014
1075
  const baseFilename = `${tool}__${rule.filename}`;
1015
1076
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
1016
- const filePath = join13(rulesDirPath, `${filename}.md`);
1077
+ const filePath = join14(rulesDirPath, `${filename}.md`);
1017
1078
  const content = generateRuleFileContent(rule);
1018
1079
  await writeFileContent(filePath, content);
1019
1080
  rulesCreated++;
@@ -1038,7 +1099,7 @@ function generateRuleFileContent(rule) {
1038
1099
  async function generateUniqueFilename(rulesDir, baseFilename) {
1039
1100
  let filename = baseFilename;
1040
1101
  let counter = 1;
1041
- while (await fileExists(join13(rulesDir, `${filename}.md`))) {
1102
+ while (await fileExists(join14(rulesDir, `${filename}.md`))) {
1042
1103
  filename = `${baseFilename}-${counter}`;
1043
1104
  counter++;
1044
1105
  }
@@ -1053,9 +1114,10 @@ async function importCommand(options = {}) {
1053
1114
  if (options.copilot) tools.push("copilot");
1054
1115
  if (options.cline) tools.push("cline");
1055
1116
  if (options.roo) tools.push("roo");
1117
+ if (options.geminicli) tools.push("geminicli");
1056
1118
  if (tools.length === 0) {
1057
1119
  console.error(
1058
- "\u274C Please specify at least one tool to import from (--claudecode, --cursor, --copilot, --cline, --roo)"
1120
+ "\u274C Please specify at least one tool to import from (--claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
1059
1121
  );
1060
1122
  process.exit(1);
1061
1123
  }
@@ -1105,7 +1167,7 @@ Importing from ${tool}...`);
1105
1167
  }
1106
1168
 
1107
1169
  // src/cli/commands/init.ts
1108
- import { join as join14 } from "path";
1170
+ import { join as join15 } from "path";
1109
1171
  async function initCommand() {
1110
1172
  const aiRulesDir = ".rulesync";
1111
1173
  console.log("Initializing rulesync...");
@@ -1235,7 +1297,7 @@ globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
1235
1297
  }
1236
1298
  ];
1237
1299
  for (const file of sampleFiles) {
1238
- const filepath = join14(aiRulesDir, file.filename);
1300
+ const filepath = join15(aiRulesDir, file.filename);
1239
1301
  if (!await fileExists(filepath)) {
1240
1302
  await writeFileContent(filepath, file.content);
1241
1303
  console.log(`Created ${filepath}`);
@@ -1378,12 +1440,12 @@ async function watchCommand() {
1378
1440
 
1379
1441
  // src/cli/index.ts
1380
1442
  var program = new Command();
1381
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.33.0");
1443
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.34.0");
1382
1444
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
1383
1445
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
1384
1446
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
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(
1447
+ 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("--geminicli", "Import from Gemini CLI (GEMINI.md)").option("-v, --verbose", "Verbose output").action(importCommand);
1448
+ 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("--geminicli", "Generate only for Gemini CLI").option("--delete", "Delete all existing files in output directories before generating").option(
1387
1449
  "-b, --base-dir <paths>",
1388
1450
  "Base directories to generate files (comma-separated for multiple paths)"
1389
1451
  ).option("-v, --verbose", "Verbose output").action(async (options) => {
@@ -1393,6 +1455,7 @@ program.command("generate").description("Generate configuration files for AI too
1393
1455
  if (options.cline) tools.push("cline");
1394
1456
  if (options.claudecode) tools.push("claudecode");
1395
1457
  if (options.roo) tools.push("roo");
1458
+ if (options.geminicli) tools.push("geminicli");
1396
1459
  const generateOptions = {
1397
1460
  verbose: options.verbose,
1398
1461
  delete: options.delete
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rulesync",
3
- "version": "0.33.0",
3
+ "version": "0.34.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",