rulesync 0.20.0 → 0.21.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
@@ -27,6 +27,8 @@ yarn global add rulesync
27
27
 
28
28
  ## 使い始める
29
29
 
30
+ ### 新しいプロジェクト
31
+
30
32
  1. **プロジェクトを初期化:**
31
33
  ```bash
32
34
  npx rulesync init
@@ -49,6 +51,22 @@ yarn global add rulesync
49
51
  npx rulesync gitignore
50
52
  ```
51
53
 
54
+ ### AIツール設定を持つ既存プロジェクト
55
+
56
+ 既にAIツールの設定がある場合、それらをインポートできます:
57
+
58
+ 1. **既存設定をインポート:**
59
+ ```bash
60
+ npx rulesync import --claude --cursor --copilot
61
+ ```
62
+
63
+ 2. **`.rulesync/`ディレクトリのインポートされたルールを確認・編集**
64
+
65
+ 3. **統合された設定を生成:**
66
+ ```bash
67
+ npx rulesync generate
68
+ ```
69
+
52
70
  以上です!AIコーディングアシスタントが生成された設定ファイルを自動的に使用するようになります。
53
71
 
54
72
  ## rulesyncを使う理由
@@ -180,7 +198,33 @@ npx rulesync generate --base-dir ./apps/web,./apps/api,./packages/shared
180
198
  - `--copilot`, `--cursor`, `--cline`, `--claude`, `--roo`: 指定されたツールのみ生成
181
199
  - `--base-dir <paths>`: 指定されたベースディレクトリに設定ファイルを生成(複数パスの場合はカンマ区切り)。異なるプロジェクトディレクトリにツール固有の設定を生成したいmonorepoセットアップに便利。
182
200
 
183
- ### 4. その他のコマンド
201
+ ### 4. 既存設定のインポート
202
+
203
+ プロジェクトに既存のAIツール設定がある場合、rulesync形式にインポートできます:
204
+
205
+ ```bash
206
+ # 既存のAIツール設定からインポート
207
+ npx rulesync import --claude # CLAUDE.mdからインポート
208
+ npx rulesync import --cursor # .cursorrulesからインポート
209
+ npx rulesync import --copilot # .github/copilot-instructions.mdからインポート
210
+ npx rulesync import --cline # .cline/instructions.mdからインポート
211
+ npx rulesync import --roo # .roo/instructions.mdからインポート
212
+
213
+ # 複数のツールからインポート
214
+ npx rulesync import --claude --cursor --copilot
215
+
216
+ # インポート時の詳細出力
217
+ npx rulesync import --claude --verbose
218
+ ```
219
+
220
+ importコマンドの動作:
221
+ - 各AIツールの既存設定ファイルをパース
222
+ - 適切なフロントマターを付けてrulesync形式に変換
223
+ - インポートしたコンテンツで新しい`.rulesync/*.md`ファイルを作成
224
+ - ファイル名の競合を避けるためツール固有のプレフィックスを使用(例:`claude__overview.md`)
225
+ - 競合が発生した場合はユニークなファイル名を生成
226
+
227
+ ### 5. その他のコマンド
184
228
 
185
229
  ```bash
186
230
  # サンプルファイルでプロジェクトを初期化
package/README.md CHANGED
@@ -27,6 +27,8 @@ yarn global add rulesync
27
27
 
28
28
  ## Getting Started
29
29
 
30
+ ### New Project
31
+
30
32
  1. **Initialize your project:**
31
33
  ```bash
32
34
  npx rulesync init
@@ -49,6 +51,22 @@ yarn global add rulesync
49
51
  npx rulesync gitignore
50
52
  ```
51
53
 
54
+ ### Existing Project with AI Tool Configurations
55
+
56
+ If you already have AI tool configurations, you can import them:
57
+
58
+ 1. **Import existing configurations:**
59
+ ```bash
60
+ npx rulesync import --claude --cursor --copilot
61
+ ```
62
+
63
+ 2. **Review and edit** the imported rules in `.rulesync/` directory
64
+
65
+ 3. **Generate unified configurations:**
66
+ ```bash
67
+ npx rulesync generate
68
+ ```
69
+
52
70
  That's it! Your AI coding assistants will now use the generated configuration files automatically.
53
71
 
54
72
  ## Why rulesync?
@@ -180,7 +198,33 @@ npx rulesync generate --base-dir ./apps/web,./apps/api,./packages/shared
180
198
  - `--copilot`, `--cursor`, `--cline`, `--claude`, `--roo`: Generate only for specified tools
181
199
  - `--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.
182
200
 
183
- ### 4. Other Commands
201
+ ### 4. Import Existing Configurations
202
+
203
+ If you already have AI tool configurations in your project, you can import them to rulesync format:
204
+
205
+ ```bash
206
+ # Import from existing AI tool configurations
207
+ npx rulesync import --claude # Import from CLAUDE.md
208
+ npx rulesync import --cursor # Import from .cursorrules
209
+ npx rulesync import --copilot # Import from .github/copilot-instructions.md
210
+ npx rulesync import --cline # Import from .cline/instructions.md
211
+ npx rulesync import --roo # Import from .roo/instructions.md
212
+
213
+ # Import from multiple tools
214
+ npx rulesync import --claude --cursor --copilot
215
+
216
+ # Verbose output during import
217
+ npx rulesync import --claude --verbose
218
+ ```
219
+
220
+ The import command will:
221
+ - Parse existing configuration files from each AI tool
222
+ - Convert them to rulesync format with appropriate frontmatter
223
+ - Create new `.rulesync/*.md` files with imported content
224
+ - Use tool-specific prefixes to avoid filename conflicts (e.g., `claude__overview.md`)
225
+ - Generate unique filenames if conflicts occur
226
+
227
+ ### 5. Other Commands
184
228
 
185
229
  ```bash
186
230
  # Initialize project with sample files
package/dist/index.js CHANGED
@@ -46,7 +46,7 @@ function getDefaultConfig() {
46
46
  };
47
47
  }
48
48
  function resolveTargets(targets, config) {
49
- if (targets.includes("*")) {
49
+ if (targets[0] === "*") {
50
50
  return config.defaultTargets;
51
51
  }
52
52
  return targets;
@@ -317,6 +317,12 @@ async function removeClaudeGeneratedFiles() {
317
317
  async function generateConfigurations(rules, config, targetTools, baseDir) {
318
318
  const outputs = [];
319
319
  const toolsToGenerate = targetTools || config.defaultTargets;
320
+ const rootFiles = rules.filter((rule) => rule.frontmatter.root === true);
321
+ if (rootFiles.length === 0) {
322
+ console.warn(
323
+ "\u26A0\uFE0F Warning: No files with 'root: true' found. This may result in incomplete configurations."
324
+ );
325
+ }
320
326
  for (const tool of toolsToGenerate) {
321
327
  const relevantRules = filterRulesForTool(rules, tool, config);
322
328
  if (relevantRules.length === 0) {
@@ -650,8 +656,387 @@ ${linesToAdd.join("\n")}
650
656
  }
651
657
  };
652
658
 
653
- // src/cli/commands/init.ts
659
+ // src/core/importer.ts
660
+ var import_node_path15 = require("path");
661
+ var import_gray_matter2 = __toESM(require("gray-matter"));
662
+
663
+ // src/parsers/claude.ts
654
664
  var import_node_path10 = require("path");
665
+ async function parseClaudeConfiguration(baseDir = process.cwd()) {
666
+ const errors = [];
667
+ const rules = [];
668
+ const claudeFilePath = (0, import_node_path10.join)(baseDir, "CLAUDE.md");
669
+ if (!await fileExists(claudeFilePath)) {
670
+ errors.push("CLAUDE.md file not found");
671
+ return { rules, errors };
672
+ }
673
+ try {
674
+ const claudeContent = await readFileContent(claudeFilePath);
675
+ const mainRule = parseClaudeMainFile(claudeContent, claudeFilePath);
676
+ if (mainRule) {
677
+ rules.push(mainRule);
678
+ }
679
+ const memoryDir = (0, import_node_path10.join)(baseDir, ".claude", "memories");
680
+ if (await fileExists(memoryDir)) {
681
+ const memoryRules = await parseClaudeMemoryFiles(memoryDir);
682
+ rules.push(...memoryRules);
683
+ }
684
+ } catch (error) {
685
+ const errorMessage = error instanceof Error ? error.message : String(error);
686
+ errors.push(`Failed to parse Claude configuration: ${errorMessage}`);
687
+ }
688
+ return { rules, errors };
689
+ }
690
+ function parseClaudeMainFile(content, filepath) {
691
+ const lines = content.split("\n");
692
+ let contentStartIndex = 0;
693
+ if (lines.some((line) => line.includes("| Document | Description | File Patterns |"))) {
694
+ const tableEndIndex = lines.findIndex(
695
+ (line, index) => index > 0 && line.trim() === "" && lines[index - 1]?.includes("|") && !lines[index + 1]?.includes("|")
696
+ );
697
+ if (tableEndIndex !== -1) {
698
+ contentStartIndex = tableEndIndex + 1;
699
+ }
700
+ }
701
+ const mainContent = lines.slice(contentStartIndex).join("\n").trim();
702
+ if (!mainContent) {
703
+ return null;
704
+ }
705
+ const frontmatter = {
706
+ root: false,
707
+ targets: ["claude"],
708
+ description: "Main Claude Code configuration",
709
+ globs: ["**/*"]
710
+ };
711
+ return {
712
+ frontmatter,
713
+ content: mainContent,
714
+ filename: "claude-main",
715
+ filepath
716
+ };
717
+ }
718
+ async function parseClaudeMemoryFiles(memoryDir) {
719
+ const rules = [];
720
+ try {
721
+ const { readdir: readdir2 } = await import("fs/promises");
722
+ const files = await readdir2(memoryDir);
723
+ for (const file of files) {
724
+ if (file.endsWith(".md")) {
725
+ const filePath = (0, import_node_path10.join)(memoryDir, file);
726
+ const content = await readFileContent(filePath);
727
+ if (content.trim()) {
728
+ const filename = (0, import_node_path10.basename)(file, ".md");
729
+ const frontmatter = {
730
+ root: false,
731
+ targets: ["claude"],
732
+ description: `Memory file: ${filename}`,
733
+ globs: ["**/*"]
734
+ };
735
+ rules.push({
736
+ frontmatter,
737
+ content: content.trim(),
738
+ filename: `claude-memory-${filename}`,
739
+ filepath: filePath
740
+ });
741
+ }
742
+ }
743
+ }
744
+ } catch (_error) {
745
+ }
746
+ return rules;
747
+ }
748
+
749
+ // src/parsers/cline.ts
750
+ var import_node_path11 = require("path");
751
+ async function parseClineConfiguration(baseDir = process.cwd()) {
752
+ const errors = [];
753
+ const rules = [];
754
+ const clineFilePath = (0, import_node_path11.join)(baseDir, ".cline", "instructions.md");
755
+ if (!await fileExists(clineFilePath)) {
756
+ errors.push(".cline/instructions.md file not found");
757
+ return { rules, errors };
758
+ }
759
+ try {
760
+ const content = await readFileContent(clineFilePath);
761
+ if (content.trim()) {
762
+ const frontmatter = {
763
+ root: false,
764
+ targets: ["cline"],
765
+ description: "Cline AI assistant instructions",
766
+ globs: ["**/*"]
767
+ };
768
+ rules.push({
769
+ frontmatter,
770
+ content: content.trim(),
771
+ filename: "cline-instructions",
772
+ filepath: clineFilePath
773
+ });
774
+ }
775
+ } catch (error) {
776
+ const errorMessage = error instanceof Error ? error.message : String(error);
777
+ errors.push(`Failed to parse Cline configuration: ${errorMessage}`);
778
+ }
779
+ return { rules, errors };
780
+ }
781
+
782
+ // src/parsers/copilot.ts
783
+ var import_node_path12 = require("path");
784
+ async function parseCopilotConfiguration(baseDir = process.cwd()) {
785
+ const errors = [];
786
+ const rules = [];
787
+ const copilotFilePath = (0, import_node_path12.join)(baseDir, ".github", "copilot-instructions.md");
788
+ if (!await fileExists(copilotFilePath)) {
789
+ errors.push(".github/copilot-instructions.md file not found");
790
+ return { rules, errors };
791
+ }
792
+ try {
793
+ const content = await readFileContent(copilotFilePath);
794
+ if (content.trim()) {
795
+ const frontmatter = {
796
+ root: false,
797
+ targets: ["copilot"],
798
+ description: "GitHub Copilot instructions",
799
+ globs: ["**/*"]
800
+ };
801
+ rules.push({
802
+ frontmatter,
803
+ content: content.trim(),
804
+ filename: "copilot-instructions",
805
+ filepath: copilotFilePath
806
+ });
807
+ }
808
+ } catch (error) {
809
+ const errorMessage = error instanceof Error ? error.message : String(error);
810
+ errors.push(`Failed to parse Copilot configuration: ${errorMessage}`);
811
+ }
812
+ return { rules, errors };
813
+ }
814
+
815
+ // src/parsers/cursor.ts
816
+ var import_node_path13 = require("path");
817
+ async function parseCursorConfiguration(baseDir = process.cwd()) {
818
+ const errors = [];
819
+ const rules = [];
820
+ const cursorFilePath = (0, import_node_path13.join)(baseDir, ".cursorrules");
821
+ if (!await fileExists(cursorFilePath)) {
822
+ errors.push(".cursorrules file not found");
823
+ return { rules, errors };
824
+ }
825
+ try {
826
+ const content = await readFileContent(cursorFilePath);
827
+ if (content.trim()) {
828
+ const frontmatter = {
829
+ root: false,
830
+ targets: ["cursor"],
831
+ description: "Cursor IDE configuration rules",
832
+ globs: ["**/*"]
833
+ };
834
+ rules.push({
835
+ frontmatter,
836
+ content: content.trim(),
837
+ filename: "cursor-rules",
838
+ filepath: cursorFilePath
839
+ });
840
+ }
841
+ } catch (error) {
842
+ const errorMessage = error instanceof Error ? error.message : String(error);
843
+ errors.push(`Failed to parse Cursor configuration: ${errorMessage}`);
844
+ }
845
+ return { rules, errors };
846
+ }
847
+
848
+ // src/parsers/roo.ts
849
+ var import_node_path14 = require("path");
850
+ async function parseRooConfiguration(baseDir = process.cwd()) {
851
+ const errors = [];
852
+ const rules = [];
853
+ const rooFilePath = (0, import_node_path14.join)(baseDir, ".roo", "instructions.md");
854
+ if (!await fileExists(rooFilePath)) {
855
+ errors.push(".roo/instructions.md file not found");
856
+ return { rules, errors };
857
+ }
858
+ try {
859
+ const content = await readFileContent(rooFilePath);
860
+ if (content.trim()) {
861
+ const frontmatter = {
862
+ root: false,
863
+ targets: ["roo"],
864
+ description: "Roo Code AI assistant instructions",
865
+ globs: ["**/*"]
866
+ };
867
+ rules.push({
868
+ frontmatter,
869
+ content: content.trim(),
870
+ filename: "roo-instructions",
871
+ filepath: rooFilePath
872
+ });
873
+ }
874
+ } catch (error) {
875
+ const errorMessage = error instanceof Error ? error.message : String(error);
876
+ errors.push(`Failed to parse Roo configuration: ${errorMessage}`);
877
+ }
878
+ return { rules, errors };
879
+ }
880
+
881
+ // src/core/importer.ts
882
+ async function importConfiguration(options) {
883
+ const { tool, baseDir = process.cwd(), rulesDir = ".rulesync", verbose = false } = options;
884
+ const errors = [];
885
+ let rules = [];
886
+ if (verbose) {
887
+ console.log(`Importing ${tool} configuration from ${baseDir}...`);
888
+ }
889
+ try {
890
+ switch (tool) {
891
+ case "claude": {
892
+ const claudeResult = await parseClaudeConfiguration(baseDir);
893
+ rules = claudeResult.rules;
894
+ errors.push(...claudeResult.errors);
895
+ break;
896
+ }
897
+ case "cursor": {
898
+ const cursorResult = await parseCursorConfiguration(baseDir);
899
+ rules = cursorResult.rules;
900
+ errors.push(...cursorResult.errors);
901
+ break;
902
+ }
903
+ case "copilot": {
904
+ const copilotResult = await parseCopilotConfiguration(baseDir);
905
+ rules = copilotResult.rules;
906
+ errors.push(...copilotResult.errors);
907
+ break;
908
+ }
909
+ case "cline": {
910
+ const clineResult = await parseClineConfiguration(baseDir);
911
+ rules = clineResult.rules;
912
+ errors.push(...clineResult.errors);
913
+ break;
914
+ }
915
+ case "roo": {
916
+ const rooResult = await parseRooConfiguration(baseDir);
917
+ rules = rooResult.rules;
918
+ errors.push(...rooResult.errors);
919
+ break;
920
+ }
921
+ default:
922
+ errors.push(`Unsupported tool: ${tool}`);
923
+ return { success: false, rulesCreated: 0, errors };
924
+ }
925
+ } catch (error) {
926
+ const errorMessage = error instanceof Error ? error.message : String(error);
927
+ errors.push(`Failed to parse ${tool} configuration: ${errorMessage}`);
928
+ return { success: false, rulesCreated: 0, errors };
929
+ }
930
+ if (rules.length === 0) {
931
+ return { success: false, rulesCreated: 0, errors };
932
+ }
933
+ const rulesDirPath = (0, import_node_path15.join)(baseDir, rulesDir);
934
+ try {
935
+ const { mkdir: mkdir3 } = await import("fs/promises");
936
+ await mkdir3(rulesDirPath, { recursive: true });
937
+ } catch (error) {
938
+ const errorMessage = error instanceof Error ? error.message : String(error);
939
+ errors.push(`Failed to create rules directory: ${errorMessage}`);
940
+ return { success: false, rulesCreated: 0, errors };
941
+ }
942
+ let rulesCreated = 0;
943
+ for (const rule of rules) {
944
+ try {
945
+ const baseFilename = `${tool}__${rule.filename}`;
946
+ const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
947
+ const filePath = (0, import_node_path15.join)(rulesDirPath, `${filename}.md`);
948
+ const content = generateRuleFileContent(rule);
949
+ await writeFileContent(filePath, content);
950
+ rulesCreated++;
951
+ if (verbose) {
952
+ console.log(`\u2705 Created rule file: ${filePath}`);
953
+ }
954
+ } catch (error) {
955
+ const errorMessage = error instanceof Error ? error.message : String(error);
956
+ errors.push(`Failed to create rule file for ${rule.filename}: ${errorMessage}`);
957
+ }
958
+ }
959
+ return {
960
+ success: rulesCreated > 0,
961
+ rulesCreated,
962
+ errors
963
+ };
964
+ }
965
+ function generateRuleFileContent(rule) {
966
+ const frontmatter = import_gray_matter2.default.stringify("", rule.frontmatter);
967
+ return frontmatter + rule.content;
968
+ }
969
+ async function generateUniqueFilename(rulesDir, baseFilename) {
970
+ let filename = baseFilename;
971
+ let counter = 1;
972
+ while (await fileExists((0, import_node_path15.join)(rulesDir, `${filename}.md`))) {
973
+ filename = `${baseFilename}-${counter}`;
974
+ counter++;
975
+ }
976
+ return filename;
977
+ }
978
+
979
+ // src/cli/commands/import.ts
980
+ async function importCommand(options = {}) {
981
+ const tools = [];
982
+ if (options.claude) tools.push("claude");
983
+ if (options.cursor) tools.push("cursor");
984
+ if (options.copilot) tools.push("copilot");
985
+ if (options.cline) tools.push("cline");
986
+ if (options.roo) tools.push("roo");
987
+ if (tools.length === 0) {
988
+ console.error(
989
+ "\u274C Please specify at least one tool to import from (--claude, --cursor, --copilot, --cline, --roo)"
990
+ );
991
+ process.exit(1);
992
+ }
993
+ console.log("Importing configuration files...");
994
+ let totalRulesCreated = 0;
995
+ const allErrors = [];
996
+ for (const tool of tools) {
997
+ if (options.verbose) {
998
+ console.log(`
999
+ Importing from ${tool}...`);
1000
+ }
1001
+ try {
1002
+ const result = await importConfiguration({
1003
+ tool,
1004
+ verbose: options.verbose ?? false
1005
+ });
1006
+ if (result.success) {
1007
+ console.log(`\u2705 Imported ${result.rulesCreated} rule(s) from ${tool}`);
1008
+ totalRulesCreated += result.rulesCreated;
1009
+ } else if (result.errors.length > 0) {
1010
+ console.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
1011
+ if (options.verbose) {
1012
+ allErrors.push(...result.errors);
1013
+ }
1014
+ }
1015
+ } catch (error) {
1016
+ const errorMessage = error instanceof Error ? error.message : String(error);
1017
+ console.error(`\u274C Error importing from ${tool}: ${errorMessage}`);
1018
+ allErrors.push(`${tool}: ${errorMessage}`);
1019
+ }
1020
+ }
1021
+ if (totalRulesCreated > 0) {
1022
+ console.log(`
1023
+ \u{1F389} Successfully imported ${totalRulesCreated} rule(s) total!`);
1024
+ console.log("You can now run 'rulesync generate' to create tool-specific configurations.");
1025
+ } else {
1026
+ console.warn(
1027
+ "\n\u26A0\uFE0F No rules were imported. Please check that configuration files exist for the selected tools."
1028
+ );
1029
+ }
1030
+ if (options.verbose && allErrors.length > 0) {
1031
+ console.log("\nDetailed errors:");
1032
+ for (const error of allErrors) {
1033
+ console.log(` - ${error}`);
1034
+ }
1035
+ }
1036
+ }
1037
+
1038
+ // src/cli/commands/init.ts
1039
+ var import_node_path16 = require("path");
655
1040
  async function initCommand() {
656
1041
  const aiRulesDir = ".rulesync";
657
1042
  console.log("Initializing rulesync...");
@@ -781,7 +1166,7 @@ globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
781
1166
  }
782
1167
  ];
783
1168
  for (const file of sampleFiles) {
784
- const filepath = (0, import_node_path10.join)(aiRulesDir, file.filename);
1169
+ const filepath = (0, import_node_path16.join)(aiRulesDir, file.filename);
785
1170
  if (!await fileExists(filepath)) {
786
1171
  await writeFileContent(filepath, file.content);
787
1172
  console.log(`Created ${filepath}`);
@@ -808,13 +1193,13 @@ async function statusCommand() {
808
1193
  console.log(`
809
1194
  \u{1F4CB} Rules: ${rules.length} total`);
810
1195
  if (rules.length > 0) {
811
- const highPriority = rules.filter((r) => r.frontmatter.priority === "high").length;
812
- const lowPriority = rules.filter((r) => r.frontmatter.priority === "low").length;
813
- console.log(` - High priority: ${highPriority}`);
814
- console.log(` - Low priority: ${lowPriority}`);
815
- const targetCounts = { copilot: 0, cursor: 0, cline: 0 };
1196
+ const rootRules = rules.filter((r) => r.frontmatter.root).length;
1197
+ const nonRootRules = rules.length - rootRules;
1198
+ console.log(` - Root rules: ${rootRules}`);
1199
+ console.log(` - Non-root rules: ${nonRootRules}`);
1200
+ const targetCounts = { copilot: 0, cursor: 0, cline: 0, claude: 0, roo: 0 };
816
1201
  for (const rule of rules) {
817
- const targets = rule.frontmatter.targets.includes("*") ? config.defaultTargets : rule.frontmatter.targets;
1202
+ const targets = rule.frontmatter.targets[0] === "*" ? config.defaultTargets : rule.frontmatter.targets;
818
1203
  for (const target of targets) {
819
1204
  if (target in targetCounts) {
820
1205
  targetCounts[target]++;
@@ -825,6 +1210,8 @@ async function statusCommand() {
825
1210
  console.log(` - Copilot: ${targetCounts.copilot} rules`);
826
1211
  console.log(` - Cursor: ${targetCounts.cursor} rules`);
827
1212
  console.log(` - Cline: ${targetCounts.cline} rules`);
1213
+ console.log(` - Claude: ${targetCounts.claude} rules`);
1214
+ console.log(` - Roo: ${targetCounts.roo} rules`);
828
1215
  }
829
1216
  console.log("\n\u{1F4E4} Generated files:");
830
1217
  for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
@@ -922,11 +1309,12 @@ async function watchCommand() {
922
1309
 
923
1310
  // src/cli/index.ts
924
1311
  var program = new import_commander.Command();
925
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.1.0");
1312
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.20.0");
926
1313
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
927
1314
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
928
1315
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
929
- 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(
1316
+ program.command("import").description("Import configurations from AI tools to rulesync format").option("--claude", "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);
1317
+ program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claude", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--delete", "Delete all existing files in output directories before generating").option(
930
1318
  "-b, --base-dir <paths>",
931
1319
  "Base directories to generate files (comma-separated for multiple paths)"
932
1320
  ).option("-v, --verbose", "Verbose output").action(async (options) => {
@@ -935,6 +1323,7 @@ program.command("generate").description("Generate configuration files for AI too
935
1323
  if (options.cursor) tools.push("cursor");
936
1324
  if (options.cline) tools.push("cline");
937
1325
  if (options.claude) tools.push("claude");
1326
+ if (options.roo) tools.push("roo");
938
1327
  const generateOptions = {
939
1328
  verbose: options.verbose,
940
1329
  delete: options.delete
package/dist/index.mjs CHANGED
@@ -23,7 +23,7 @@ function getDefaultConfig() {
23
23
  };
24
24
  }
25
25
  function resolveTargets(targets, config) {
26
- if (targets.includes("*")) {
26
+ if (targets[0] === "*") {
27
27
  return config.defaultTargets;
28
28
  }
29
29
  return targets;
@@ -294,6 +294,12 @@ async function removeClaudeGeneratedFiles() {
294
294
  async function generateConfigurations(rules, config, targetTools, baseDir) {
295
295
  const outputs = [];
296
296
  const toolsToGenerate = targetTools || config.defaultTargets;
297
+ const rootFiles = rules.filter((rule) => rule.frontmatter.root === true);
298
+ if (rootFiles.length === 0) {
299
+ console.warn(
300
+ "\u26A0\uFE0F Warning: No files with 'root: true' found. This may result in incomplete configurations."
301
+ );
302
+ }
297
303
  for (const tool of toolsToGenerate) {
298
304
  const relevantRules = filterRulesForTool(rules, tool, config);
299
305
  if (relevantRules.length === 0) {
@@ -627,8 +633,387 @@ ${linesToAdd.join("\n")}
627
633
  }
628
634
  };
629
635
 
636
+ // src/core/importer.ts
637
+ import { join as join13 } from "path";
638
+ import matter2 from "gray-matter";
639
+
640
+ // src/parsers/claude.ts
641
+ import { basename as basename2, join as join8 } from "path";
642
+ async function parseClaudeConfiguration(baseDir = process.cwd()) {
643
+ const errors = [];
644
+ const rules = [];
645
+ const claudeFilePath = join8(baseDir, "CLAUDE.md");
646
+ if (!await fileExists(claudeFilePath)) {
647
+ errors.push("CLAUDE.md file not found");
648
+ return { rules, errors };
649
+ }
650
+ try {
651
+ const claudeContent = await readFileContent(claudeFilePath);
652
+ const mainRule = parseClaudeMainFile(claudeContent, claudeFilePath);
653
+ if (mainRule) {
654
+ rules.push(mainRule);
655
+ }
656
+ const memoryDir = join8(baseDir, ".claude", "memories");
657
+ if (await fileExists(memoryDir)) {
658
+ const memoryRules = await parseClaudeMemoryFiles(memoryDir);
659
+ rules.push(...memoryRules);
660
+ }
661
+ } catch (error) {
662
+ const errorMessage = error instanceof Error ? error.message : String(error);
663
+ errors.push(`Failed to parse Claude configuration: ${errorMessage}`);
664
+ }
665
+ return { rules, errors };
666
+ }
667
+ function parseClaudeMainFile(content, filepath) {
668
+ const lines = content.split("\n");
669
+ let contentStartIndex = 0;
670
+ if (lines.some((line) => line.includes("| Document | Description | File Patterns |"))) {
671
+ const tableEndIndex = lines.findIndex(
672
+ (line, index) => index > 0 && line.trim() === "" && lines[index - 1]?.includes("|") && !lines[index + 1]?.includes("|")
673
+ );
674
+ if (tableEndIndex !== -1) {
675
+ contentStartIndex = tableEndIndex + 1;
676
+ }
677
+ }
678
+ const mainContent = lines.slice(contentStartIndex).join("\n").trim();
679
+ if (!mainContent) {
680
+ return null;
681
+ }
682
+ const frontmatter = {
683
+ root: false,
684
+ targets: ["claude"],
685
+ description: "Main Claude Code configuration",
686
+ globs: ["**/*"]
687
+ };
688
+ return {
689
+ frontmatter,
690
+ content: mainContent,
691
+ filename: "claude-main",
692
+ filepath
693
+ };
694
+ }
695
+ async function parseClaudeMemoryFiles(memoryDir) {
696
+ const rules = [];
697
+ try {
698
+ const { readdir: readdir2 } = await import("fs/promises");
699
+ const files = await readdir2(memoryDir);
700
+ for (const file of files) {
701
+ if (file.endsWith(".md")) {
702
+ const filePath = join8(memoryDir, file);
703
+ const content = await readFileContent(filePath);
704
+ if (content.trim()) {
705
+ const filename = basename2(file, ".md");
706
+ const frontmatter = {
707
+ root: false,
708
+ targets: ["claude"],
709
+ description: `Memory file: ${filename}`,
710
+ globs: ["**/*"]
711
+ };
712
+ rules.push({
713
+ frontmatter,
714
+ content: content.trim(),
715
+ filename: `claude-memory-${filename}`,
716
+ filepath: filePath
717
+ });
718
+ }
719
+ }
720
+ }
721
+ } catch (_error) {
722
+ }
723
+ return rules;
724
+ }
725
+
726
+ // src/parsers/cline.ts
727
+ import { join as join9 } from "path";
728
+ async function parseClineConfiguration(baseDir = process.cwd()) {
729
+ const errors = [];
730
+ const rules = [];
731
+ const clineFilePath = join9(baseDir, ".cline", "instructions.md");
732
+ if (!await fileExists(clineFilePath)) {
733
+ errors.push(".cline/instructions.md file not found");
734
+ return { rules, errors };
735
+ }
736
+ try {
737
+ const content = await readFileContent(clineFilePath);
738
+ if (content.trim()) {
739
+ const frontmatter = {
740
+ root: false,
741
+ targets: ["cline"],
742
+ description: "Cline AI assistant instructions",
743
+ globs: ["**/*"]
744
+ };
745
+ rules.push({
746
+ frontmatter,
747
+ content: content.trim(),
748
+ filename: "cline-instructions",
749
+ filepath: clineFilePath
750
+ });
751
+ }
752
+ } catch (error) {
753
+ const errorMessage = error instanceof Error ? error.message : String(error);
754
+ errors.push(`Failed to parse Cline configuration: ${errorMessage}`);
755
+ }
756
+ return { rules, errors };
757
+ }
758
+
759
+ // src/parsers/copilot.ts
760
+ import { join as join10 } from "path";
761
+ async function parseCopilotConfiguration(baseDir = process.cwd()) {
762
+ const errors = [];
763
+ const rules = [];
764
+ const copilotFilePath = join10(baseDir, ".github", "copilot-instructions.md");
765
+ if (!await fileExists(copilotFilePath)) {
766
+ errors.push(".github/copilot-instructions.md file not found");
767
+ return { rules, errors };
768
+ }
769
+ try {
770
+ const content = await readFileContent(copilotFilePath);
771
+ if (content.trim()) {
772
+ const frontmatter = {
773
+ root: false,
774
+ targets: ["copilot"],
775
+ description: "GitHub Copilot instructions",
776
+ globs: ["**/*"]
777
+ };
778
+ rules.push({
779
+ frontmatter,
780
+ content: content.trim(),
781
+ filename: "copilot-instructions",
782
+ filepath: copilotFilePath
783
+ });
784
+ }
785
+ } catch (error) {
786
+ const errorMessage = error instanceof Error ? error.message : String(error);
787
+ errors.push(`Failed to parse Copilot configuration: ${errorMessage}`);
788
+ }
789
+ return { rules, errors };
790
+ }
791
+
792
+ // src/parsers/cursor.ts
793
+ import { join as join11 } from "path";
794
+ async function parseCursorConfiguration(baseDir = process.cwd()) {
795
+ const errors = [];
796
+ const rules = [];
797
+ const cursorFilePath = join11(baseDir, ".cursorrules");
798
+ if (!await fileExists(cursorFilePath)) {
799
+ errors.push(".cursorrules file not found");
800
+ return { rules, errors };
801
+ }
802
+ try {
803
+ const content = await readFileContent(cursorFilePath);
804
+ if (content.trim()) {
805
+ const frontmatter = {
806
+ root: false,
807
+ targets: ["cursor"],
808
+ description: "Cursor IDE configuration rules",
809
+ globs: ["**/*"]
810
+ };
811
+ rules.push({
812
+ frontmatter,
813
+ content: content.trim(),
814
+ filename: "cursor-rules",
815
+ filepath: cursorFilePath
816
+ });
817
+ }
818
+ } catch (error) {
819
+ const errorMessage = error instanceof Error ? error.message : String(error);
820
+ errors.push(`Failed to parse Cursor configuration: ${errorMessage}`);
821
+ }
822
+ return { rules, errors };
823
+ }
824
+
825
+ // src/parsers/roo.ts
826
+ import { join as join12 } from "path";
827
+ async function parseRooConfiguration(baseDir = process.cwd()) {
828
+ const errors = [];
829
+ const rules = [];
830
+ const rooFilePath = join12(baseDir, ".roo", "instructions.md");
831
+ if (!await fileExists(rooFilePath)) {
832
+ errors.push(".roo/instructions.md file not found");
833
+ return { rules, errors };
834
+ }
835
+ try {
836
+ const content = await readFileContent(rooFilePath);
837
+ if (content.trim()) {
838
+ const frontmatter = {
839
+ root: false,
840
+ targets: ["roo"],
841
+ description: "Roo Code AI assistant instructions",
842
+ globs: ["**/*"]
843
+ };
844
+ rules.push({
845
+ frontmatter,
846
+ content: content.trim(),
847
+ filename: "roo-instructions",
848
+ filepath: rooFilePath
849
+ });
850
+ }
851
+ } catch (error) {
852
+ const errorMessage = error instanceof Error ? error.message : String(error);
853
+ errors.push(`Failed to parse Roo configuration: ${errorMessage}`);
854
+ }
855
+ return { rules, errors };
856
+ }
857
+
858
+ // src/core/importer.ts
859
+ async function importConfiguration(options) {
860
+ const { tool, baseDir = process.cwd(), rulesDir = ".rulesync", verbose = false } = options;
861
+ const errors = [];
862
+ let rules = [];
863
+ if (verbose) {
864
+ console.log(`Importing ${tool} configuration from ${baseDir}...`);
865
+ }
866
+ try {
867
+ switch (tool) {
868
+ case "claude": {
869
+ const claudeResult = await parseClaudeConfiguration(baseDir);
870
+ rules = claudeResult.rules;
871
+ errors.push(...claudeResult.errors);
872
+ break;
873
+ }
874
+ case "cursor": {
875
+ const cursorResult = await parseCursorConfiguration(baseDir);
876
+ rules = cursorResult.rules;
877
+ errors.push(...cursorResult.errors);
878
+ break;
879
+ }
880
+ case "copilot": {
881
+ const copilotResult = await parseCopilotConfiguration(baseDir);
882
+ rules = copilotResult.rules;
883
+ errors.push(...copilotResult.errors);
884
+ break;
885
+ }
886
+ case "cline": {
887
+ const clineResult = await parseClineConfiguration(baseDir);
888
+ rules = clineResult.rules;
889
+ errors.push(...clineResult.errors);
890
+ break;
891
+ }
892
+ case "roo": {
893
+ const rooResult = await parseRooConfiguration(baseDir);
894
+ rules = rooResult.rules;
895
+ errors.push(...rooResult.errors);
896
+ break;
897
+ }
898
+ default:
899
+ errors.push(`Unsupported tool: ${tool}`);
900
+ return { success: false, rulesCreated: 0, errors };
901
+ }
902
+ } catch (error) {
903
+ const errorMessage = error instanceof Error ? error.message : String(error);
904
+ errors.push(`Failed to parse ${tool} configuration: ${errorMessage}`);
905
+ return { success: false, rulesCreated: 0, errors };
906
+ }
907
+ if (rules.length === 0) {
908
+ return { success: false, rulesCreated: 0, errors };
909
+ }
910
+ const rulesDirPath = join13(baseDir, rulesDir);
911
+ try {
912
+ const { mkdir: mkdir3 } = await import("fs/promises");
913
+ await mkdir3(rulesDirPath, { recursive: true });
914
+ } catch (error) {
915
+ const errorMessage = error instanceof Error ? error.message : String(error);
916
+ errors.push(`Failed to create rules directory: ${errorMessage}`);
917
+ return { success: false, rulesCreated: 0, errors };
918
+ }
919
+ let rulesCreated = 0;
920
+ for (const rule of rules) {
921
+ try {
922
+ const baseFilename = `${tool}__${rule.filename}`;
923
+ const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
924
+ const filePath = join13(rulesDirPath, `${filename}.md`);
925
+ const content = generateRuleFileContent(rule);
926
+ await writeFileContent(filePath, content);
927
+ rulesCreated++;
928
+ if (verbose) {
929
+ console.log(`\u2705 Created rule file: ${filePath}`);
930
+ }
931
+ } catch (error) {
932
+ const errorMessage = error instanceof Error ? error.message : String(error);
933
+ errors.push(`Failed to create rule file for ${rule.filename}: ${errorMessage}`);
934
+ }
935
+ }
936
+ return {
937
+ success: rulesCreated > 0,
938
+ rulesCreated,
939
+ errors
940
+ };
941
+ }
942
+ function generateRuleFileContent(rule) {
943
+ const frontmatter = matter2.stringify("", rule.frontmatter);
944
+ return frontmatter + rule.content;
945
+ }
946
+ async function generateUniqueFilename(rulesDir, baseFilename) {
947
+ let filename = baseFilename;
948
+ let counter = 1;
949
+ while (await fileExists(join13(rulesDir, `${filename}.md`))) {
950
+ filename = `${baseFilename}-${counter}`;
951
+ counter++;
952
+ }
953
+ return filename;
954
+ }
955
+
956
+ // src/cli/commands/import.ts
957
+ async function importCommand(options = {}) {
958
+ const tools = [];
959
+ if (options.claude) tools.push("claude");
960
+ if (options.cursor) tools.push("cursor");
961
+ if (options.copilot) tools.push("copilot");
962
+ if (options.cline) tools.push("cline");
963
+ if (options.roo) tools.push("roo");
964
+ if (tools.length === 0) {
965
+ console.error(
966
+ "\u274C Please specify at least one tool to import from (--claude, --cursor, --copilot, --cline, --roo)"
967
+ );
968
+ process.exit(1);
969
+ }
970
+ console.log("Importing configuration files...");
971
+ let totalRulesCreated = 0;
972
+ const allErrors = [];
973
+ for (const tool of tools) {
974
+ if (options.verbose) {
975
+ console.log(`
976
+ Importing from ${tool}...`);
977
+ }
978
+ try {
979
+ const result = await importConfiguration({
980
+ tool,
981
+ verbose: options.verbose ?? false
982
+ });
983
+ if (result.success) {
984
+ console.log(`\u2705 Imported ${result.rulesCreated} rule(s) from ${tool}`);
985
+ totalRulesCreated += result.rulesCreated;
986
+ } else if (result.errors.length > 0) {
987
+ console.warn(`\u26A0\uFE0F Failed to import from ${tool}: ${result.errors[0]}`);
988
+ if (options.verbose) {
989
+ allErrors.push(...result.errors);
990
+ }
991
+ }
992
+ } catch (error) {
993
+ const errorMessage = error instanceof Error ? error.message : String(error);
994
+ console.error(`\u274C Error importing from ${tool}: ${errorMessage}`);
995
+ allErrors.push(`${tool}: ${errorMessage}`);
996
+ }
997
+ }
998
+ if (totalRulesCreated > 0) {
999
+ console.log(`
1000
+ \u{1F389} Successfully imported ${totalRulesCreated} rule(s) total!`);
1001
+ console.log("You can now run 'rulesync generate' to create tool-specific configurations.");
1002
+ } else {
1003
+ console.warn(
1004
+ "\n\u26A0\uFE0F No rules were imported. Please check that configuration files exist for the selected tools."
1005
+ );
1006
+ }
1007
+ if (options.verbose && allErrors.length > 0) {
1008
+ console.log("\nDetailed errors:");
1009
+ for (const error of allErrors) {
1010
+ console.log(` - ${error}`);
1011
+ }
1012
+ }
1013
+ }
1014
+
630
1015
  // src/cli/commands/init.ts
631
- import { join as join8 } from "path";
1016
+ import { join as join14 } from "path";
632
1017
  async function initCommand() {
633
1018
  const aiRulesDir = ".rulesync";
634
1019
  console.log("Initializing rulesync...");
@@ -758,7 +1143,7 @@ globs: ["src/api/**/*.ts", "src/services/**/*.ts", "src/models/**/*.ts"]
758
1143
  }
759
1144
  ];
760
1145
  for (const file of sampleFiles) {
761
- const filepath = join8(aiRulesDir, file.filename);
1146
+ const filepath = join14(aiRulesDir, file.filename);
762
1147
  if (!await fileExists(filepath)) {
763
1148
  await writeFileContent(filepath, file.content);
764
1149
  console.log(`Created ${filepath}`);
@@ -785,13 +1170,13 @@ async function statusCommand() {
785
1170
  console.log(`
786
1171
  \u{1F4CB} Rules: ${rules.length} total`);
787
1172
  if (rules.length > 0) {
788
- const highPriority = rules.filter((r) => r.frontmatter.priority === "high").length;
789
- const lowPriority = rules.filter((r) => r.frontmatter.priority === "low").length;
790
- console.log(` - High priority: ${highPriority}`);
791
- console.log(` - Low priority: ${lowPriority}`);
792
- const targetCounts = { copilot: 0, cursor: 0, cline: 0 };
1173
+ const rootRules = rules.filter((r) => r.frontmatter.root).length;
1174
+ const nonRootRules = rules.length - rootRules;
1175
+ console.log(` - Root rules: ${rootRules}`);
1176
+ console.log(` - Non-root rules: ${nonRootRules}`);
1177
+ const targetCounts = { copilot: 0, cursor: 0, cline: 0, claude: 0, roo: 0 };
793
1178
  for (const rule of rules) {
794
- const targets = rule.frontmatter.targets.includes("*") ? config.defaultTargets : rule.frontmatter.targets;
1179
+ const targets = rule.frontmatter.targets[0] === "*" ? config.defaultTargets : rule.frontmatter.targets;
795
1180
  for (const target of targets) {
796
1181
  if (target in targetCounts) {
797
1182
  targetCounts[target]++;
@@ -802,6 +1187,8 @@ async function statusCommand() {
802
1187
  console.log(` - Copilot: ${targetCounts.copilot} rules`);
803
1188
  console.log(` - Cursor: ${targetCounts.cursor} rules`);
804
1189
  console.log(` - Cline: ${targetCounts.cline} rules`);
1190
+ console.log(` - Claude: ${targetCounts.claude} rules`);
1191
+ console.log(` - Roo: ${targetCounts.roo} rules`);
805
1192
  }
806
1193
  console.log("\n\u{1F4E4} Generated files:");
807
1194
  for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
@@ -899,11 +1286,12 @@ async function watchCommand() {
899
1286
 
900
1287
  // src/cli/index.ts
901
1288
  var program = new Command();
902
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.1.0");
1289
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.20.0");
903
1290
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
904
1291
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
905
1292
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
906
- 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(
1293
+ program.command("import").description("Import configurations from AI tools to rulesync format").option("--claude", "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);
1294
+ program.command("generate").description("Generate configuration files for AI tools").option("--copilot", "Generate only for GitHub Copilot").option("--cursor", "Generate only for Cursor").option("--cline", "Generate only for Cline").option("--claude", "Generate only for Claude Code").option("--roo", "Generate only for Roo Code").option("--delete", "Delete all existing files in output directories before generating").option(
907
1295
  "-b, --base-dir <paths>",
908
1296
  "Base directories to generate files (comma-separated for multiple paths)"
909
1297
  ).option("-v, --verbose", "Verbose output").action(async (options) => {
@@ -912,6 +1300,7 @@ program.command("generate").description("Generate configuration files for AI too
912
1300
  if (options.cursor) tools.push("cursor");
913
1301
  if (options.cline) tools.push("cline");
914
1302
  if (options.claude) tools.push("claude");
1303
+ if (options.roo) tools.push("roo");
915
1304
  const generateOptions = {
916
1305
  verbose: options.verbose,
917
1306
  delete: options.delete
package/package.json CHANGED
@@ -1,19 +1,7 @@
1
1
  {
2
2
  "name": "rulesync",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
5
- "main": "dist/index.js",
6
- "module": "dist/index.mjs",
7
- "types": "dist/index.d.ts",
8
- "bin": {
9
- "rulesync": "dist/index.js"
10
- },
11
- "files": [
12
- "dist"
13
- ],
14
- "engines": {
15
- "node": ">=20.0.0"
16
- },
17
5
  "keywords": [
18
6
  "ai",
19
7
  "rules",
@@ -24,20 +12,31 @@
24
12
  "configuration",
25
13
  "development"
26
14
  ],
27
- "author": "dyoshikawa",
28
- "license": "MIT",
15
+ "homepage": "https://github.com/dyoshikawa/rulesync#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/dyoshikawa/rulesync/issues"
18
+ },
29
19
  "repository": {
30
20
  "type": "git",
31
21
  "url": "https://github.com/dyoshikawa/rulesync.git"
32
22
  },
33
- "bugs": {
34
- "url": "https://github.com/dyoshikawa/rulesync/issues"
23
+ "license": "MIT",
24
+ "author": "dyoshikawa",
25
+ "main": "dist/index.js",
26
+ "module": "dist/index.mjs",
27
+ "types": "dist/index.d.ts",
28
+ "bin": {
29
+ "rulesync": "dist/index.js"
35
30
  },
36
- "homepage": "https://github.com/dyoshikawa/rulesync#readme",
37
- "publishConfig": {
38
- "access": "public"
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "dependencies": {
35
+ "chokidar": "4.0.3",
36
+ "commander": "14.0.0",
37
+ "gray-matter": "4.0.3",
38
+ "marked": "15.0.12"
39
39
  },
40
- "packageManager": "pnpm@7.33.7",
41
40
  "devDependencies": {
42
41
  "@biomejs/biome": "2.0.0",
43
42
  "@secretlint/secretlint-rule-preset-recommend": "10.1.0",
@@ -45,29 +44,36 @@
45
44
  "@types/node": "24.0.3",
46
45
  "@vitest/coverage-v8": "3.2.4",
47
46
  "secretlint": "10.1.0",
47
+ "sort-package-json": "3.2.1",
48
48
  "tsup": "8.5.0",
49
49
  "tsx": "4.20.3",
50
50
  "typescript": "5.8.3",
51
51
  "vitest": "3.2.4"
52
52
  },
53
- "dependencies": {
54
- "chokidar": "4.0.3",
55
- "commander": "14.0.0",
56
- "gray-matter": "4.0.3",
57
- "marked": "15.0.12"
53
+ "packageManager": "pnpm@7.33.7",
54
+ "engines": {
55
+ "node": ">=20.0.0"
56
+ },
57
+ "publishConfig": {
58
+ "access": "public"
58
59
  },
59
60
  "scripts": {
60
- "dev": "tsx src/cli/index.ts",
61
+ "bcheck": "biome check src/",
62
+ "bcheck:fix": "biome check --write src/",
61
63
  "build": "tsup src/cli/index.ts --format cjs,esm --dts --clean",
64
+ "check": "pnpm run lint && pnpm run format && pnpm run bcheck && pnpm run typecheck",
65
+ "dev": "tsx src/cli/index.ts",
66
+ "fix": "pnpm run lint:fix && pnpm run format:fix && pnpm run bcheck:fix",
67
+ "format": "biome format src/",
68
+ "format:fix": "biome format --write src/",
69
+ "postinstall": "pnpm sort && pnpm secretlint && pnpm check && pnpm test",
62
70
  "lint": "biome lint src/",
63
71
  "lint:fix": "biome lint --write src/",
64
- "format": "biome format --write src/",
65
- "format:check": "biome format src/",
66
- "check": "biome check src/",
67
- "check:fix": "biome check --write src/",
68
72
  "secretlint": "secretlint \"**/*\"",
73
+ "sort": "sort-package-json",
69
74
  "test": "vitest",
75
+ "test:coverage": "vitest --coverage",
70
76
  "test:watch": "vitest --watch",
71
- "test:coverage": "vitest --coverage"
77
+ "typecheck": "tsc --noEmit"
72
78
  }
73
79
  }