rulesync 0.49.0 → 0.51.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/dist/index.cjs CHANGED
@@ -27,12 +27,16 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
  ));
28
28
 
29
29
  // src/types/tool-targets.ts
30
- var import_mini, ToolTargetSchema, ToolTargetsSchema, WildcardTargetSchema, RulesyncTargetsSchema;
30
+ function isToolTarget(target) {
31
+ if (!target) return false;
32
+ return ALL_TOOL_TARGETS.some((validTarget) => validTarget === target);
33
+ }
34
+ var import_mini, ALL_TOOL_TARGETS, ToolTargetSchema, ToolTargetsSchema, WildcardTargetSchema, RulesyncTargetsSchema;
31
35
  var init_tool_targets = __esm({
32
36
  "src/types/tool-targets.ts"() {
33
37
  "use strict";
34
38
  import_mini = require("zod/mini");
35
- ToolTargetSchema = import_mini.z.enum([
39
+ ALL_TOOL_TARGETS = [
36
40
  "augmentcode",
37
41
  "augmentcode-legacy",
38
42
  "copilot",
@@ -41,8 +45,10 @@ var init_tool_targets = __esm({
41
45
  "claudecode",
42
46
  "roo",
43
47
  "geminicli",
44
- "kiro"
45
- ]);
48
+ "kiro",
49
+ "junie"
50
+ ];
51
+ ToolTargetSchema = import_mini.z.enum(ALL_TOOL_TARGETS);
46
52
  ToolTargetsSchema = import_mini.z.array(ToolTargetSchema);
47
53
  WildcardTargetSchema = import_mini.z.tuple([import_mini.z.literal("*")]);
48
54
  RulesyncTargetsSchema = import_mini.z.union([ToolTargetsSchema, WildcardTargetSchema]);
@@ -354,6 +360,58 @@ var init_geminicli = __esm({
354
360
  }
355
361
  });
356
362
 
363
+ // src/generators/mcp/junie.ts
364
+ function generateJunieMcp(config) {
365
+ const junieConfig = {
366
+ mcpServers: {}
367
+ };
368
+ for (const [serverName, server] of Object.entries(config.mcpServers)) {
369
+ if (!shouldIncludeServer(server, "junie")) continue;
370
+ const junieServer = {
371
+ name: serverName
372
+ };
373
+ if (server.command) {
374
+ junieServer.command = server.command;
375
+ if (server.args) junieServer.args = server.args;
376
+ } else if (server.url || server.httpUrl) {
377
+ if (server.httpUrl) {
378
+ junieServer.httpUrl = server.httpUrl;
379
+ } else if (server.url) {
380
+ junieServer.url = server.url;
381
+ }
382
+ }
383
+ if (server.env) {
384
+ junieServer.env = server.env;
385
+ }
386
+ if (server.cwd) {
387
+ junieServer.workingDirectory = server.cwd;
388
+ }
389
+ if (server.timeout !== void 0) {
390
+ junieServer.timeout = server.timeout;
391
+ }
392
+ if (server.trust !== void 0) {
393
+ junieServer.trust = server.trust;
394
+ }
395
+ if (server.transport) {
396
+ if (String(server.transport) === "streamable-http") {
397
+ junieServer.transport = "http";
398
+ } else if (server.transport === "stdio" || server.transport === "http" || server.transport === "sse") {
399
+ junieServer.transport = server.transport;
400
+ }
401
+ } else if (server.command) {
402
+ junieServer.transport = "stdio";
403
+ }
404
+ junieConfig.mcpServers[serverName] = junieServer;
405
+ }
406
+ return JSON.stringify(junieConfig, null, 2);
407
+ }
408
+ var init_junie = __esm({
409
+ "src/generators/mcp/junie.ts"() {
410
+ "use strict";
411
+ init_mcp_helpers();
412
+ }
413
+ });
414
+
357
415
  // src/generators/mcp/kiro.ts
358
416
  function generateKiroMcp(config) {
359
417
  const kiroConfig = {
@@ -478,19 +536,11 @@ function getDefaultConfig() {
478
536
  claudecode: ".",
479
537
  roo: ".roo/rules",
480
538
  geminicli: ".gemini/memories",
481
- kiro: ".kiro/steering"
539
+ kiro: ".kiro/steering",
540
+ junie: "."
482
541
  },
483
542
  watchEnabled: false,
484
- defaultTargets: [
485
- "augmentcode",
486
- "copilot",
487
- "cursor",
488
- "cline",
489
- "claudecode",
490
- "roo",
491
- "geminicli",
492
- "kiro"
493
- ]
543
+ defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy")
494
544
  };
495
545
  }
496
546
  function resolveTargets(targets, config) {
@@ -537,174 +587,679 @@ async function addCommand(filename) {
537
587
  }
538
588
  }
539
589
 
540
- // src/generators/ignore/augmentcode.ts
541
- var import_node_path = require("path");
542
- async function generateAugmentCodeIgnoreFiles(rules, config, baseDir) {
543
- const outputs = [];
544
- const augmentignoreContent = generateAugmentignoreContent(rules);
545
- const outputPath = baseDir || process.cwd();
546
- const filepath = (0, import_node_path.join)(outputPath, ".augmentignore");
547
- outputs.push({
548
- tool: "augmentcode",
549
- filepath,
550
- content: augmentignoreContent
551
- });
552
- return outputs;
553
- }
554
- function generateAugmentignoreContent(rules) {
555
- const lines = [
556
- "# Generated by rulesync - AugmentCode ignore patterns",
557
- "# AugmentCode uses a two-tier approach: .gitignore first, then .augmentignore",
558
- "# This file provides Augment-specific exclusions and re-inclusions",
559
- ""
560
- ];
561
- lines.push(
562
- "# Security and Secrets (critical exclusions)",
563
- "# Environment files",
564
- ".env*",
565
- "",
566
- "# Private keys and certificates",
567
- "*.pem",
568
- "*.key",
569
- "*.p12",
570
- "*.crt",
571
- "*.der",
572
- "",
573
- "# SSH keys",
574
- "id_rsa*",
575
- "id_dsa*",
576
- "",
577
- "# AWS credentials",
578
- ".aws/",
579
- "aws-exports.js",
580
- "",
581
- "# API keys and tokens",
582
- "**/apikeys/",
583
- "**/*_token*",
584
- "**/*_secret*",
585
- ""
586
- );
587
- lines.push(
588
- "# Build Artifacts and Dependencies",
589
- "# Build outputs",
590
- "dist/",
591
- "build/",
592
- "out/",
593
- "target/",
594
- "",
595
- "# Dependencies",
596
- "node_modules/",
597
- "venv/",
598
- "*.egg-info/",
599
- "",
600
- "# Logs",
601
- "*.log",
602
- "logs/",
603
- "",
604
- "# Temporary files",
605
- "*.tmp",
606
- "*.swp",
607
- "*.swo",
608
- "*~",
609
- ""
610
- );
611
- lines.push(
612
- "# Large Files and Media",
613
- "# Binary files",
614
- "*.jar",
615
- "*.png",
616
- "*.jpg",
617
- "*.jpeg",
618
- "*.gif",
619
- "*.mp4",
620
- "*.avi",
621
- "*.zip",
622
- "*.tar.gz",
623
- "*.rar",
624
- "",
625
- "# Database files",
626
- "*.sqlite",
627
- "*.db",
628
- "*.mdb",
629
- "",
630
- "# Data files",
631
- "*.csv",
632
- "*.tsv",
633
- "*.xlsx",
634
- ""
635
- );
636
- lines.push(
637
- "# Performance Optimization",
638
- "# Exclude files that are too large for effective AI processing",
639
- "**/*.{mp4,avi,mov,mkv}",
640
- "**/*.{zip,tar,gz,rar}",
641
- "**/*.{pdf,doc,docx}",
642
- "**/logs/**/*.log",
643
- "",
644
- "# But include small configuration files",
645
- "!**/config.{json,yaml,yml}",
646
- ""
647
- );
648
- const rulePatterns = extractIgnorePatternsFromRules(rules);
649
- if (rulePatterns.length > 0) {
650
- lines.push("# Project-specific patterns from rulesync rules");
651
- lines.push(...rulePatterns);
652
- lines.push("");
590
+ // src/cli/commands/config.ts
591
+ var import_node_fs = require("fs");
592
+ var import_node_path2 = __toESM(require("path"), 1);
593
+
594
+ // src/types/claudecode.ts
595
+ var import_mini2 = require("zod/mini");
596
+ var ClaudeSettingsSchema = import_mini2.z.looseObject({
597
+ permissions: import_mini2.z._default(
598
+ import_mini2.z.looseObject({
599
+ deny: import_mini2.z._default(import_mini2.z.array(import_mini2.z.string()), [])
600
+ }),
601
+ { deny: [] }
602
+ )
603
+ });
604
+
605
+ // src/types/config.ts
606
+ var import_mini3 = require("zod/mini");
607
+ init_tool_targets();
608
+ var ConfigSchema = import_mini3.z.object({
609
+ aiRulesDir: import_mini3.z.string(),
610
+ outputPaths: import_mini3.z.record(ToolTargetSchema, import_mini3.z.string()),
611
+ watchEnabled: import_mini3.z.boolean(),
612
+ defaultTargets: ToolTargetsSchema
613
+ });
614
+
615
+ // src/types/config-options.ts
616
+ var import_mini4 = require("zod/mini");
617
+ init_tool_targets();
618
+ var OutputPathsSchema = import_mini4.z.object({
619
+ augmentcode: import_mini4.z.optional(import_mini4.z.string()),
620
+ "augmentcode-legacy": import_mini4.z.optional(import_mini4.z.string()),
621
+ copilot: import_mini4.z.optional(import_mini4.z.string()),
622
+ cursor: import_mini4.z.optional(import_mini4.z.string()),
623
+ cline: import_mini4.z.optional(import_mini4.z.string()),
624
+ claudecode: import_mini4.z.optional(import_mini4.z.string()),
625
+ roo: import_mini4.z.optional(import_mini4.z.string()),
626
+ geminicli: import_mini4.z.optional(import_mini4.z.string()),
627
+ kiro: import_mini4.z.optional(import_mini4.z.string()),
628
+ junie: import_mini4.z.optional(import_mini4.z.string())
629
+ });
630
+ var ConfigOptionsSchema = import_mini4.z.object({
631
+ aiRulesDir: import_mini4.z.optional(import_mini4.z.string()),
632
+ outputPaths: import_mini4.z.optional(OutputPathsSchema),
633
+ watchEnabled: import_mini4.z.optional(import_mini4.z.boolean()),
634
+ defaultTargets: import_mini4.z.optional(ToolTargetsSchema),
635
+ targets: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
636
+ exclude: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
637
+ verbose: import_mini4.z.optional(import_mini4.z.boolean()),
638
+ delete: import_mini4.z.optional(import_mini4.z.boolean()),
639
+ baseDir: import_mini4.z.optional(import_mini4.z.union([import_mini4.z.string(), import_mini4.z.array(import_mini4.z.string())])),
640
+ watch: import_mini4.z.optional(
641
+ import_mini4.z.object({
642
+ enabled: import_mini4.z.optional(import_mini4.z.boolean()),
643
+ interval: import_mini4.z.optional(import_mini4.z.number()),
644
+ ignore: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string()))
645
+ })
646
+ )
647
+ });
648
+ var MergedConfigSchema = import_mini4.z.object({
649
+ aiRulesDir: import_mini4.z.string(),
650
+ outputPaths: import_mini4.z.record(ToolTargetSchema, import_mini4.z.string()),
651
+ watchEnabled: import_mini4.z.boolean(),
652
+ defaultTargets: ToolTargetsSchema,
653
+ targets: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
654
+ exclude: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
655
+ verbose: import_mini4.z.optional(import_mini4.z.boolean()),
656
+ delete: import_mini4.z.optional(import_mini4.z.boolean()),
657
+ baseDir: import_mini4.z.optional(import_mini4.z.union([import_mini4.z.string(), import_mini4.z.array(import_mini4.z.string())])),
658
+ configPath: import_mini4.z.optional(import_mini4.z.string()),
659
+ watch: import_mini4.z.optional(
660
+ import_mini4.z.object({
661
+ enabled: import_mini4.z.optional(import_mini4.z.boolean()),
662
+ interval: import_mini4.z.optional(import_mini4.z.number()),
663
+ ignore: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string()))
664
+ })
665
+ )
666
+ });
667
+
668
+ // src/types/mcp.ts
669
+ var import_mini5 = require("zod/mini");
670
+ init_tool_targets();
671
+ var McpTransportTypeSchema = import_mini5.z.enum(["stdio", "sse", "http"]);
672
+ var McpServerBaseSchema = import_mini5.z.object({
673
+ command: import_mini5.z.optional(import_mini5.z.string()),
674
+ args: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
675
+ url: import_mini5.z.optional(import_mini5.z.string()),
676
+ httpUrl: import_mini5.z.optional(import_mini5.z.string()),
677
+ env: import_mini5.z.optional(import_mini5.z.record(import_mini5.z.string(), import_mini5.z.string())),
678
+ disabled: import_mini5.z.optional(import_mini5.z.boolean()),
679
+ networkTimeout: import_mini5.z.optional(import_mini5.z.number()),
680
+ timeout: import_mini5.z.optional(import_mini5.z.number()),
681
+ trust: import_mini5.z.optional(import_mini5.z.boolean()),
682
+ cwd: import_mini5.z.optional(import_mini5.z.string()),
683
+ transport: import_mini5.z.optional(McpTransportTypeSchema),
684
+ type: import_mini5.z.optional(import_mini5.z.enum(["sse", "streamable-http"])),
685
+ alwaysAllow: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
686
+ tools: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
687
+ kiroAutoApprove: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
688
+ kiroAutoBlock: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string()))
689
+ });
690
+ var RulesyncMcpServerSchema = import_mini5.z.extend(McpServerBaseSchema, {
691
+ targets: import_mini5.z.optional(RulesyncTargetsSchema)
692
+ });
693
+ var McpConfigSchema = import_mini5.z.object({
694
+ mcpServers: import_mini5.z.record(import_mini5.z.string(), McpServerBaseSchema)
695
+ });
696
+ var RulesyncMcpConfigSchema = import_mini5.z.object({
697
+ mcpServers: import_mini5.z.record(import_mini5.z.string(), RulesyncMcpServerSchema)
698
+ });
699
+
700
+ // src/types/rules.ts
701
+ var import_mini6 = require("zod/mini");
702
+ init_tool_targets();
703
+ var RuleFrontmatterSchema = import_mini6.z.object({
704
+ root: import_mini6.z.boolean(),
705
+ targets: RulesyncTargetsSchema,
706
+ description: import_mini6.z.string(),
707
+ globs: import_mini6.z.array(import_mini6.z.string()),
708
+ cursorRuleType: import_mini6.z.optional(import_mini6.z.enum(["always", "manual", "specificFiles", "intelligently"])),
709
+ tags: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string()))
710
+ });
711
+ var ParsedRuleSchema = import_mini6.z.object({
712
+ frontmatter: RuleFrontmatterSchema,
713
+ content: import_mini6.z.string(),
714
+ filename: import_mini6.z.string(),
715
+ filepath: import_mini6.z.string()
716
+ });
717
+ var GeneratedOutputSchema = import_mini6.z.object({
718
+ tool: ToolTargetSchema,
719
+ filepath: import_mini6.z.string(),
720
+ content: import_mini6.z.string()
721
+ });
722
+ var GenerateOptionsSchema = import_mini6.z.object({
723
+ targetTools: import_mini6.z.optional(ToolTargetsSchema),
724
+ outputDir: import_mini6.z.optional(import_mini6.z.string()),
725
+ watch: import_mini6.z.optional(import_mini6.z.boolean())
726
+ });
727
+
728
+ // src/types/index.ts
729
+ init_tool_targets();
730
+
731
+ // src/utils/config-loader.ts
732
+ var import_c12 = require("c12");
733
+ var import_core = require("zod/v4/core");
734
+ var MODULE_NAME = "rulesync";
735
+ async function loadConfig(options = {}) {
736
+ const defaultConfig = getDefaultConfig();
737
+ if (options.noConfig) {
738
+ return {
739
+ config: defaultConfig,
740
+ isEmpty: true
741
+ };
653
742
  }
654
- lines.push(
655
- "# Team Collaboration",
656
- "# Exclude personal IDE settings",
657
- ".vscode/settings.json",
658
- ".idea/workspace.xml",
659
- "",
660
- "# But include shared team settings",
661
- "!.vscode/extensions.json",
662
- "!.idea/codeStyles/",
663
- "",
664
- "# Exclude test fixtures with sensitive data",
665
- "tests/fixtures/real-data/**",
666
- "",
667
- "# Re-include important documentation",
668
- "!vendor/*/README.md",
669
- "!third-party/*/LICENSE",
670
- ""
671
- );
672
- return lines.join("\n");
673
- }
674
- function extractIgnorePatternsFromRules(rules) {
675
- const patterns = [];
676
- for (const rule of rules) {
677
- if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
678
- for (const glob of rule.frontmatter.globs) {
679
- if (shouldExcludeFromAugmentCode(glob)) {
680
- patterns.push(`# Exclude: ${rule.frontmatter.description}`);
681
- patterns.push(glob);
682
- }
743
+ try {
744
+ const loadOptions = {
745
+ name: MODULE_NAME,
746
+ cwd: options.cwd || process.cwd(),
747
+ rcFile: false,
748
+ // Disable rc file lookup
749
+ configFile: "rulesync",
750
+ // Will look for rulesync.jsonc, rulesync.ts, etc.
751
+ defaults: defaultConfig
752
+ };
753
+ if (options.configPath) {
754
+ loadOptions.configFile = options.configPath;
755
+ }
756
+ const { config, configFile } = await (0, import_c12.loadConfig)(loadOptions);
757
+ if (!config || Object.keys(config).length === 0) {
758
+ return {
759
+ config: defaultConfig,
760
+ isEmpty: true
761
+ };
762
+ }
763
+ try {
764
+ ConfigOptionsSchema.parse(config);
765
+ } catch (error) {
766
+ if (error instanceof import_core.$ZodError) {
767
+ const issues = error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
768
+ throw new Error(`Invalid configuration in ${configFile}:
769
+ ${issues}`);
683
770
  }
771
+ throw error;
684
772
  }
685
- const contentPatterns = extractAugmentCodeIgnorePatternsFromContent(rule.content);
686
- patterns.push(...contentPatterns);
773
+ const processedConfig = postProcessConfig(config);
774
+ const result = {
775
+ config: processedConfig,
776
+ isEmpty: false
777
+ };
778
+ if (configFile) {
779
+ result.filepath = configFile;
780
+ }
781
+ return result;
782
+ } catch (error) {
783
+ throw new Error(
784
+ `Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`
785
+ );
687
786
  }
688
- return patterns;
689
787
  }
690
- function shouldExcludeFromAugmentCode(glob) {
691
- const excludePatterns = [
692
- // Large generated files that slow indexing
693
- "**/assets/generated/**",
694
- "**/public/build/**",
695
- // Test fixtures with potentially sensitive data
696
- "**/tests/fixtures/**",
697
- "**/test/fixtures/**",
698
- "**/*.fixture.*",
699
- // Build outputs that provide little value for AI context
788
+ function postProcessConfig(config) {
789
+ const processed = { ...config };
790
+ if (processed.baseDir && !Array.isArray(processed.baseDir)) {
791
+ processed.baseDir = [processed.baseDir];
792
+ }
793
+ if (config.targets || config.exclude) {
794
+ const baseTargets = config.targets || processed.defaultTargets;
795
+ if (config.exclude && config.exclude.length > 0) {
796
+ processed.defaultTargets = baseTargets.filter(
797
+ (target) => config.exclude && !config.exclude.includes(target)
798
+ );
799
+ } else {
800
+ processed.defaultTargets = baseTargets;
801
+ }
802
+ }
803
+ return processed;
804
+ }
805
+ function generateMinimalConfig(options) {
806
+ if (!options || Object.keys(options).length === 0) {
807
+ return generateSampleConfig();
808
+ }
809
+ const lines = ["{"];
810
+ if (options.targets || options.exclude) {
811
+ lines.push(` // Available tools: ${ALL_TOOL_TARGETS.join(", ")}`);
812
+ }
813
+ if (options.targets) {
814
+ lines.push(` "targets": ${JSON.stringify(options.targets)}`);
815
+ }
816
+ if (options.exclude) {
817
+ const comma = lines.length > 1 ? "," : "";
818
+ if (comma) lines[lines.length - 1] += comma;
819
+ lines.push(` "exclude": ${JSON.stringify(options.exclude)}`);
820
+ }
821
+ if (options.aiRulesDir) {
822
+ const comma = lines.length > 1 ? "," : "";
823
+ if (comma) lines[lines.length - 1] += comma;
824
+ lines.push(` "aiRulesDir": "${options.aiRulesDir}"`);
825
+ }
826
+ if (options.outputPaths) {
827
+ const comma = lines.length > 1 ? "," : "";
828
+ if (comma) lines[lines.length - 1] += comma;
829
+ lines.push(
830
+ ` "outputPaths": ${JSON.stringify(options.outputPaths, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n")}`
831
+ );
832
+ }
833
+ if (options.baseDir) {
834
+ const comma = lines.length > 1 ? "," : "";
835
+ if (comma) lines[lines.length - 1] += comma;
836
+ lines.push(` "baseDir": ${JSON.stringify(options.baseDir)}`);
837
+ }
838
+ if (options.delete !== void 0) {
839
+ const comma = lines.length > 1 ? "," : "";
840
+ if (comma) lines[lines.length - 1] += comma;
841
+ lines.push(` "delete": ${options.delete}`);
842
+ }
843
+ if (options.verbose !== void 0) {
844
+ const comma = lines.length > 1 ? "," : "";
845
+ if (comma) lines[lines.length - 1] += comma;
846
+ lines.push(` "verbose": ${options.verbose}`);
847
+ }
848
+ if (options.watch) {
849
+ const comma = lines.length > 1 ? "," : "";
850
+ if (comma) lines[lines.length - 1] += comma;
851
+ lines.push(
852
+ ` "watch": ${JSON.stringify(options.watch, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n")}`
853
+ );
854
+ }
855
+ lines.push("}");
856
+ return lines.join("\n");
857
+ }
858
+ function generateSampleConfig(options) {
859
+ const targets = options?.targets || ALL_TOOL_TARGETS;
860
+ const excludeValue = options?.exclude ? JSON.stringify(options.exclude) : null;
861
+ const aiRulesDir = options?.aiRulesDir || null;
862
+ const baseDir = options?.baseDir || null;
863
+ const deleteFlag = options?.delete || false;
864
+ const verbose = options?.verbose !== void 0 ? options.verbose : true;
865
+ return `{
866
+ // List of tools to generate configurations for
867
+ // Available: ${ALL_TOOL_TARGETS.join(", ")}
868
+ "targets": ${JSON.stringify(targets)},
869
+
870
+ // Tools to exclude from generation (overrides targets)
871
+ ${excludeValue ? `"exclude": ${excludeValue},` : '// "exclude": ["roo"],'}
872
+ ${aiRulesDir ? `
873
+ // Directory containing AI rule files
874
+ "aiRulesDir": "${aiRulesDir}",` : ""}
875
+
876
+ // Custom output paths for specific tools
877
+ "outputPaths": {
878
+ "copilot": ".github/copilot-instructions.md"
879
+ },
880
+ ${baseDir ? `
881
+ // Base directory for generation
882
+ "baseDir": "${baseDir}",` : `
883
+ // Base directory or directories for generation
884
+ // "baseDir": "./packages",
885
+ // "baseDir": ["./packages/frontend", "./packages/backend"],`}
886
+
887
+ // Delete existing files before generating
888
+ "delete": ${deleteFlag},
889
+
890
+ // Enable verbose output
891
+ "verbose": ${verbose},
892
+
893
+ // Watch configuration
894
+ "watch": {
895
+ "enabled": false,
896
+ "interval": 1000,
897
+ "ignore": ["node_modules/**", "dist/**"]
898
+ }
899
+ }
900
+ `;
901
+ }
902
+ function mergeWithCliOptions(config, cliOptions) {
903
+ const merged = { ...config };
904
+ if (cliOptions.verbose !== void 0) {
905
+ merged.verbose = cliOptions.verbose;
906
+ }
907
+ if (cliOptions.delete !== void 0) {
908
+ merged.delete = cliOptions.delete;
909
+ }
910
+ if (cliOptions.baseDirs && cliOptions.baseDirs.length > 0) {
911
+ merged.baseDir = cliOptions.baseDirs;
912
+ }
913
+ if (cliOptions.tools && cliOptions.tools.length > 0) {
914
+ merged.defaultTargets = cliOptions.tools;
915
+ merged.exclude = void 0;
916
+ }
917
+ return merged;
918
+ }
919
+
920
+ // src/utils/file.ts
921
+ var import_promises2 = require("fs/promises");
922
+ var import_node_path = require("path");
923
+ async function ensureDir(dirPath) {
924
+ try {
925
+ await (0, import_promises2.stat)(dirPath);
926
+ } catch {
927
+ await (0, import_promises2.mkdir)(dirPath, { recursive: true });
928
+ }
929
+ }
930
+ async function readFileContent(filepath) {
931
+ return (0, import_promises2.readFile)(filepath, "utf-8");
932
+ }
933
+ async function writeFileContent(filepath, content) {
934
+ await ensureDir((0, import_node_path.dirname)(filepath));
935
+ await (0, import_promises2.writeFile)(filepath, content, "utf-8");
936
+ }
937
+ async function fileExists(filepath) {
938
+ try {
939
+ await (0, import_promises2.stat)(filepath);
940
+ return true;
941
+ } catch {
942
+ return false;
943
+ }
944
+ }
945
+ async function findFiles(dir, extension = ".md") {
946
+ try {
947
+ const files = await (0, import_promises2.readdir)(dir);
948
+ return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path.join)(dir, file));
949
+ } catch {
950
+ return [];
951
+ }
952
+ }
953
+ async function removeDirectory(dirPath) {
954
+ const dangerousPaths = [".", "/", "~", "src", "node_modules"];
955
+ if (dangerousPaths.includes(dirPath) || dirPath === "") {
956
+ console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
957
+ return;
958
+ }
959
+ try {
960
+ if (await fileExists(dirPath)) {
961
+ await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
962
+ }
963
+ } catch (error) {
964
+ console.warn(`Failed to remove directory ${dirPath}:`, error);
965
+ }
966
+ }
967
+ async function removeFile(filepath) {
968
+ try {
969
+ if (await fileExists(filepath)) {
970
+ await (0, import_promises2.rm)(filepath);
971
+ }
972
+ } catch (error) {
973
+ console.warn(`Failed to remove file ${filepath}:`, error);
974
+ }
975
+ }
976
+ async function removeClaudeGeneratedFiles() {
977
+ const filesToRemove = ["CLAUDE.md", ".claude/memories"];
978
+ for (const fileOrDir of filesToRemove) {
979
+ if (fileOrDir.endsWith("/memories")) {
980
+ await removeDirectory(fileOrDir);
981
+ } else {
982
+ await removeFile(fileOrDir);
983
+ }
984
+ }
985
+ }
986
+
987
+ // src/utils/rules.ts
988
+ function isToolSpecificRule(rule, targetTool) {
989
+ const filename = rule.filename;
990
+ const toolPatterns = {
991
+ "augmentcode-legacy": /^specification-augmentcode-legacy-/i,
992
+ augmentcode: /^specification-augmentcode-/i,
993
+ copilot: /^specification-copilot-/i,
994
+ cursor: /^specification-cursor-/i,
995
+ cline: /^specification-cline-/i,
996
+ claudecode: /^specification-claudecode-/i,
997
+ roo: /^specification-roo-/i,
998
+ geminicli: /^specification-geminicli-/i,
999
+ kiro: /^specification-kiro-/i
1000
+ };
1001
+ for (const [tool, pattern] of Object.entries(toolPatterns)) {
1002
+ if (pattern.test(filename)) {
1003
+ return tool === targetTool;
1004
+ }
1005
+ }
1006
+ return true;
1007
+ }
1008
+
1009
+ // src/cli/commands/config.ts
1010
+ async function configCommand(options = {}) {
1011
+ if (options.init) {
1012
+ await initConfig(options);
1013
+ return;
1014
+ }
1015
+ await showConfig();
1016
+ }
1017
+ async function showConfig() {
1018
+ console.log("Loading configuration...\n");
1019
+ try {
1020
+ const result = await loadConfig();
1021
+ if (result.isEmpty) {
1022
+ console.log("No configuration file found. Using default configuration.\n");
1023
+ } else {
1024
+ console.log(`Configuration loaded from: ${result.filepath}
1025
+ `);
1026
+ }
1027
+ console.log("Current configuration:");
1028
+ console.log("=====================");
1029
+ const config = result.config;
1030
+ console.log(`
1031
+ AI Rules Directory: ${config.aiRulesDir}`);
1032
+ console.log(`
1033
+ Default Targets: ${config.defaultTargets.join(", ")}`);
1034
+ if (config.exclude && config.exclude.length > 0) {
1035
+ console.log(`Excluded Targets: ${config.exclude.join(", ")}`);
1036
+ }
1037
+ console.log("\nOutput Paths:");
1038
+ for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
1039
+ console.log(` ${tool}: ${outputPath}`);
1040
+ }
1041
+ if (config.baseDir) {
1042
+ const dirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
1043
+ console.log(`
1044
+ Base Directories: ${dirs.join(", ")}`);
1045
+ }
1046
+ console.log(`
1047
+ Verbose: ${config.verbose || false}`);
1048
+ console.log(`Delete before generate: ${config.delete || false}`);
1049
+ if (config.watch) {
1050
+ console.log("\nWatch Configuration:");
1051
+ console.log(` Enabled: ${config.watch.enabled || false}`);
1052
+ if (config.watch.interval) {
1053
+ console.log(` Interval: ${config.watch.interval}ms`);
1054
+ }
1055
+ if (config.watch.ignore && config.watch.ignore.length > 0) {
1056
+ console.log(` Ignore patterns: ${config.watch.ignore.join(", ")}`);
1057
+ }
1058
+ }
1059
+ console.log("\nTip: Use 'rulesync config init' to create a configuration file.");
1060
+ } catch (error) {
1061
+ console.error(
1062
+ "\u274C Failed to load configuration:",
1063
+ error instanceof Error ? error.message : String(error)
1064
+ );
1065
+ process.exit(1);
1066
+ }
1067
+ }
1068
+ var FORMAT_CONFIG = {
1069
+ jsonc: {
1070
+ filename: "rulesync.jsonc",
1071
+ generator: generateJsoncConfig
1072
+ },
1073
+ ts: {
1074
+ filename: "rulesync.ts",
1075
+ generator: generateTsConfig
1076
+ }
1077
+ };
1078
+ async function initConfig(options) {
1079
+ const validFormats = Object.keys(FORMAT_CONFIG);
1080
+ const selectedFormat = options.format || "jsonc";
1081
+ if (!validFormats.includes(selectedFormat)) {
1082
+ console.error(
1083
+ `\u274C Invalid format: ${selectedFormat}. Valid formats are: ${validFormats.join(", ")}`
1084
+ );
1085
+ process.exit(1);
1086
+ }
1087
+ const formatConfig = FORMAT_CONFIG[selectedFormat];
1088
+ const filename = formatConfig.filename;
1089
+ const configOptions = {};
1090
+ if (options.targets) {
1091
+ const targets = options.targets.split(",").map((t) => t.trim());
1092
+ const validTargets = [];
1093
+ for (const target of targets) {
1094
+ const result = ToolTargetSchema.safeParse(target);
1095
+ if (result.success) {
1096
+ validTargets.push(result.data);
1097
+ } else {
1098
+ console.error(`\u274C Invalid target: ${target}`);
1099
+ process.exit(1);
1100
+ }
1101
+ }
1102
+ configOptions.targets = validTargets;
1103
+ }
1104
+ if (options.exclude) {
1105
+ const excludes = options.exclude.split(",").map((t) => t.trim());
1106
+ const validExcludes = [];
1107
+ for (const exclude of excludes) {
1108
+ const result = ToolTargetSchema.safeParse(exclude);
1109
+ if (result.success) {
1110
+ validExcludes.push(result.data);
1111
+ } else {
1112
+ console.error(`\u274C Invalid exclude target: ${exclude}`);
1113
+ process.exit(1);
1114
+ }
1115
+ }
1116
+ configOptions.exclude = validExcludes;
1117
+ }
1118
+ if (options.aiRulesDir) {
1119
+ configOptions.aiRulesDir = options.aiRulesDir;
1120
+ }
1121
+ if (options.baseDir) {
1122
+ configOptions.baseDir = options.baseDir;
1123
+ }
1124
+ if (options.verbose !== void 0) {
1125
+ configOptions.verbose = options.verbose;
1126
+ }
1127
+ if (options.delete !== void 0) {
1128
+ configOptions.delete = options.delete;
1129
+ }
1130
+ const content = formatConfig.generator(configOptions);
1131
+ const filepath = import_node_path2.default.join(process.cwd(), filename);
1132
+ try {
1133
+ const fs2 = await import("fs/promises");
1134
+ await fs2.access(filepath);
1135
+ console.error(`\u274C Configuration file already exists: ${filepath}`);
1136
+ console.log("Remove the existing file or choose a different format.");
1137
+ process.exit(1);
1138
+ } catch {
1139
+ }
1140
+ try {
1141
+ (0, import_node_fs.writeFileSync)(filepath, content, "utf-8");
1142
+ console.log(`\u2705 Created configuration file: ${filepath}`);
1143
+ console.log("\nYou can now customize the configuration to fit your needs.");
1144
+ console.log("Run 'rulesync generate' to use the new configuration.");
1145
+ } catch (error) {
1146
+ console.error(
1147
+ `\u274C Failed to create configuration file: ${error instanceof Error ? error.message : String(error)}`
1148
+ );
1149
+ process.exit(1);
1150
+ }
1151
+ }
1152
+ function generateJsoncConfig(options) {
1153
+ if (options && Object.keys(options).length > 0) {
1154
+ return generateMinimalConfig(options);
1155
+ }
1156
+ return generateSampleConfig(options);
1157
+ }
1158
+ function generateTsConfig(options) {
1159
+ if (!options || Object.keys(options).length === 0) {
1160
+ return `import type { ConfigOptions } from "rulesync";
1161
+
1162
+ const config: ConfigOptions = {
1163
+ // List of tools to generate configurations for
1164
+ // Available: ${ALL_TOOL_TARGETS.join(", ")}
1165
+ targets: ${JSON.stringify(ALL_TOOL_TARGETS)},
1166
+
1167
+ // Custom output paths for specific tools
1168
+ // outputPaths: {
1169
+ // copilot: ".github/copilot-instructions.md",
1170
+ // },
1171
+
1172
+ // Delete existing files before generating
1173
+ // delete: false,
1174
+
1175
+ // Enable verbose output
1176
+ verbose: true,
1177
+ };
1178
+
1179
+ export default config;`;
1180
+ }
1181
+ const configLines = [];
1182
+ if (options.targets) {
1183
+ configLines.push(` targets: ${JSON.stringify(options.targets)}`);
1184
+ }
1185
+ if (options.exclude) {
1186
+ configLines.push(` exclude: ${JSON.stringify(options.exclude)}`);
1187
+ }
1188
+ if (options.aiRulesDir) {
1189
+ configLines.push(` aiRulesDir: "${options.aiRulesDir}"`);
1190
+ }
1191
+ if (options.outputPaths) {
1192
+ const pathsStr = JSON.stringify(options.outputPaths, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
1193
+ configLines.push(` outputPaths: ${pathsStr}`);
1194
+ }
1195
+ if (options.baseDir) {
1196
+ configLines.push(` baseDir: ${JSON.stringify(options.baseDir)}`);
1197
+ }
1198
+ if (options.delete !== void 0) {
1199
+ configLines.push(` delete: ${options.delete}`);
1200
+ }
1201
+ if (options.verbose !== void 0) {
1202
+ configLines.push(` verbose: ${options.verbose}`);
1203
+ }
1204
+ if (options.watch) {
1205
+ const watchStr = JSON.stringify(options.watch, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
1206
+ configLines.push(` watch: ${watchStr}`);
1207
+ }
1208
+ const configContent = `import type { ConfigOptions } from "rulesync";
1209
+
1210
+ const config: ConfigOptions = {
1211
+ ${configLines.join(",\n")},
1212
+ };
1213
+
1214
+ export default config;
1215
+ `;
1216
+ return configContent;
1217
+ }
1218
+
1219
+ // src/cli/commands/generate.ts
1220
+ var import_node_path14 = require("path");
1221
+
1222
+ // src/generators/ignore/augmentcode.ts
1223
+ var import_node_path3 = require("path");
1224
+
1225
+ // src/generators/ignore/shared-helpers.ts
1226
+ function extractIgnorePatternsFromRules(rules) {
1227
+ const patterns = [];
1228
+ for (const rule of rules) {
1229
+ if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
1230
+ for (const glob of rule.frontmatter.globs) {
1231
+ if (shouldExcludeFromAI(glob)) {
1232
+ patterns.push(`# Exclude: ${rule.frontmatter.description}`);
1233
+ patterns.push(glob);
1234
+ }
1235
+ }
1236
+ }
1237
+ const contentPatterns = extractIgnorePatternsFromContent(rule.content);
1238
+ patterns.push(...contentPatterns);
1239
+ }
1240
+ return patterns;
1241
+ }
1242
+ function shouldExcludeFromAI(glob) {
1243
+ const excludePatterns = [
1244
+ // Large generated files that slow indexing
1245
+ "**/assets/generated/**",
1246
+ "**/public/build/**",
1247
+ // Test fixtures with potentially sensitive data
1248
+ "**/tests/fixtures/**",
1249
+ "**/test/fixtures/**",
1250
+ "**/*.fixture.*",
1251
+ // Build outputs that provide little value for AI context
700
1252
  "**/dist/**",
701
1253
  "**/build/**",
702
1254
  "**/coverage/**",
703
1255
  // Configuration that might contain sensitive data
704
1256
  "**/config/production/**",
705
1257
  "**/config/secrets/**",
1258
+ "**/config/prod/**",
706
1259
  "**/deploy/prod/**",
707
- // Internal documentation
1260
+ "**/*.prod.*",
1261
+ // Internal documentation that might be sensitive
1262
+ "**/internal/**",
708
1263
  "**/internal-docs/**",
709
1264
  "**/proprietary/**",
710
1265
  "**/personal-notes/**",
@@ -716,11 +1271,17 @@ function shouldExcludeFromAugmentCode(glob) {
716
1271
  return regex.test(glob);
717
1272
  });
718
1273
  }
719
- function extractAugmentCodeIgnorePatternsFromContent(content) {
1274
+ function extractIgnorePatternsFromContent(content) {
720
1275
  const patterns = [];
721
1276
  const lines = content.split("\n");
722
1277
  for (const line of lines) {
723
1278
  const trimmed = line.trim();
1279
+ if (trimmed.startsWith("# IGNORE:") || trimmed.startsWith("# aiignore:")) {
1280
+ const pattern = trimmed.replace(/^# (IGNORE|aiignore):\s*/, "").trim();
1281
+ if (pattern) {
1282
+ patterns.push(pattern);
1283
+ }
1284
+ }
724
1285
  if (trimmed.startsWith("# AUGMENT_IGNORE:") || trimmed.startsWith("# augmentignore:")) {
725
1286
  const pattern = trimmed.replace(/^# (AUGMENT_IGNORE|augmentignore):\s*/, "").trim();
726
1287
  if (pattern) {
@@ -733,8 +1294,8 @@ function extractAugmentCodeIgnorePatternsFromContent(content) {
733
1294
  patterns.push(`!${pattern}`);
734
1295
  }
735
1296
  }
736
- if (trimmed.includes("large file") || trimmed.includes("binary") || trimmed.includes("media")) {
737
- const matches = trimmed.match(/['"`]([^'"`]+\.(mp4|avi|zip|tar\.gz|rar|pdf|doc|xlsx))['"`]/g);
1297
+ if (trimmed.includes("exclude") || trimmed.includes("ignore")) {
1298
+ const matches = trimmed.match(/['"`]([^'"`]+\.(log|tmp|cache|temp))['"`]/g);
738
1299
  if (matches) {
739
1300
  patterns.push(...matches.map((m) => m.replace(/['"`]/g, "")));
740
1301
  }
@@ -742,14 +1303,330 @@ function extractAugmentCodeIgnorePatternsFromContent(content) {
742
1303
  }
743
1304
  return patterns;
744
1305
  }
1306
+ function extractAugmentCodeIgnorePatternsFromContent(content) {
1307
+ const patterns = [];
1308
+ const lines = content.split("\n");
1309
+ for (const line of lines) {
1310
+ const trimmed = line.trim();
1311
+ if (trimmed.startsWith("# AUGMENT_IGNORE:") || trimmed.startsWith("# augmentignore:")) {
1312
+ const pattern = trimmed.replace(/^# (AUGMENT_IGNORE|augmentignore):\s*/, "").trim();
1313
+ if (pattern) {
1314
+ patterns.push(pattern);
1315
+ }
1316
+ }
1317
+ if (trimmed.startsWith("# AUGMENT_INCLUDE:") || trimmed.startsWith("# augmentinclude:")) {
1318
+ const pattern = trimmed.replace(/^# (AUGMENT_INCLUDE|augmentinclude):\s*/, "").trim();
1319
+ if (pattern) {
1320
+ patterns.push(`!${pattern}`);
1321
+ }
1322
+ }
1323
+ if (trimmed.includes("large file") || trimmed.includes("binary") || trimmed.includes("media")) {
1324
+ const regex = /['"`]([^'"`]+\.(mp4|avi|zip|tar\.gz|rar|pdf|doc|xlsx))['"`]/g;
1325
+ let match;
1326
+ while ((match = regex.exec(trimmed)) !== null) {
1327
+ if (match[1]) {
1328
+ patterns.push(match[1]);
1329
+ }
1330
+ }
1331
+ }
1332
+ }
1333
+ return patterns;
1334
+ }
1335
+
1336
+ // src/generators/ignore/augmentcode.ts
1337
+ async function generateAugmentCodeIgnoreFiles(rules, config, baseDir) {
1338
+ const outputs = [];
1339
+ const augmentignoreContent = generateAugmentignoreContent(rules);
1340
+ const outputPath = baseDir || process.cwd();
1341
+ const filepath = (0, import_node_path3.join)(outputPath, ".augmentignore");
1342
+ outputs.push({
1343
+ tool: "augmentcode",
1344
+ filepath,
1345
+ content: augmentignoreContent
1346
+ });
1347
+ return outputs;
1348
+ }
1349
+ function generateAugmentignoreContent(rules) {
1350
+ const lines = [
1351
+ "# Generated by rulesync - AugmentCode ignore patterns",
1352
+ "# AugmentCode uses a two-tier approach: .gitignore first, then .augmentignore",
1353
+ "# This file provides Augment-specific exclusions and re-inclusions",
1354
+ ""
1355
+ ];
1356
+ lines.push(
1357
+ "# Security and Secrets (critical exclusions)",
1358
+ "# Environment files",
1359
+ ".env*",
1360
+ "",
1361
+ "# Private keys and certificates",
1362
+ "*.pem",
1363
+ "*.key",
1364
+ "*.p12",
1365
+ "*.crt",
1366
+ "*.der",
1367
+ "",
1368
+ "# SSH keys",
1369
+ "id_rsa*",
1370
+ "id_dsa*",
1371
+ "",
1372
+ "# AWS credentials",
1373
+ ".aws/",
1374
+ "aws-exports.js",
1375
+ "",
1376
+ "# API keys and tokens",
1377
+ "**/apikeys/",
1378
+ "**/*_token*",
1379
+ "**/*_secret*",
1380
+ ""
1381
+ );
1382
+ lines.push(
1383
+ "# Build Artifacts and Dependencies",
1384
+ "# Build outputs",
1385
+ "dist/",
1386
+ "build/",
1387
+ "out/",
1388
+ "target/",
1389
+ "",
1390
+ "# Dependencies",
1391
+ "node_modules/",
1392
+ "venv/",
1393
+ "*.egg-info/",
1394
+ "",
1395
+ "# Logs",
1396
+ "*.log",
1397
+ "logs/",
1398
+ "",
1399
+ "# Temporary files",
1400
+ "*.tmp",
1401
+ "*.swp",
1402
+ "*.swo",
1403
+ "*~",
1404
+ ""
1405
+ );
1406
+ lines.push(
1407
+ "# Large Files and Media",
1408
+ "# Binary files",
1409
+ "*.jar",
1410
+ "*.png",
1411
+ "*.jpg",
1412
+ "*.jpeg",
1413
+ "*.gif",
1414
+ "*.mp4",
1415
+ "*.avi",
1416
+ "*.zip",
1417
+ "*.tar.gz",
1418
+ "*.rar",
1419
+ "",
1420
+ "# Database files",
1421
+ "*.sqlite",
1422
+ "*.db",
1423
+ "*.mdb",
1424
+ "",
1425
+ "# Data files",
1426
+ "*.csv",
1427
+ "*.tsv",
1428
+ "*.xlsx",
1429
+ ""
1430
+ );
1431
+ lines.push(
1432
+ "# Performance Optimization",
1433
+ "# Exclude files that are too large for effective AI processing",
1434
+ "**/*.{mp4,avi,mov,mkv}",
1435
+ "**/*.{zip,tar,gz,rar}",
1436
+ "**/*.{pdf,doc,docx}",
1437
+ "**/logs/**/*.log",
1438
+ "",
1439
+ "# But include small configuration files",
1440
+ "!**/config.{json,yaml,yml}",
1441
+ ""
1442
+ );
1443
+ const rulePatterns = extractIgnorePatternsFromRules(rules);
1444
+ const augmentPatterns = [];
1445
+ for (const rule of rules) {
1446
+ augmentPatterns.push(...extractAugmentCodeIgnorePatternsFromContent(rule.content));
1447
+ }
1448
+ const allPatterns = [...rulePatterns, ...augmentPatterns];
1449
+ if (allPatterns.length > 0) {
1450
+ lines.push("# Project-specific patterns from rulesync rules");
1451
+ lines.push(...allPatterns);
1452
+ lines.push("");
1453
+ }
1454
+ lines.push(
1455
+ "# Team Collaboration",
1456
+ "# Exclude personal IDE settings",
1457
+ ".vscode/settings.json",
1458
+ ".idea/workspace.xml",
1459
+ "",
1460
+ "# But include shared team settings",
1461
+ "!.vscode/extensions.json",
1462
+ "!.idea/codeStyles/",
1463
+ "",
1464
+ "# Exclude test fixtures with sensitive data",
1465
+ "tests/fixtures/real-data/**",
1466
+ "",
1467
+ "# Re-include important documentation",
1468
+ "!vendor/*/README.md",
1469
+ "!third-party/*/LICENSE",
1470
+ ""
1471
+ );
1472
+ return lines.join("\n");
1473
+ }
1474
+
1475
+ // src/generators/ignore/junie.ts
1476
+ var import_node_path4 = require("path");
1477
+ async function generateJunieIgnoreFiles(rules, config, baseDir) {
1478
+ const outputs = [];
1479
+ const aiignoreContent = generateAiignoreContent(rules);
1480
+ const outputPath = baseDir || process.cwd();
1481
+ const filepath = (0, import_node_path4.join)(outputPath, ".aiignore");
1482
+ outputs.push({
1483
+ tool: "junie",
1484
+ filepath,
1485
+ content: aiignoreContent
1486
+ });
1487
+ return outputs;
1488
+ }
1489
+ function generateAiignoreContent(rules) {
1490
+ const lines = [
1491
+ "# Generated by rulesync - JetBrains Junie AI ignore file",
1492
+ "# This file controls which files the AI can access automatically",
1493
+ "# AI must ask before reading or editing matched files/directories",
1494
+ "",
1495
+ "# \u2500\u2500\u2500\u2500\u2500 Source Control Metadata \u2500\u2500\u2500\u2500\u2500",
1496
+ ".git/",
1497
+ ".svn/",
1498
+ ".hg/",
1499
+ ".idea/",
1500
+ "*.iml",
1501
+ ".vscode/settings.json",
1502
+ "",
1503
+ "# \u2500\u2500\u2500\u2500\u2500 Build Artifacts \u2500\u2500\u2500\u2500\u2500",
1504
+ "/out/",
1505
+ "/dist/",
1506
+ "/target/",
1507
+ "/build/",
1508
+ "*.class",
1509
+ "*.jar",
1510
+ "*.war",
1511
+ "",
1512
+ "# \u2500\u2500\u2500\u2500\u2500 Secrets & Credentials \u2500\u2500\u2500\u2500\u2500",
1513
+ "# Environment files",
1514
+ ".env",
1515
+ ".env.*",
1516
+ "!.env.example",
1517
+ "",
1518
+ "# Key material",
1519
+ "*.pem",
1520
+ "*.key",
1521
+ "*.crt",
1522
+ "*.p12",
1523
+ "*.pfx",
1524
+ "*.der",
1525
+ "id_rsa*",
1526
+ "id_dsa*",
1527
+ "*.ppk",
1528
+ "",
1529
+ "# Cloud and service configs",
1530
+ "aws-credentials.json",
1531
+ "gcp-service-account*.json",
1532
+ "azure-credentials.json",
1533
+ "secrets/**",
1534
+ "config/secrets/",
1535
+ "**/secrets/",
1536
+ "",
1537
+ "# Database credentials",
1538
+ "database.yml",
1539
+ "**/database/config.*",
1540
+ "",
1541
+ "# API keys and tokens",
1542
+ "**/apikeys/",
1543
+ "**/*_token*",
1544
+ "**/*_secret*",
1545
+ "**/*api_key*",
1546
+ "",
1547
+ "# \u2500\u2500\u2500\u2500\u2500 Infrastructure & Deployment \u2500\u2500\u2500\u2500\u2500",
1548
+ "# Terraform state",
1549
+ "*.tfstate",
1550
+ "*.tfstate.*",
1551
+ ".terraform/",
1552
+ "",
1553
+ "# Kubernetes secrets",
1554
+ "**/k8s/**/secret*.yaml",
1555
+ "**/kubernetes/**/secret*.yaml",
1556
+ "",
1557
+ "# Docker secrets",
1558
+ "docker-compose.override.yml",
1559
+ "**/docker/secrets/",
1560
+ "",
1561
+ "# \u2500\u2500\u2500\u2500\u2500 Logs & Runtime Data \u2500\u2500\u2500\u2500\u2500",
1562
+ "*.log",
1563
+ "*.tmp",
1564
+ "*.cache",
1565
+ "logs/",
1566
+ "/var/log/",
1567
+ "coverage/",
1568
+ ".nyc_output/",
1569
+ "",
1570
+ "# \u2500\u2500\u2500\u2500\u2500 Large Data Files \u2500\u2500\u2500\u2500\u2500",
1571
+ "*.csv",
1572
+ "*.xlsx",
1573
+ "*.sqlite",
1574
+ "*.db",
1575
+ "*.dump",
1576
+ "data/",
1577
+ "datasets/",
1578
+ "",
1579
+ "# \u2500\u2500\u2500\u2500\u2500 Node.js Specific \u2500\u2500\u2500\u2500\u2500",
1580
+ "node_modules/",
1581
+ ".pnpm-store/",
1582
+ ".yarn/",
1583
+ ".next/",
1584
+ ".nuxt/",
1585
+ ".cache/",
1586
+ ".parcel-cache/",
1587
+ "",
1588
+ "# \u2500\u2500\u2500\u2500\u2500 Python Specific \u2500\u2500\u2500\u2500\u2500",
1589
+ "__pycache__/",
1590
+ "*.pyc",
1591
+ "*.pyo",
1592
+ "*.pyd",
1593
+ ".Python",
1594
+ "venv/",
1595
+ ".venv/",
1596
+ "env/",
1597
+ ".env/",
1598
+ "",
1599
+ "# \u2500\u2500\u2500\u2500\u2500 Java Specific \u2500\u2500\u2500\u2500\u2500",
1600
+ "*.class",
1601
+ "*.jar",
1602
+ "*.war",
1603
+ "target/",
1604
+ ""
1605
+ ];
1606
+ const rulePatterns = extractIgnorePatternsFromRules(rules);
1607
+ if (rulePatterns.length > 0) {
1608
+ lines.push("# \u2500\u2500\u2500\u2500\u2500 Project-specific exclusions from rulesync rules \u2500\u2500\u2500\u2500\u2500");
1609
+ lines.push(...rulePatterns);
1610
+ lines.push("");
1611
+ }
1612
+ lines.push(
1613
+ "# \u2500\u2500\u2500\u2500\u2500 Allow specific source files (uncomment as needed) \u2500\u2500\u2500\u2500\u2500",
1614
+ "# !src/**/*.ts",
1615
+ "# !src/**/*.js",
1616
+ "# !lib/**/*.py",
1617
+ "# !src/main/**/*.java",
1618
+ ""
1619
+ );
1620
+ return lines.join("\n");
1621
+ }
745
1622
 
746
1623
  // src/generators/ignore/kiro.ts
747
- var import_node_path2 = require("path");
1624
+ var import_node_path5 = require("path");
748
1625
  async function generateKiroIgnoreFiles(rules, config, baseDir) {
749
1626
  const outputs = [];
750
- const aiignoreContent = generateAiignoreContent(rules);
1627
+ const aiignoreContent = generateAiignoreContent2(rules);
751
1628
  const outputPath = baseDir || process.cwd();
752
- const filepath = (0, import_node_path2.join)(outputPath, ".aiignore");
1629
+ const filepath = (0, import_node_path5.join)(outputPath, ".aiignore");
753
1630
  outputs.push({
754
1631
  tool: "kiro",
755
1632
  filepath,
@@ -757,7 +1634,7 @@ async function generateKiroIgnoreFiles(rules, config, baseDir) {
757
1634
  });
758
1635
  return outputs;
759
1636
  }
760
- function generateAiignoreContent(rules) {
1637
+ function generateAiignoreContent2(rules) {
761
1638
  const lines = [
762
1639
  "# Generated by rulesync - Kiro AI-specific exclusions",
763
1640
  "# This file excludes files that can be in Git but shouldn't be read by the AI",
@@ -786,162 +1663,33 @@ function generateAiignoreContent(rules) {
786
1663
  "# Reinforce critical exclusions from .gitignore",
787
1664
  "*.pem",
788
1665
  "*.key",
789
- ".env*",
790
- ""
791
- );
792
- const rulePatterns = extractIgnorePatternsFromRules2(rules);
793
- if (rulePatterns.length > 0) {
794
- lines.push("# Project-specific exclusions from rulesync rules");
795
- lines.push(...rulePatterns);
796
- lines.push("");
797
- }
798
- return lines.join("\n");
799
- }
800
- function extractIgnorePatternsFromRules2(rules) {
801
- const patterns = [];
802
- for (const rule of rules) {
803
- if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
804
- for (const glob of rule.frontmatter.globs) {
805
- if (shouldExcludeFromAI(glob)) {
806
- patterns.push(`# Exclude: ${rule.frontmatter.description}`);
807
- patterns.push(glob);
808
- }
809
- }
810
- }
811
- const contentPatterns = extractIgnorePatternsFromContent(rule.content);
812
- patterns.push(...contentPatterns);
813
- }
814
- return patterns;
815
- }
816
- function shouldExcludeFromAI(glob) {
817
- const excludePatterns = [
818
- // Test and fixture files that might be large or confusing
819
- "**/test/fixtures/**",
820
- "**/tests/fixtures/**",
821
- "**/*.fixture.*",
822
- // Build and generated files
823
- "**/dist/**",
824
- "**/build/**",
825
- "**/coverage/**",
826
- // Configuration that might contain sensitive data
827
- "**/config/production/**",
828
- "**/config/prod/**",
829
- "**/*.prod.*",
830
- // Documentation that might be sensitive
831
- "**/internal/**",
832
- "**/private/**",
833
- "**/confidential/**"
834
- ];
835
- return excludePatterns.some((pattern) => {
836
- const regex = new RegExp(pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*"));
837
- return regex.test(glob);
838
- });
839
- }
840
- function extractIgnorePatternsFromContent(content) {
841
- const patterns = [];
842
- const lines = content.split("\n");
843
- for (const line of lines) {
844
- const trimmed = line.trim();
845
- if (trimmed.startsWith("# IGNORE:") || trimmed.startsWith("# aiignore:")) {
846
- const pattern = trimmed.replace(/^# (IGNORE|aiignore):\s*/, "").trim();
847
- if (pattern) {
848
- patterns.push(pattern);
849
- }
850
- }
851
- if (trimmed.includes("exclude") || trimmed.includes("ignore")) {
852
- const matches = trimmed.match(/['"`]([^'"`]+\.(log|tmp|cache|temp))['"`]/g);
853
- if (matches) {
854
- patterns.push(...matches.map((m) => m.replace(/['"`]/g, "")));
855
- }
856
- }
1666
+ ".env*",
1667
+ ""
1668
+ );
1669
+ const rulePatterns = extractIgnorePatternsFromRules(rules);
1670
+ if (rulePatterns.length > 0) {
1671
+ lines.push("# Project-specific exclusions from rulesync rules");
1672
+ lines.push(...rulePatterns);
1673
+ lines.push("");
857
1674
  }
858
- return patterns;
1675
+ return lines.join("\n");
859
1676
  }
860
1677
 
861
1678
  // src/generators/rules/augmentcode.ts
862
- var import_node_path6 = require("path");
1679
+ var import_node_path8 = require("path");
863
1680
 
864
1681
  // src/generators/rules/shared-helpers.ts
865
- var import_node_path5 = require("path");
1682
+ var import_node_path7 = require("path");
866
1683
 
867
1684
  // src/utils/ignore.ts
868
- var import_node_path4 = require("path");
1685
+ var import_node_path6 = require("path");
869
1686
  var import_micromatch = __toESM(require("micromatch"), 1);
870
-
871
- // src/utils/file.ts
872
- var import_promises2 = require("fs/promises");
873
- var import_node_path3 = require("path");
874
- async function ensureDir(dirPath) {
875
- try {
876
- await (0, import_promises2.stat)(dirPath);
877
- } catch {
878
- await (0, import_promises2.mkdir)(dirPath, { recursive: true });
879
- }
880
- }
881
- async function readFileContent(filepath) {
882
- return (0, import_promises2.readFile)(filepath, "utf-8");
883
- }
884
- async function writeFileContent(filepath, content) {
885
- await ensureDir((0, import_node_path3.dirname)(filepath));
886
- await (0, import_promises2.writeFile)(filepath, content, "utf-8");
887
- }
888
- async function fileExists(filepath) {
889
- try {
890
- await (0, import_promises2.stat)(filepath);
891
- return true;
892
- } catch {
893
- return false;
894
- }
895
- }
896
- async function findFiles(dir, extension = ".md") {
897
- try {
898
- const files = await (0, import_promises2.readdir)(dir);
899
- return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path3.join)(dir, file));
900
- } catch {
901
- return [];
902
- }
903
- }
904
- async function removeDirectory(dirPath) {
905
- const dangerousPaths = [".", "/", "~", "src", "node_modules"];
906
- if (dangerousPaths.includes(dirPath) || dirPath === "") {
907
- console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
908
- return;
909
- }
910
- try {
911
- if (await fileExists(dirPath)) {
912
- await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
913
- }
914
- } catch (error) {
915
- console.warn(`Failed to remove directory ${dirPath}:`, error);
916
- }
917
- }
918
- async function removeFile(filepath) {
919
- try {
920
- if (await fileExists(filepath)) {
921
- await (0, import_promises2.rm)(filepath);
922
- }
923
- } catch (error) {
924
- console.warn(`Failed to remove file ${filepath}:`, error);
925
- }
926
- }
927
- async function removeClaudeGeneratedFiles() {
928
- const filesToRemove = ["CLAUDE.md", ".claude/memories"];
929
- for (const fileOrDir of filesToRemove) {
930
- if (fileOrDir.endsWith("/memories")) {
931
- await removeDirectory(fileOrDir);
932
- } else {
933
- await removeFile(fileOrDir);
934
- }
935
- }
936
- }
937
-
938
- // src/utils/ignore.ts
939
1687
  var cachedIgnorePatterns = null;
940
1688
  async function loadIgnorePatterns(baseDir = process.cwd()) {
941
1689
  if (cachedIgnorePatterns) {
942
1690
  return cachedIgnorePatterns;
943
1691
  }
944
- const ignorePath = (0, import_node_path4.join)(baseDir, ".rulesyncignore");
1692
+ const ignorePath = (0, import_node_path6.join)(baseDir, ".rulesyncignore");
945
1693
  if (!await fileExists(ignorePath)) {
946
1694
  cachedIgnorePatterns = { patterns: [] };
947
1695
  return cachedIgnorePatterns;
@@ -986,7 +1734,7 @@ function filterIgnoredFiles(files, ignorePatterns) {
986
1734
 
987
1735
  // src/generators/rules/shared-helpers.ts
988
1736
  function resolveOutputDir(config, tool, baseDir) {
989
- return baseDir ? (0, import_node_path5.join)(baseDir, config.outputPaths[tool]) : config.outputPaths[tool];
1737
+ return baseDir ? (0, import_node_path7.join)(baseDir, config.outputPaths[tool]) : config.outputPaths[tool];
990
1738
  }
991
1739
  function createOutputsArray() {
992
1740
  return [];
@@ -995,7 +1743,7 @@ function addOutput(outputs, tool, config, baseDir, relativePath, content) {
995
1743
  const outputDir = resolveOutputDir(config, tool, baseDir);
996
1744
  outputs.push({
997
1745
  tool,
998
- filepath: (0, import_node_path5.join)(outputDir, relativePath),
1746
+ filepath: (0, import_node_path7.join)(outputDir, relativePath),
999
1747
  content
1000
1748
  });
1001
1749
  }
@@ -1004,7 +1752,7 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
1004
1752
  for (const rule of rules) {
1005
1753
  const content = generatorConfig.generateContent(rule);
1006
1754
  const outputDir = resolveOutputDir(config, generatorConfig.tool, baseDir);
1007
- const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : (0, import_node_path5.join)(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
1755
+ const filepath = generatorConfig.pathResolver ? generatorConfig.pathResolver(rule, outputDir) : (0, import_node_path7.join)(outputDir, `${rule.filename}${generatorConfig.fileExtension}`);
1008
1756
  outputs.push({
1009
1757
  tool: generatorConfig.tool,
1010
1758
  filepath,
@@ -1013,13 +1761,57 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
1013
1761
  }
1014
1762
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1015
1763
  if (ignorePatterns.patterns.length > 0) {
1016
- const ignorePath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
1764
+ const ignorePath = baseDir ? (0, import_node_path7.join)(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
1765
+ const ignoreContent = generateIgnoreFile(ignorePatterns.patterns, generatorConfig.tool);
1766
+ outputs.push({
1767
+ tool: generatorConfig.tool,
1768
+ filepath: ignorePath,
1769
+ content: ignoreContent
1770
+ });
1771
+ }
1772
+ return outputs;
1773
+ }
1774
+ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1775
+ const outputs = [];
1776
+ const rootRules = rules.filter((r) => r.frontmatter.root === true);
1777
+ const detailRules = rules.filter((r) => r.frontmatter.root === false);
1778
+ const rootRule = rootRules[0];
1779
+ if (generatorConfig.generateDetailContent && generatorConfig.detailSubDir) {
1780
+ for (const rule of detailRules) {
1781
+ const content = generatorConfig.generateDetailContent(rule);
1782
+ const filepath = baseDir ? (0, import_node_path7.join)(baseDir, generatorConfig.detailSubDir, `${rule.filename}.md`) : (0, import_node_path7.join)(generatorConfig.detailSubDir, `${rule.filename}.md`);
1783
+ outputs.push({
1784
+ tool: generatorConfig.tool,
1785
+ filepath,
1786
+ content
1787
+ });
1788
+ }
1789
+ }
1790
+ if (generatorConfig.generateRootContent && generatorConfig.rootFilePath) {
1791
+ const rootContent = generatorConfig.generateRootContent(rootRule, detailRules, baseDir);
1792
+ const rootFilepath = baseDir ? (0, import_node_path7.join)(baseDir, generatorConfig.rootFilePath) : generatorConfig.rootFilePath;
1793
+ outputs.push({
1794
+ tool: generatorConfig.tool,
1795
+ filepath: rootFilepath,
1796
+ content: rootContent
1797
+ });
1798
+ }
1799
+ const ignorePatterns = await loadIgnorePatterns(baseDir);
1800
+ if (ignorePatterns.patterns.length > 0) {
1801
+ const ignorePath = baseDir ? (0, import_node_path7.join)(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
1017
1802
  const ignoreContent = generateIgnoreFile(ignorePatterns.patterns, generatorConfig.tool);
1018
1803
  outputs.push({
1019
1804
  tool: generatorConfig.tool,
1020
1805
  filepath: ignorePath,
1021
1806
  content: ignoreContent
1022
1807
  });
1808
+ if (generatorConfig.updateAdditionalConfig) {
1809
+ const additionalOutputs = await generatorConfig.updateAdditionalConfig(
1810
+ ignorePatterns.patterns,
1811
+ baseDir
1812
+ );
1813
+ outputs.push(...additionalOutputs);
1814
+ }
1023
1815
  }
1024
1816
  return outputs;
1025
1817
  }
@@ -1056,7 +1848,7 @@ async function generateAugmentcodeConfig(rules, config, baseDir) {
1056
1848
  "augmentcode",
1057
1849
  config,
1058
1850
  baseDir,
1059
- (0, import_node_path6.join)(".augment", "rules", `${rule.filename}.md`),
1851
+ (0, import_node_path8.join)(".augment", "rules", `${rule.filename}.md`),
1060
1852
  generateRuleFile(rule)
1061
1853
  );
1062
1854
  });
@@ -1109,42 +1901,29 @@ function generateLegacyGuidelinesFile(allRules) {
1109
1901
  }
1110
1902
 
1111
1903
  // src/generators/rules/claudecode.ts
1112
- var import_node_path7 = require("path");
1113
-
1114
- // src/types/claudecode.ts
1115
- var import_mini2 = require("zod/mini");
1116
- var ClaudeSettingsSchema = import_mini2.z.looseObject({
1117
- permissions: import_mini2.z._default(
1118
- import_mini2.z.looseObject({
1119
- deny: import_mini2.z._default(import_mini2.z.array(import_mini2.z.string()), [])
1120
- }),
1121
- { deny: [] }
1122
- )
1123
- });
1124
-
1125
- // src/generators/rules/claudecode.ts
1904
+ var import_node_path9 = require("path");
1126
1905
  async function generateClaudecodeConfig(rules, config, baseDir) {
1127
1906
  const outputs = [];
1128
1907
  const rootRules = rules.filter((r) => r.frontmatter.root === true);
1129
1908
  const detailRules = rules.filter((r) => r.frontmatter.root === false);
1130
1909
  const claudeMdContent = generateClaudeMarkdown(rootRules, detailRules);
1131
- const claudeOutputDir = baseDir ? (0, import_node_path7.join)(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
1910
+ const claudeOutputDir = baseDir ? (0, import_node_path9.join)(baseDir, config.outputPaths.claudecode) : config.outputPaths.claudecode;
1132
1911
  outputs.push({
1133
1912
  tool: "claudecode",
1134
- filepath: (0, import_node_path7.join)(claudeOutputDir, "CLAUDE.md"),
1913
+ filepath: (0, import_node_path9.join)(claudeOutputDir, "CLAUDE.md"),
1135
1914
  content: claudeMdContent
1136
1915
  });
1137
1916
  for (const rule of detailRules) {
1138
1917
  const memoryContent = generateMemoryFile(rule);
1139
1918
  outputs.push({
1140
1919
  tool: "claudecode",
1141
- filepath: (0, import_node_path7.join)(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
1920
+ filepath: (0, import_node_path9.join)(claudeOutputDir, ".claude", "memories", `${rule.filename}.md`),
1142
1921
  content: memoryContent
1143
1922
  });
1144
1923
  }
1145
1924
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1146
1925
  if (ignorePatterns.patterns.length > 0) {
1147
- const settingsPath = baseDir ? (0, import_node_path7.join)(baseDir, ".claude", "settings.json") : (0, import_node_path7.join)(".claude", "settings.json");
1926
+ const settingsPath = baseDir ? (0, import_node_path9.join)(baseDir, ".claude", "settings.json") : (0, import_node_path9.join)(".claude", "settings.json");
1148
1927
  await updateClaudeSettings(settingsPath, ignorePatterns.patterns);
1149
1928
  }
1150
1929
  return outputs;
@@ -1223,7 +2002,7 @@ async function generateClineConfig(rules, config, baseDir) {
1223
2002
  }
1224
2003
 
1225
2004
  // src/generators/rules/copilot.ts
1226
- var import_node_path8 = require("path");
2005
+ var import_node_path10 = require("path");
1227
2006
  async function generateCopilotConfig(rules, config, baseDir) {
1228
2007
  return generateComplexRulesConfig(
1229
2008
  rules,
@@ -1235,7 +2014,7 @@ async function generateCopilotConfig(rules, config, baseDir) {
1235
2014
  generateContent: generateCopilotMarkdown,
1236
2015
  getOutputPath: (rule, outputDir) => {
1237
2016
  const baseFilename = rule.filename.replace(/\.md$/, "");
1238
- return (0, import_node_path8.join)(outputDir, `${baseFilename}.instructions.md`);
2017
+ return (0, import_node_path10.join)(outputDir, `${baseFilename}.instructions.md`);
1239
2018
  }
1240
2019
  },
1241
2020
  baseDir
@@ -1256,7 +2035,7 @@ function generateCopilotMarkdown(rule) {
1256
2035
  }
1257
2036
 
1258
2037
  // src/generators/rules/cursor.ts
1259
- var import_node_path9 = require("path");
2038
+ var import_node_path11 = require("path");
1260
2039
  async function generateCursorConfig(rules, config, baseDir) {
1261
2040
  return generateComplexRulesConfig(
1262
2041
  rules,
@@ -1267,7 +2046,7 @@ async function generateCursorConfig(rules, config, baseDir) {
1267
2046
  ignoreFileName: ".cursorignore",
1268
2047
  generateContent: generateCursorMarkdown,
1269
2048
  getOutputPath: (rule, outputDir) => {
1270
- return (0, import_node_path9.join)(outputDir, `${rule.filename}.mdc`);
2049
+ return (0, import_node_path11.join)(outputDir, `${rule.filename}.mdc`);
1271
2050
  }
1272
2051
  },
1273
2052
  baseDir
@@ -1327,39 +2106,18 @@ function determineCursorRuleType(frontmatter) {
1327
2106
  }
1328
2107
 
1329
2108
  // src/generators/rules/geminicli.ts
1330
- var import_node_path10 = require("path");
1331
2109
  async function generateGeminiConfig(rules, config, baseDir) {
1332
- const outputs = [];
1333
- const rootRule = rules.find((rule) => rule.frontmatter.root === true);
1334
- const memoryRules = rules.filter((rule) => rule.frontmatter.root === false);
1335
- for (const rule of memoryRules) {
1336
- const content = generateGeminiMemoryMarkdown(rule);
1337
- const outputDir = baseDir ? (0, import_node_path10.join)(baseDir, config.outputPaths.geminicli) : config.outputPaths.geminicli;
1338
- const filepath = (0, import_node_path10.join)(outputDir, `${rule.filename}.md`);
1339
- outputs.push({
1340
- tool: "geminicli",
1341
- filepath,
1342
- content
1343
- });
1344
- }
1345
- const rootContent = generateGeminiRootMarkdown(rootRule, memoryRules, baseDir);
1346
- const rootFilepath = baseDir ? (0, import_node_path10.join)(baseDir, "GEMINI.md") : "GEMINI.md";
1347
- outputs.push({
2110
+ const generatorConfig = {
1348
2111
  tool: "geminicli",
1349
- filepath: rootFilepath,
1350
- content: rootContent
1351
- });
1352
- const ignorePatterns = await loadIgnorePatterns(baseDir);
1353
- if (ignorePatterns.patterns.length > 0) {
1354
- const aiexcludePath = baseDir ? (0, import_node_path10.join)(baseDir, ".aiexclude") : ".aiexclude";
1355
- const aiexcludeContent = generateAiexclude(ignorePatterns.patterns);
1356
- outputs.push({
1357
- tool: "geminicli",
1358
- filepath: aiexcludePath,
1359
- content: aiexcludeContent
1360
- });
1361
- }
1362
- return outputs;
2112
+ fileExtension: ".md",
2113
+ ignoreFileName: ".aiexclude",
2114
+ generateContent: generateGeminiMemoryMarkdown,
2115
+ generateDetailContent: generateGeminiMemoryMarkdown,
2116
+ generateRootContent: generateGeminiRootMarkdown,
2117
+ rootFilePath: "GEMINI.md",
2118
+ detailSubDir: ".gemini/memories"
2119
+ };
2120
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
1363
2121
  }
1364
2122
  function generateGeminiMemoryMarkdown(rule) {
1365
2123
  return rule.content.trim();
@@ -1388,24 +2146,42 @@ function generateGeminiRootMarkdown(rootRule, memoryRules, _baseDir) {
1388
2146
  }
1389
2147
  return lines.join("\n");
1390
2148
  }
1391
- function generateAiexclude(patterns) {
1392
- const lines = [
1393
- "# Generated by rulesync from .rulesyncignore",
1394
- "# This file is automatically generated. Do not edit manually.",
1395
- "",
1396
- ...patterns
1397
- ];
1398
- return lines.join("\n");
2149
+
2150
+ // src/generators/rules/junie.ts
2151
+ async function generateJunieConfig(rules, config, baseDir) {
2152
+ const generatorConfig = {
2153
+ tool: "junie",
2154
+ fileExtension: ".md",
2155
+ ignoreFileName: ".aiignore",
2156
+ generateContent: (rule) => rule.content.trim(),
2157
+ generateRootContent: generateGuidelinesMarkdown,
2158
+ rootFilePath: ".junie/guidelines.md"
2159
+ };
2160
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
2161
+ }
2162
+ function generateGuidelinesMarkdown(rootRule, detailRules) {
2163
+ const lines = [];
2164
+ if (rootRule) {
2165
+ lines.push(rootRule.content);
2166
+ lines.push("");
2167
+ }
2168
+ if (detailRules.length > 0) {
2169
+ for (const rule of detailRules) {
2170
+ lines.push(rule.content);
2171
+ lines.push("");
2172
+ }
2173
+ }
2174
+ return lines.join("\n").trim();
1399
2175
  }
1400
2176
 
1401
2177
  // src/generators/rules/kiro.ts
1402
- var import_node_path11 = require("path");
2178
+ var import_node_path12 = require("path");
1403
2179
  async function generateKiroConfig(rules, config, baseDir) {
1404
2180
  const outputs = [];
1405
2181
  for (const rule of rules) {
1406
2182
  const content = generateKiroMarkdown(rule);
1407
- const outputDir = baseDir ? (0, import_node_path11.join)(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
1408
- const filepath = (0, import_node_path11.join)(outputDir, `${rule.filename}.md`);
2183
+ const outputDir = baseDir ? (0, import_node_path12.join)(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
2184
+ const filepath = (0, import_node_path12.join)(outputDir, `${rule.filename}.md`);
1409
2185
  outputs.push({
1410
2186
  tool: "kiro",
1411
2187
  filepath,
@@ -1459,7 +2235,10 @@ async function generateConfigurations(rules, config, targetTools, baseDir) {
1459
2235
  function filterRulesForTool(rules, tool, config) {
1460
2236
  return rules.filter((rule) => {
1461
2237
  const targets = resolveTargets(rule.frontmatter.targets, config);
1462
- return targets.includes(tool);
2238
+ if (!targets.includes(tool)) {
2239
+ return false;
2240
+ }
2241
+ return isToolSpecificRule(rule, tool);
1463
2242
  });
1464
2243
  }
1465
2244
  async function generateForTool(tool, rules, config, baseDir) {
@@ -1490,6 +2269,11 @@ async function generateForTool(tool, rules, config, baseDir) {
1490
2269
  return generateRooConfig(rules, config, baseDir);
1491
2270
  case "geminicli":
1492
2271
  return generateGeminiConfig(rules, config, baseDir);
2272
+ case "junie": {
2273
+ const junieRulesOutputs = await generateJunieConfig(rules, config, baseDir);
2274
+ const junieIgnoreOutputs = await generateJunieIgnoreFiles(rules, config, baseDir);
2275
+ return [...junieRulesOutputs, ...junieIgnoreOutputs];
2276
+ }
1493
2277
  case "kiro": {
1494
2278
  const kiroRulesOutputs = await generateKiroConfig(rules, config, baseDir);
1495
2279
  const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
@@ -1502,83 +2286,8 @@ async function generateForTool(tool, rules, config, baseDir) {
1502
2286
  }
1503
2287
 
1504
2288
  // src/core/parser.ts
1505
- var import_node_path12 = require("path");
2289
+ var import_node_path13 = require("path");
1506
2290
  var import_gray_matter = __toESM(require("gray-matter"), 1);
1507
-
1508
- // src/types/config.ts
1509
- var import_mini3 = require("zod/mini");
1510
- init_tool_targets();
1511
- var ConfigSchema = import_mini3.z.object({
1512
- aiRulesDir: import_mini3.z.string(),
1513
- outputPaths: import_mini3.z.record(ToolTargetSchema, import_mini3.z.string()),
1514
- watchEnabled: import_mini3.z.boolean(),
1515
- defaultTargets: ToolTargetsSchema
1516
- });
1517
-
1518
- // src/types/mcp.ts
1519
- var import_mini4 = require("zod/mini");
1520
- init_tool_targets();
1521
- var McpTransportTypeSchema = import_mini4.z.enum(["stdio", "sse", "http"]);
1522
- var McpServerBaseSchema = import_mini4.z.object({
1523
- command: import_mini4.z.optional(import_mini4.z.string()),
1524
- args: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string())),
1525
- url: import_mini4.z.optional(import_mini4.z.string()),
1526
- httpUrl: import_mini4.z.optional(import_mini4.z.string()),
1527
- env: import_mini4.z.optional(import_mini4.z.record(import_mini4.z.string(), import_mini4.z.string())),
1528
- disabled: import_mini4.z.optional(import_mini4.z.boolean()),
1529
- networkTimeout: import_mini4.z.optional(import_mini4.z.number()),
1530
- timeout: import_mini4.z.optional(import_mini4.z.number()),
1531
- trust: import_mini4.z.optional(import_mini4.z.boolean()),
1532
- cwd: import_mini4.z.optional(import_mini4.z.string()),
1533
- transport: import_mini4.z.optional(McpTransportTypeSchema),
1534
- type: import_mini4.z.optional(import_mini4.z.enum(["sse", "streamable-http"])),
1535
- alwaysAllow: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string())),
1536
- tools: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string())),
1537
- kiroAutoApprove: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string())),
1538
- kiroAutoBlock: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string()))
1539
- });
1540
- var RulesyncMcpServerSchema = import_mini4.z.extend(McpServerBaseSchema, {
1541
- targets: import_mini4.z.optional(RulesyncTargetsSchema)
1542
- });
1543
- var McpConfigSchema = import_mini4.z.object({
1544
- mcpServers: import_mini4.z.record(import_mini4.z.string(), McpServerBaseSchema)
1545
- });
1546
- var RulesyncMcpConfigSchema = import_mini4.z.object({
1547
- mcpServers: import_mini4.z.record(import_mini4.z.string(), RulesyncMcpServerSchema)
1548
- });
1549
-
1550
- // src/types/rules.ts
1551
- var import_mini5 = require("zod/mini");
1552
- init_tool_targets();
1553
- var RuleFrontmatterSchema = import_mini5.z.object({
1554
- root: import_mini5.z.boolean(),
1555
- targets: RulesyncTargetsSchema,
1556
- description: import_mini5.z.string(),
1557
- globs: import_mini5.z.array(import_mini5.z.string()),
1558
- cursorRuleType: import_mini5.z.optional(import_mini5.z.enum(["always", "manual", "specificFiles", "intelligently"])),
1559
- tags: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string()))
1560
- });
1561
- var ParsedRuleSchema = import_mini5.z.object({
1562
- frontmatter: RuleFrontmatterSchema,
1563
- content: import_mini5.z.string(),
1564
- filename: import_mini5.z.string(),
1565
- filepath: import_mini5.z.string()
1566
- });
1567
- var GeneratedOutputSchema = import_mini5.z.object({
1568
- tool: ToolTargetSchema,
1569
- filepath: import_mini5.z.string(),
1570
- content: import_mini5.z.string()
1571
- });
1572
- var GenerateOptionsSchema = import_mini5.z.object({
1573
- targetTools: import_mini5.z.optional(ToolTargetsSchema),
1574
- outputDir: import_mini5.z.optional(import_mini5.z.string()),
1575
- watch: import_mini5.z.optional(import_mini5.z.boolean())
1576
- });
1577
-
1578
- // src/types/index.ts
1579
- init_tool_targets();
1580
-
1581
- // src/core/parser.ts
1582
2291
  async function parseRulesFromDirectory(aiRulesDir) {
1583
2292
  const ignorePatterns = await loadIgnorePatterns();
1584
2293
  const allRuleFiles = await findFiles(aiRulesDir, ".md");
@@ -1615,7 +2324,7 @@ async function parseRuleFile(filepath) {
1615
2324
  const parsed = (0, import_gray_matter.default)(content);
1616
2325
  try {
1617
2326
  const frontmatter = RuleFrontmatterSchema.parse(parsed.data);
1618
- const filename = (0, import_node_path12.basename)(filepath, ".md");
2327
+ const filename = (0, import_node_path13.basename)(filepath, ".md");
1619
2328
  return {
1620
2329
  frontmatter,
1621
2330
  content: parsed.content,
@@ -1680,7 +2389,7 @@ async function validateRule(rule) {
1680
2389
  }
1681
2390
 
1682
2391
  // src/core/mcp-generator.ts
1683
- var path3 = __toESM(require("path"), 1);
2392
+ var path4 = __toESM(require("path"), 1);
1684
2393
 
1685
2394
  // src/generators/mcp/index.ts
1686
2395
  init_augmentcode();
@@ -1689,14 +2398,15 @@ init_cline();
1689
2398
  init_copilot();
1690
2399
  init_cursor();
1691
2400
  init_geminicli();
2401
+ init_junie();
1692
2402
  init_kiro();
1693
2403
  init_roo();
1694
2404
 
1695
2405
  // src/core/mcp-parser.ts
1696
2406
  var fs = __toESM(require("fs"), 1);
1697
- var path2 = __toESM(require("path"), 1);
2407
+ var path3 = __toESM(require("path"), 1);
1698
2408
  function parseMcpConfig(projectRoot) {
1699
- const mcpPath = path2.join(projectRoot, ".rulesync", ".mcp.json");
2409
+ const mcpPath = path3.join(projectRoot, ".rulesync", ".mcp.json");
1700
2410
  if (!fs.existsSync(mcpPath)) {
1701
2411
  return null;
1702
2412
  }
@@ -1720,7 +2430,7 @@ function parseMcpConfig(projectRoot) {
1720
2430
  }
1721
2431
 
1722
2432
  // src/core/mcp-generator.ts
1723
- async function generateMcpConfigs(projectRoot, baseDir) {
2433
+ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
1724
2434
  const results = [];
1725
2435
  const targetRoot = baseDir || projectRoot;
1726
2436
  const config = parseMcpConfig(projectRoot);
@@ -1730,55 +2440,70 @@ async function generateMcpConfigs(projectRoot, baseDir) {
1730
2440
  const generators = [
1731
2441
  {
1732
2442
  tool: "augmentcode-project",
1733
- path: path3.join(targetRoot, ".mcp.json"),
2443
+ path: path4.join(targetRoot, ".mcp.json"),
1734
2444
  generate: () => generateAugmentcodeMcp(config)
1735
2445
  },
1736
2446
  {
1737
2447
  tool: "augmentcode-legacy-project",
1738
- path: path3.join(targetRoot, ".mcp.json"),
2448
+ path: path4.join(targetRoot, ".mcp.json"),
1739
2449
  generate: () => generateAugmentcodeMcp(config)
1740
2450
  },
1741
2451
  {
1742
2452
  tool: "claude-project",
1743
- path: path3.join(targetRoot, ".mcp.json"),
2453
+ path: path4.join(targetRoot, ".mcp.json"),
1744
2454
  generate: () => generateClaudeMcp(config)
1745
2455
  },
1746
2456
  {
1747
2457
  tool: "copilot-editor",
1748
- path: path3.join(targetRoot, ".vscode", "mcp.json"),
2458
+ path: path4.join(targetRoot, ".vscode", "mcp.json"),
1749
2459
  generate: () => generateCopilotMcp(config, "editor")
1750
2460
  },
1751
2461
  {
1752
2462
  tool: "cursor-project",
1753
- path: path3.join(targetRoot, ".cursor", "mcp.json"),
2463
+ path: path4.join(targetRoot, ".cursor", "mcp.json"),
1754
2464
  generate: () => generateCursorMcp(config)
1755
2465
  },
1756
2466
  {
1757
2467
  tool: "cline-project",
1758
- path: path3.join(targetRoot, ".cline", "mcp.json"),
2468
+ path: path4.join(targetRoot, ".cline", "mcp.json"),
1759
2469
  generate: () => generateClineMcp(config)
1760
2470
  },
1761
2471
  {
1762
2472
  tool: "gemini-project",
1763
- path: path3.join(targetRoot, ".gemini", "settings.json"),
2473
+ path: path4.join(targetRoot, ".gemini", "settings.json"),
1764
2474
  generate: () => generateGeminiCliMcp(config)
1765
2475
  },
2476
+ {
2477
+ tool: "junie-project",
2478
+ path: path4.join(targetRoot, ".junie", "mcp-config.json"),
2479
+ generate: () => generateJunieMcp(config)
2480
+ },
1766
2481
  {
1767
2482
  tool: "kiro-project",
1768
- path: path3.join(targetRoot, ".kiro", "mcp.json"),
2483
+ path: path4.join(targetRoot, ".kiro", "mcp.json"),
1769
2484
  generate: () => generateKiroMcp(config)
1770
2485
  },
1771
2486
  {
1772
2487
  tool: "roo-project",
1773
- path: path3.join(targetRoot, ".roo", "mcp.json"),
2488
+ path: path4.join(targetRoot, ".roo", "mcp.json"),
1774
2489
  generate: () => generateRooMcp(config)
1775
2490
  }
1776
2491
  ];
1777
- for (const generator of generators) {
2492
+ const filteredGenerators = targetTools ? generators.filter((g) => {
2493
+ const baseTool = g.tool.split("-")[0];
2494
+ if (!isToolTarget(baseTool)) {
2495
+ return false;
2496
+ }
2497
+ if (baseTool === "augmentcode") {
2498
+ return targetTools.includes("augmentcode") || targetTools.includes("augmentcode-legacy");
2499
+ }
2500
+ return targetTools.includes(baseTool);
2501
+ }) : generators;
2502
+ for (const generator of filteredGenerators) {
1778
2503
  try {
1779
2504
  const content = generator.generate();
1780
2505
  const parsed = JSON.parse(content);
1781
- if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
2506
+ if (generator.tool.includes("augmentcode") || generator.tool.includes("claude") || generator.tool.includes("cline") || generator.tool.includes("cursor") || generator.tool.includes("gemini") || generator.tool.includes("junie") || generator.tool.includes("kiro") || generator.tool.includes("roo")) {
1782
2507
  if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
1783
2508
  results.push({
1784
2509
  tool: generator.tool,
@@ -1818,15 +2543,58 @@ async function generateMcpConfigs(projectRoot, baseDir) {
1818
2543
 
1819
2544
  // src/cli/commands/generate.ts
1820
2545
  async function generateCommand(options = {}) {
1821
- const config = getDefaultConfig();
1822
- const baseDirs = options.baseDirs || [process.cwd()];
2546
+ const configLoaderOptions = {
2547
+ ...options.config !== void 0 && { configPath: options.config },
2548
+ ...options.noConfig !== void 0 && { noConfig: options.noConfig }
2549
+ };
2550
+ const configResult = await loadConfig(configLoaderOptions);
2551
+ const cliOptions = {
2552
+ ...options.tools !== void 0 && { tools: options.tools },
2553
+ ...options.verbose !== void 0 && { verbose: options.verbose },
2554
+ ...options.delete !== void 0 && { delete: options.delete },
2555
+ ...options.baseDirs !== void 0 && { baseDirs: options.baseDirs }
2556
+ };
2557
+ const config = mergeWithCliOptions(configResult.config, cliOptions);
2558
+ if (options.tools && options.tools.length > 0) {
2559
+ const configTargets = config.defaultTargets;
2560
+ const cliTools = options.tools;
2561
+ const cliToolsSet = new Set(cliTools);
2562
+ const configTargetsSet = new Set(configTargets);
2563
+ const notInConfig = cliTools.filter((tool) => !configTargetsSet.has(tool));
2564
+ const notInCli = configTargets.filter((tool) => !cliToolsSet.has(tool));
2565
+ if (notInConfig.length > 0 || notInCli.length > 0) {
2566
+ console.warn("\u26A0\uFE0F Warning: CLI tool selection differs from configuration!");
2567
+ console.warn(` Config targets: ${configTargets.join(", ")}`);
2568
+ console.warn(` CLI specified: ${cliTools.join(", ")}`);
2569
+ if (notInConfig.length > 0) {
2570
+ console.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
2571
+ }
2572
+ if (notInCli.length > 0) {
2573
+ console.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
2574
+ }
2575
+ console.warn("\n The configuration file targets will be used.");
2576
+ console.warn(" To change targets, update your rulesync config file.");
2577
+ console.warn("");
2578
+ }
2579
+ }
2580
+ let baseDirs;
2581
+ if (config.baseDir) {
2582
+ baseDirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
2583
+ } else if (options.baseDirs) {
2584
+ baseDirs = options.baseDirs;
2585
+ } else {
2586
+ baseDirs = [process.cwd()];
2587
+ }
2588
+ if (config.verbose && configResult.filepath) {
2589
+ console.log(`Loaded configuration from: ${configResult.filepath}`);
2590
+ }
1823
2591
  console.log("Generating configuration files...");
1824
2592
  if (!await fileExists(config.aiRulesDir)) {
1825
2593
  console.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
1826
2594
  process.exit(1);
1827
2595
  }
1828
2596
  try {
1829
- if (options.verbose) {
2597
+ if (config.verbose) {
1830
2598
  console.log(`Parsing rules from ${config.aiRulesDir}...`);
1831
2599
  }
1832
2600
  const rules = await parseRulesFromDirectory(config.aiRulesDir);
@@ -1834,18 +2602,26 @@ async function generateCommand(options = {}) {
1834
2602
  console.warn("\u26A0\uFE0F No rules found in .rulesync directory");
1835
2603
  return;
1836
2604
  }
1837
- if (options.verbose) {
2605
+ if (config.verbose) {
1838
2606
  console.log(`Found ${rules.length} rule(s)`);
1839
2607
  console.log(`Base directories: ${baseDirs.join(", ")}`);
1840
2608
  }
1841
- if (options.delete) {
1842
- if (options.verbose) {
2609
+ if (config.delete) {
2610
+ if (config.verbose) {
1843
2611
  console.log("Deleting existing output directories...");
1844
2612
  }
1845
- const targetTools = options.tools || config.defaultTargets;
2613
+ const targetTools = config.defaultTargets;
1846
2614
  const deleteTasks = [];
1847
2615
  for (const tool of targetTools) {
1848
2616
  switch (tool) {
2617
+ case "augmentcode":
2618
+ deleteTasks.push(removeDirectory((0, import_node_path14.join)(".augment", "rules")));
2619
+ deleteTasks.push(removeDirectory((0, import_node_path14.join)(".augment", "ignore")));
2620
+ break;
2621
+ case "augmentcode-legacy":
2622
+ deleteTasks.push(removeClaudeGeneratedFiles());
2623
+ deleteTasks.push(removeDirectory((0, import_node_path14.join)(".augment", "ignore")));
2624
+ break;
1849
2625
  case "copilot":
1850
2626
  deleteTasks.push(removeDirectory(config.outputPaths.copilot));
1851
2627
  break;
@@ -1870,19 +2646,19 @@ async function generateCommand(options = {}) {
1870
2646
  }
1871
2647
  }
1872
2648
  await Promise.all(deleteTasks);
1873
- if (options.verbose) {
2649
+ if (config.verbose) {
1874
2650
  console.log("Deleted existing output directories");
1875
2651
  }
1876
2652
  }
1877
2653
  let totalOutputs = 0;
1878
2654
  for (const baseDir of baseDirs) {
1879
- if (options.verbose) {
2655
+ if (config.verbose) {
1880
2656
  console.log(`
1881
2657
  Generating configurations for base directory: ${baseDir}`);
1882
2658
  }
1883
- const outputs = await generateConfigurations(rules, config, options.tools, baseDir);
2659
+ const outputs = await generateConfigurations(rules, config, config.defaultTargets, baseDir);
1884
2660
  if (outputs.length === 0) {
1885
- if (options.verbose) {
2661
+ if (config.verbose) {
1886
2662
  console.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
1887
2663
  }
1888
2664
  continue;
@@ -1897,17 +2673,18 @@ Generating configurations for base directory: ${baseDir}`);
1897
2673
  console.warn("\u26A0\uFE0F No configurations generated");
1898
2674
  return;
1899
2675
  }
1900
- if (options.verbose) {
2676
+ if (config.verbose) {
1901
2677
  console.log("\nGenerating MCP configurations...");
1902
2678
  }
1903
2679
  let totalMcpOutputs = 0;
1904
2680
  for (const baseDir of baseDirs) {
1905
2681
  const mcpResults = await generateMcpConfigs(
1906
2682
  process.cwd(),
1907
- baseDir === process.cwd() ? void 0 : baseDir
2683
+ baseDir === process.cwd() ? void 0 : baseDir,
2684
+ config.defaultTargets
1908
2685
  );
1909
2686
  if (mcpResults.length === 0) {
1910
- if (options.verbose) {
2687
+ if (config.verbose) {
1911
2688
  console.log(`No MCP configuration found for ${baseDir}`);
1912
2689
  }
1913
2690
  continue;
@@ -1918,7 +2695,7 @@ Generating configurations for base directory: ${baseDir}`);
1918
2695
  totalMcpOutputs++;
1919
2696
  } else if (result.status === "error") {
1920
2697
  console.error(`\u274C Failed to generate ${result.tool} MCP configuration: ${result.error}`);
1921
- } else if (options.verbose && result.status === "skipped") {
2698
+ } else if (config.verbose && result.status === "skipped") {
1922
2699
  console.log(`\u23ED\uFE0F Skipped ${result.tool} MCP configuration (no servers configured)`);
1923
2700
  }
1924
2701
  }
@@ -1937,10 +2714,10 @@ Generating configurations for base directory: ${baseDir}`);
1937
2714
  }
1938
2715
 
1939
2716
  // src/cli/commands/gitignore.ts
1940
- var import_node_fs = require("fs");
1941
- var import_node_path13 = require("path");
2717
+ var import_node_fs2 = require("fs");
2718
+ var import_node_path15 = require("path");
1942
2719
  var gitignoreCommand = async () => {
1943
- const gitignorePath = (0, import_node_path13.join)(process.cwd(), ".gitignore");
2720
+ const gitignorePath = (0, import_node_path15.join)(process.cwd(), ".gitignore");
1944
2721
  const rulesFilesToIgnore = [
1945
2722
  "# Generated by rulesync - AI tool configuration files",
1946
2723
  "**/.github/copilot-instructions.md",
@@ -1962,6 +2739,8 @@ var gitignoreCommand = async () => {
1962
2739
  "**/.kiro/steering/",
1963
2740
  "**/.augment/rules/",
1964
2741
  "**/.augment-guidelines",
2742
+ "**/.junie/guidelines.md",
2743
+ "**/.noai",
1965
2744
  "**/.mcp.json",
1966
2745
  "!.rulesync/.mcp.json",
1967
2746
  "**/.cursor/mcp.json",
@@ -1971,8 +2750,8 @@ var gitignoreCommand = async () => {
1971
2750
  "**/.roo/mcp.json"
1972
2751
  ];
1973
2752
  let gitignoreContent = "";
1974
- if ((0, import_node_fs.existsSync)(gitignorePath)) {
1975
- gitignoreContent = (0, import_node_fs.readFileSync)(gitignorePath, "utf-8");
2753
+ if ((0, import_node_fs2.existsSync)(gitignorePath)) {
2754
+ gitignoreContent = (0, import_node_fs2.readFileSync)(gitignorePath, "utf-8");
1976
2755
  }
1977
2756
  const linesToAdd = [];
1978
2757
  for (const rule of rulesFilesToIgnore) {
@@ -1989,7 +2768,7 @@ var gitignoreCommand = async () => {
1989
2768
  ${linesToAdd.join("\n")}
1990
2769
  ` : `${linesToAdd.join("\n")}
1991
2770
  `;
1992
- (0, import_node_fs.writeFileSync)(gitignorePath, newContent);
2771
+ (0, import_node_fs2.writeFileSync)(gitignorePath, newContent);
1993
2772
  console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
1994
2773
  for (const line of linesToAdd) {
1995
2774
  if (!line.startsWith("#")) {
@@ -1999,11 +2778,11 @@ ${linesToAdd.join("\n")}
1999
2778
  };
2000
2779
 
2001
2780
  // src/core/importer.ts
2002
- var import_node_path18 = require("path");
2781
+ var import_node_path21 = require("path");
2003
2782
  var import_gray_matter5 = __toESM(require("gray-matter"), 1);
2004
2783
 
2005
2784
  // src/parsers/augmentcode.ts
2006
- var import_node_path14 = require("path");
2785
+ var import_node_path16 = require("path");
2007
2786
  var import_gray_matter2 = __toESM(require("gray-matter"), 1);
2008
2787
 
2009
2788
  // src/utils/parser-helpers.ts
@@ -2044,7 +2823,7 @@ async function safeReadFile(operation, errorContext) {
2044
2823
  // src/parsers/augmentcode.ts
2045
2824
  async function parseAugmentcodeConfiguration(baseDir = process.cwd()) {
2046
2825
  const result = createParseResult();
2047
- const rulesDir = (0, import_node_path14.join)(baseDir, ".augment", "rules");
2826
+ const rulesDir = (0, import_node_path16.join)(baseDir, ".augment", "rules");
2048
2827
  if (await fileExists(rulesDir)) {
2049
2828
  const rulesResult = await parseAugmentRules(rulesDir);
2050
2829
  addRules(result, rulesResult.rules);
@@ -2062,7 +2841,7 @@ async function parseAugmentRules(rulesDir) {
2062
2841
  const files = await readdir2(rulesDir);
2063
2842
  for (const file of files) {
2064
2843
  if (file.endsWith(".md") || file.endsWith(".mdc")) {
2065
- const filePath = (0, import_node_path14.join)(rulesDir, file);
2844
+ const filePath = (0, import_node_path16.join)(rulesDir, file);
2066
2845
  try {
2067
2846
  const rawContent = await readFileContent(filePath);
2068
2847
  const parsed = (0, import_gray_matter2.default)(rawContent);
@@ -2071,7 +2850,7 @@ async function parseAugmentRules(rulesDir) {
2071
2850
  const description = frontmatterData.description || "";
2072
2851
  const tags = Array.isArray(frontmatterData.tags) ? frontmatterData.tags : void 0;
2073
2852
  const isRoot = ruleType === "always";
2074
- const filename = (0, import_node_path14.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
2853
+ const filename = (0, import_node_path16.basename)(file, file.endsWith(".mdc") ? ".mdc" : ".md");
2075
2854
  const frontmatter = {
2076
2855
  root: isRoot,
2077
2856
  targets: ["augmentcode"],
@@ -2100,10 +2879,10 @@ async function parseAugmentRules(rulesDir) {
2100
2879
  }
2101
2880
 
2102
2881
  // src/parsers/augmentcode-legacy.ts
2103
- var import_node_path15 = require("path");
2882
+ var import_node_path17 = require("path");
2104
2883
  async function parseAugmentcodeLegacyConfiguration(baseDir = process.cwd()) {
2105
2884
  const result = createParseResult();
2106
- const guidelinesPath = (0, import_node_path15.join)(baseDir, ".augment-guidelines");
2885
+ const guidelinesPath = (0, import_node_path17.join)(baseDir, ".augment-guidelines");
2107
2886
  if (await fileExists(guidelinesPath)) {
2108
2887
  const guidelinesResult = await parseAugmentGuidelines(guidelinesPath);
2109
2888
  if (guidelinesResult.rule) {
@@ -2146,13 +2925,13 @@ async function parseAugmentGuidelines(guidelinesPath) {
2146
2925
  }
2147
2926
 
2148
2927
  // src/parsers/shared-helpers.ts
2149
- var import_node_path16 = require("path");
2928
+ var import_node_path18 = require("path");
2150
2929
  var import_gray_matter3 = __toESM(require("gray-matter"), 1);
2151
2930
  async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2152
2931
  const errors = [];
2153
2932
  const rules = [];
2154
2933
  if (config.mainFile) {
2155
- const mainFilePath = (0, import_node_path16.join)(baseDir, config.mainFile.path);
2934
+ const mainFilePath = (0, import_node_path18.join)(baseDir, config.mainFile.path);
2156
2935
  if (await fileExists(mainFilePath)) {
2157
2936
  try {
2158
2937
  const rawContent = await readFileContent(mainFilePath);
@@ -2192,14 +2971,14 @@ async function parseConfigurationFiles(baseDir = process.cwd(), config) {
2192
2971
  }
2193
2972
  if (config.directories) {
2194
2973
  for (const dirConfig of config.directories) {
2195
- const dirPath = (0, import_node_path16.join)(baseDir, dirConfig.directory);
2974
+ const dirPath = (0, import_node_path18.join)(baseDir, dirConfig.directory);
2196
2975
  if (await fileExists(dirPath)) {
2197
2976
  try {
2198
2977
  const { readdir: readdir2 } = await import("fs/promises");
2199
2978
  const files = await readdir2(dirPath);
2200
2979
  for (const file of files) {
2201
2980
  if (file.endsWith(dirConfig.filePattern)) {
2202
- const filePath = (0, import_node_path16.join)(dirPath, file);
2981
+ const filePath = (0, import_node_path18.join)(dirPath, file);
2203
2982
  try {
2204
2983
  const rawContent = await readFileContent(filePath);
2205
2984
  let content;
@@ -2247,7 +3026,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
2247
3026
  const rules = [];
2248
3027
  let ignorePatterns;
2249
3028
  let mcpServers;
2250
- const mainFilePath = (0, import_node_path16.join)(baseDir, config.mainFileName);
3029
+ const mainFilePath = (0, import_node_path18.join)(baseDir, config.mainFileName);
2251
3030
  if (!await fileExists(mainFilePath)) {
2252
3031
  errors.push(`${config.mainFileName} file not found`);
2253
3032
  return { rules, errors };
@@ -2258,12 +3037,12 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
2258
3037
  if (mainRule) {
2259
3038
  rules.push(mainRule);
2260
3039
  }
2261
- const memoryDir = (0, import_node_path16.join)(baseDir, config.memoryDirPath);
3040
+ const memoryDir = (0, import_node_path18.join)(baseDir, config.memoryDirPath);
2262
3041
  if (await fileExists(memoryDir)) {
2263
3042
  const memoryRules = await parseMemoryFiles(memoryDir, config);
2264
3043
  rules.push(...memoryRules);
2265
3044
  }
2266
- const settingsPath = (0, import_node_path16.join)(baseDir, config.settingsPath);
3045
+ const settingsPath = (0, import_node_path18.join)(baseDir, config.settingsPath);
2267
3046
  if (await fileExists(settingsPath)) {
2268
3047
  const settingsResult = await parseSettingsFile(settingsPath, config.tool);
2269
3048
  if (settingsResult.ignorePatterns) {
@@ -2275,7 +3054,7 @@ async function parseMemoryBasedConfiguration(baseDir = process.cwd(), config) {
2275
3054
  errors.push(...settingsResult.errors);
2276
3055
  }
2277
3056
  if (config.additionalIgnoreFile) {
2278
- const additionalIgnorePath = (0, import_node_path16.join)(baseDir, config.additionalIgnoreFile.path);
3057
+ const additionalIgnorePath = (0, import_node_path18.join)(baseDir, config.additionalIgnoreFile.path);
2279
3058
  if (await fileExists(additionalIgnorePath)) {
2280
3059
  const additionalPatterns = await config.additionalIgnoreFile.parser(additionalIgnorePath);
2281
3060
  if (additionalPatterns.length > 0) {
@@ -2329,10 +3108,10 @@ async function parseMemoryFiles(memoryDir, config) {
2329
3108
  const files = await readdir2(memoryDir);
2330
3109
  for (const file of files) {
2331
3110
  if (file.endsWith(".md")) {
2332
- const filePath = (0, import_node_path16.join)(memoryDir, file);
3111
+ const filePath = (0, import_node_path18.join)(memoryDir, file);
2333
3112
  const content = await readFileContent(filePath);
2334
3113
  if (content.trim()) {
2335
- const filename = (0, import_node_path16.basename)(file, ".md");
3114
+ const filename = (0, import_node_path18.basename)(file, ".md");
2336
3115
  const frontmatter = {
2337
3116
  root: false,
2338
3117
  targets: [config.tool],
@@ -2445,10 +3224,10 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
2445
3224
  }
2446
3225
 
2447
3226
  // src/parsers/cursor.ts
2448
- var import_node_path17 = require("path");
3227
+ var import_node_path19 = require("path");
2449
3228
  var import_gray_matter4 = __toESM(require("gray-matter"), 1);
2450
3229
  var import_js_yaml = require("js-yaml");
2451
- var import_mini6 = require("zod/mini");
3230
+ var import_mini7 = require("zod/mini");
2452
3231
  var customMatterOptions = {
2453
3232
  engines: {
2454
3233
  yaml: {
@@ -2476,7 +3255,7 @@ var customMatterOptions = {
2476
3255
  }
2477
3256
  };
2478
3257
  function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
2479
- const FrontmatterSchema = import_mini6.z.record(import_mini6.z.string(), import_mini6.z.unknown());
3258
+ const FrontmatterSchema = import_mini7.z.record(import_mini7.z.string(), import_mini7.z.unknown());
2480
3259
  const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
2481
3260
  if (!parseResult.success) {
2482
3261
  return {
@@ -2570,7 +3349,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
2570
3349
  const rules = [];
2571
3350
  let ignorePatterns;
2572
3351
  let mcpServers;
2573
- const cursorFilePath = (0, import_node_path17.join)(baseDir, ".cursorrules");
3352
+ const cursorFilePath = (0, import_node_path19.join)(baseDir, ".cursorrules");
2574
3353
  if (await fileExists(cursorFilePath)) {
2575
3354
  try {
2576
3355
  const rawContent = await readFileContent(cursorFilePath);
@@ -2591,20 +3370,20 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
2591
3370
  errors.push(`Failed to parse .cursorrules file: ${errorMessage}`);
2592
3371
  }
2593
3372
  }
2594
- const cursorRulesDir = (0, import_node_path17.join)(baseDir, ".cursor", "rules");
3373
+ const cursorRulesDir = (0, import_node_path19.join)(baseDir, ".cursor", "rules");
2595
3374
  if (await fileExists(cursorRulesDir)) {
2596
3375
  try {
2597
3376
  const { readdir: readdir2 } = await import("fs/promises");
2598
3377
  const files = await readdir2(cursorRulesDir);
2599
3378
  for (const file of files) {
2600
3379
  if (file.endsWith(".mdc")) {
2601
- const filePath = (0, import_node_path17.join)(cursorRulesDir, file);
3380
+ const filePath = (0, import_node_path19.join)(cursorRulesDir, file);
2602
3381
  try {
2603
3382
  const rawContent = await readFileContent(filePath);
2604
3383
  const parsed = (0, import_gray_matter4.default)(rawContent, customMatterOptions);
2605
3384
  const content = parsed.content.trim();
2606
3385
  if (content) {
2607
- const filename = (0, import_node_path17.basename)(file, ".mdc");
3386
+ const filename = (0, import_node_path19.basename)(file, ".mdc");
2608
3387
  const frontmatter = convertCursorMdcFrontmatter(parsed.data, filename);
2609
3388
  rules.push({
2610
3389
  frontmatter,
@@ -2627,7 +3406,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
2627
3406
  if (rules.length === 0) {
2628
3407
  errors.push("No Cursor configuration files found (.cursorrules or .cursor/rules/*.mdc)");
2629
3408
  }
2630
- const cursorIgnorePath = (0, import_node_path17.join)(baseDir, ".cursorignore");
3409
+ const cursorIgnorePath = (0, import_node_path19.join)(baseDir, ".cursorignore");
2631
3410
  if (await fileExists(cursorIgnorePath)) {
2632
3411
  try {
2633
3412
  const content = await readFileContent(cursorIgnorePath);
@@ -2640,7 +3419,7 @@ async function parseCursorConfiguration(baseDir = process.cwd()) {
2640
3419
  errors.push(`Failed to parse .cursorignore: ${errorMessage}`);
2641
3420
  }
2642
3421
  }
2643
- const cursorMcpPath = (0, import_node_path17.join)(baseDir, ".cursor", "mcp.json");
3422
+ const cursorMcpPath = (0, import_node_path19.join)(baseDir, ".cursor", "mcp.json");
2644
3423
  if (await fileExists(cursorMcpPath)) {
2645
3424
  try {
2646
3425
  const content = await readFileContent(cursorMcpPath);
@@ -2688,6 +3467,42 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
2688
3467
  });
2689
3468
  }
2690
3469
 
3470
+ // src/parsers/junie.ts
3471
+ var import_node_path20 = require("path");
3472
+ async function parseJunieConfiguration(baseDir = process.cwd()) {
3473
+ const errors = [];
3474
+ const rules = [];
3475
+ const guidelinesPath = (0, import_node_path20.join)(baseDir, ".junie", "guidelines.md");
3476
+ if (!await fileExists(guidelinesPath)) {
3477
+ errors.push(".junie/guidelines.md file not found");
3478
+ return { rules, errors };
3479
+ }
3480
+ try {
3481
+ const content = await readFileContent(guidelinesPath);
3482
+ if (content.trim()) {
3483
+ const frontmatter = {
3484
+ root: false,
3485
+ targets: ["junie"],
3486
+ description: "Junie project guidelines",
3487
+ globs: ["**/*"]
3488
+ };
3489
+ rules.push({
3490
+ frontmatter,
3491
+ content: content.trim(),
3492
+ filename: "junie-guidelines",
3493
+ filepath: guidelinesPath
3494
+ });
3495
+ }
3496
+ } catch (error) {
3497
+ const errorMessage = error instanceof Error ? error.message : String(error);
3498
+ errors.push(`Failed to parse .junie/guidelines.md: ${errorMessage}`);
3499
+ }
3500
+ if (rules.length === 0) {
3501
+ errors.push("No valid Junie configuration found");
3502
+ }
3503
+ return { rules, errors };
3504
+ }
3505
+
2691
3506
  // src/parsers/roo.ts
2692
3507
  async function parseRooConfiguration(baseDir = process.cwd()) {
2693
3508
  return parseConfigurationFiles(baseDir, {
@@ -2774,6 +3589,12 @@ async function importConfiguration(options) {
2774
3589
  mcpServers = geminiResult.mcpServers;
2775
3590
  break;
2776
3591
  }
3592
+ case "junie": {
3593
+ const junieResult = await parseJunieConfiguration(baseDir);
3594
+ rules = junieResult.rules;
3595
+ errors.push(...junieResult.errors);
3596
+ break;
3597
+ }
2777
3598
  default:
2778
3599
  errors.push(`Unsupported tool: ${tool}`);
2779
3600
  return { success: false, rulesCreated: 0, errors };
@@ -2786,7 +3607,7 @@ async function importConfiguration(options) {
2786
3607
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
2787
3608
  return { success: false, rulesCreated: 0, errors };
2788
3609
  }
2789
- const rulesDirPath = (0, import_node_path18.join)(baseDir, rulesDir);
3610
+ const rulesDirPath = (0, import_node_path21.join)(baseDir, rulesDir);
2790
3611
  try {
2791
3612
  const { mkdir: mkdir3 } = await import("fs/promises");
2792
3613
  await mkdir3(rulesDirPath, { recursive: true });
@@ -2800,7 +3621,7 @@ async function importConfiguration(options) {
2800
3621
  try {
2801
3622
  const baseFilename = `${tool}__${rule.filename}`;
2802
3623
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
2803
- const filePath = (0, import_node_path18.join)(rulesDirPath, `${filename}.md`);
3624
+ const filePath = (0, import_node_path21.join)(rulesDirPath, `${filename}.md`);
2804
3625
  const content = generateRuleFileContent(rule);
2805
3626
  await writeFileContent(filePath, content);
2806
3627
  rulesCreated++;
@@ -2815,7 +3636,7 @@ async function importConfiguration(options) {
2815
3636
  let ignoreFileCreated = false;
2816
3637
  if (ignorePatterns && ignorePatterns.length > 0) {
2817
3638
  try {
2818
- const rulesyncignorePath = (0, import_node_path18.join)(baseDir, ".rulesyncignore");
3639
+ const rulesyncignorePath = (0, import_node_path21.join)(baseDir, ".rulesyncignore");
2819
3640
  const ignoreContent = `${ignorePatterns.join("\n")}
2820
3641
  `;
2821
3642
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -2831,7 +3652,7 @@ async function importConfiguration(options) {
2831
3652
  let mcpFileCreated = false;
2832
3653
  if (mcpServers && Object.keys(mcpServers).length > 0) {
2833
3654
  try {
2834
- const mcpPath = (0, import_node_path18.join)(baseDir, rulesDir, ".mcp.json");
3655
+ const mcpPath = (0, import_node_path21.join)(baseDir, rulesDir, ".mcp.json");
2835
3656
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
2836
3657
  `;
2837
3658
  await writeFileContent(mcpPath, mcpContent);
@@ -2859,7 +3680,7 @@ function generateRuleFileContent(rule) {
2859
3680
  async function generateUniqueFilename(rulesDir, baseFilename) {
2860
3681
  let filename = baseFilename;
2861
3682
  let counter = 1;
2862
- while (await fileExists((0, import_node_path18.join)(rulesDir, `${filename}.md`))) {
3683
+ while (await fileExists((0, import_node_path21.join)(rulesDir, `${filename}.md`))) {
2863
3684
  filename = `${baseFilename}-${counter}`;
2864
3685
  counter++;
2865
3686
  }
@@ -2870,7 +3691,7 @@ async function generateUniqueFilename(rulesDir, baseFilename) {
2870
3691
  async function importCommand(options = {}) {
2871
3692
  const tools = [];
2872
3693
  if (options.augmentcode) tools.push("augmentcode");
2873
- if (options.augmentcodeLegacy) tools.push("augmentcode-legacy");
3694
+ if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
2874
3695
  if (options.claudecode) tools.push("claudecode");
2875
3696
  if (options.cursor) tools.push("cursor");
2876
3697
  if (options.copilot) tools.push("copilot");
@@ -2879,7 +3700,7 @@ async function importCommand(options = {}) {
2879
3700
  if (options.geminicli) tools.push("geminicli");
2880
3701
  if (tools.length === 0) {
2881
3702
  console.error(
2882
- "\u274C Please specify one tool to import from (--augmentcode, --augmentcodeLegacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
3703
+ "\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
2883
3704
  );
2884
3705
  process.exit(1);
2885
3706
  }
@@ -2926,7 +3747,7 @@ async function importCommand(options = {}) {
2926
3747
  }
2927
3748
 
2928
3749
  // src/cli/commands/init.ts
2929
- var import_node_path19 = require("path");
3750
+ var import_node_path22 = require("path");
2930
3751
  async function initCommand() {
2931
3752
  const aiRulesDir = ".rulesync";
2932
3753
  console.log("Initializing rulesync...");
@@ -2973,7 +3794,7 @@ globs: ["**/*"]
2973
3794
  - Follow single responsibility principle
2974
3795
  `
2975
3796
  };
2976
- const filepath = (0, import_node_path19.join)(aiRulesDir, sampleFile.filename);
3797
+ const filepath = (0, import_node_path22.join)(aiRulesDir, sampleFile.filename);
2977
3798
  if (!await fileExists(filepath)) {
2978
3799
  await writeFileContent(filepath, sampleFile.content);
2979
3800
  console.log(`Created ${filepath}`);
@@ -3087,11 +3908,11 @@ async function watchCommand() {
3087
3908
  persistent: true
3088
3909
  });
3089
3910
  let isGenerating = false;
3090
- const handleChange = async (path4) => {
3911
+ const handleChange = async (path5) => {
3091
3912
  if (isGenerating) return;
3092
3913
  isGenerating = true;
3093
3914
  console.log(`
3094
- \u{1F4DD} Detected change in ${path4}`);
3915
+ \u{1F4DD} Detected change in ${path5}`);
3095
3916
  try {
3096
3917
  await generateCommand({ verbose: false });
3097
3918
  console.log("\u2705 Regenerated configuration files");
@@ -3101,10 +3922,10 @@ async function watchCommand() {
3101
3922
  isGenerating = false;
3102
3923
  }
3103
3924
  };
3104
- watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path4) => {
3925
+ watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path5) => {
3105
3926
  console.log(`
3106
- \u{1F5D1}\uFE0F Removed ${path4}`);
3107
- handleChange(path4);
3927
+ \u{1F5D1}\uFE0F Removed ${path5}`);
3928
+ handleChange(path5);
3108
3929
  }).on("error", (error) => {
3109
3930
  console.error("\u274C Watcher error:", error);
3110
3931
  });
@@ -3117,28 +3938,31 @@ async function watchCommand() {
3117
3938
 
3118
3939
  // src/cli/index.ts
3119
3940
  var program = new import_commander.Command();
3120
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.49.0");
3941
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.51.0");
3121
3942
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
3122
3943
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
3123
3944
  program.command("gitignore").description("Add generated files to .gitignore").action(gitignoreCommand);
3124
- program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcodeLegacy", "Import from AugmentCode legacy format (.augment-guidelines)").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);
3125
- program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcodeLegacy", "Generate only for AugmentCode legacy format").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("--kiro", "Generate only for Kiro IDE").option("--delete", "Delete all existing files in output directories before generating").option(
3945
+ program.command("import").description("Import configurations from AI tools to rulesync format").option("--augmentcode", "Import from AugmentCode (.augment/rules/)").option("--augmentcode-legacy", "Import from AugmentCode legacy format (.augment-guidelines)").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("--junie", "Import from JetBrains Junie (.junie/guidelines.md)").option("-v, --verbose", "Verbose output").action(importCommand);
3946
+ program.command("generate").description("Generate configuration files for AI tools").option("--augmentcode", "Generate only for AugmentCode").option("--augmentcode-legacy", "Generate only for AugmentCode legacy format").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("--junie", "Generate only for JetBrains Junie").option("--kiro", "Generate only for Kiro IDE").option("--delete", "Delete all existing files in output directories before generating").option(
3126
3947
  "-b, --base-dir <paths>",
3127
3948
  "Base directories to generate files (comma-separated for multiple paths)"
3128
- ).option("-v, --verbose", "Verbose output").action(async (options) => {
3949
+ ).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
3129
3950
  const tools = [];
3130
3951
  if (options.augmentcode) tools.push("augmentcode");
3131
- if (options.augmentcodeLegacy) tools.push("augmentcode-legacy");
3952
+ if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
3132
3953
  if (options.copilot) tools.push("copilot");
3133
3954
  if (options.cursor) tools.push("cursor");
3134
3955
  if (options.cline) tools.push("cline");
3135
3956
  if (options.claudecode) tools.push("claudecode");
3136
3957
  if (options.roo) tools.push("roo");
3137
3958
  if (options.geminicli) tools.push("geminicli");
3959
+ if (options.junie) tools.push("junie");
3138
3960
  if (options.kiro) tools.push("kiro");
3139
3961
  const generateOptions = {
3140
3962
  verbose: options.verbose,
3141
- delete: options.delete
3963
+ delete: options.delete,
3964
+ config: options.config,
3965
+ noConfig: options.noConfig
3142
3966
  };
3143
3967
  if (tools.length > 0) {
3144
3968
  generateOptions.tools = tools;
@@ -3151,4 +3975,5 @@ program.command("generate").description("Generate configuration files for AI too
3151
3975
  program.command("validate").description("Validate rulesync configuration").action(validateCommand);
3152
3976
  program.command("status").description("Show current status of rulesync").action(statusCommand);
3153
3977
  program.command("watch").description("Watch for changes and auto-generate configurations").action(watchCommand);
3978
+ program.command("config").description("Show or initialize rulesync configuration").option("--init", "Initialize a new configuration file").option("--format <format>", "Configuration file format (jsonc, ts)", "jsonc").option("--targets <tools>", "Comma-separated list of tools to generate for").option("--exclude <tools>", "Comma-separated list of tools to exclude").option("--ai-rules-dir <dir>", "Directory containing AI rule files").option("--base-dir <path>", "Base directory for generation").option("--verbose", "Enable verbose output").option("--delete", "Delete existing files before generating").action(configCommand);
3154
3979
  program.parse();