rulesync 0.49.0 → 0.52.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]);
@@ -130,84 +136,108 @@ var init_augmentcode = __esm({
130
136
  }
131
137
  });
132
138
 
139
+ // src/generators/mcp/shared-factory.ts
140
+ function generateMcpConfig(config, toolConfig) {
141
+ const servers = {};
142
+ for (const [serverName, server] of Object.entries(config.mcpServers)) {
143
+ if (!shouldIncludeServer(server, toolConfig.target)) continue;
144
+ servers[serverName] = toolConfig.serverTransform(server, serverName);
145
+ }
146
+ const finalConfig = toolConfig.configWrapper(servers);
147
+ return JSON.stringify(finalConfig, null, 2);
148
+ }
149
+ var configWrappers;
150
+ var init_shared_factory = __esm({
151
+ "src/generators/mcp/shared-factory.ts"() {
152
+ "use strict";
153
+ init_mcp_helpers();
154
+ configWrappers = {
155
+ /**
156
+ * Standard mcpServers wrapper
157
+ */
158
+ mcpServers: (servers) => ({
159
+ mcpServers: servers
160
+ }),
161
+ /**
162
+ * Servers-only wrapper (for tools that use "servers" instead of "mcpServers")
163
+ */
164
+ servers: (servers) => ({
165
+ servers
166
+ })
167
+ };
168
+ }
169
+ });
170
+
133
171
  // src/generators/mcp/claudecode.ts
134
172
  function generateClaudeMcp(config) {
135
- const claudeSettings = {
136
- mcpServers: {}
137
- };
138
- const shouldInclude = (server) => {
139
- return shouldIncludeServer(server, "claudecode");
140
- };
141
- for (const [serverName, server] of Object.entries(config.mcpServers)) {
142
- if (!shouldInclude(server)) continue;
143
- const claudeServer = {};
144
- if (server.command) {
145
- claudeServer.command = server.command;
146
- if (server.args) claudeServer.args = server.args;
147
- } else if (server.url || server.httpUrl) {
148
- const url = server.httpUrl || server.url;
149
- if (url) {
150
- claudeServer.url = url;
173
+ return generateMcpConfig(config, {
174
+ target: "claudecode",
175
+ configPaths: [".claude/settings.json"],
176
+ serverTransform: (server) => {
177
+ const claudeServer = {};
178
+ if (server.command) {
179
+ claudeServer.command = server.command;
180
+ if (server.args) claudeServer.args = server.args;
181
+ } else if (server.url || server.httpUrl) {
182
+ const url = server.httpUrl || server.url;
183
+ if (url) {
184
+ claudeServer.url = url;
185
+ }
186
+ if (server.httpUrl) {
187
+ claudeServer.transport = "http";
188
+ } else if (server.transport === "sse") {
189
+ claudeServer.transport = "sse";
190
+ }
151
191
  }
152
- if (server.httpUrl) {
153
- claudeServer.transport = "http";
154
- } else if (server.transport === "sse") {
155
- claudeServer.transport = "sse";
192
+ if (server.env) {
193
+ claudeServer.env = server.env;
156
194
  }
157
- }
158
- if (server.env) {
159
- claudeServer.env = server.env;
160
- }
161
- if (claudeSettings.mcpServers) {
162
- claudeSettings.mcpServers[serverName] = claudeServer;
163
- }
164
- }
165
- return JSON.stringify(claudeSettings, null, 2);
195
+ return claudeServer;
196
+ },
197
+ configWrapper: configWrappers.mcpServers
198
+ });
166
199
  }
167
200
  var init_claudecode = __esm({
168
201
  "src/generators/mcp/claudecode.ts"() {
169
202
  "use strict";
170
- init_mcp_helpers();
203
+ init_shared_factory();
171
204
  }
172
205
  });
173
206
 
174
207
  // src/generators/mcp/cline.ts
175
208
  function generateClineMcp(config) {
176
- const clineConfig = {
177
- mcpServers: {}
178
- };
179
- const shouldInclude = (server) => {
180
- return shouldIncludeServer(server, "cline");
181
- };
182
- for (const [serverName, server] of Object.entries(config.mcpServers)) {
183
- if (!shouldInclude(server)) continue;
184
- const clineServer = {};
185
- if (server.command) {
186
- clineServer.command = server.command;
187
- if (server.args) clineServer.args = server.args;
188
- } else if (server.url) {
189
- clineServer.url = server.url;
190
- }
191
- if (server.env) {
192
- clineServer.env = server.env;
193
- }
194
- if (server.disabled !== void 0) {
195
- clineServer.disabled = server.disabled;
196
- }
197
- if (server.alwaysAllow) {
198
- clineServer.alwaysAllow = server.alwaysAllow;
199
- }
200
- if (server.networkTimeout !== void 0) {
201
- clineServer.networkTimeout = server.networkTimeout;
202
- }
203
- clineConfig.mcpServers[serverName] = clineServer;
204
- }
205
- return JSON.stringify(clineConfig, null, 2);
209
+ return generateMcpConfig(config, {
210
+ target: "cline",
211
+ configPaths: [".cline/mcp.json"],
212
+ serverTransform: (server) => {
213
+ const clineServer = {};
214
+ if (server.command) {
215
+ clineServer.command = server.command;
216
+ if (server.args) clineServer.args = server.args;
217
+ } else if (server.url) {
218
+ clineServer.url = server.url;
219
+ }
220
+ if (server.env) {
221
+ clineServer.env = server.env;
222
+ }
223
+ if (server.disabled !== void 0) {
224
+ clineServer.disabled = server.disabled;
225
+ }
226
+ if (server.alwaysAllow) {
227
+ clineServer.alwaysAllow = server.alwaysAllow;
228
+ }
229
+ if (server.networkTimeout !== void 0) {
230
+ clineServer.networkTimeout = server.networkTimeout;
231
+ }
232
+ return clineServer;
233
+ },
234
+ configWrapper: configWrappers.mcpServers
235
+ });
206
236
  }
207
237
  var init_cline = __esm({
208
238
  "src/generators/mcp/cline.ts"() {
209
239
  "use strict";
210
- init_mcp_helpers();
240
+ init_shared_factory();
211
241
  }
212
242
  });
213
243
 
@@ -354,6 +384,58 @@ var init_geminicli = __esm({
354
384
  }
355
385
  });
356
386
 
387
+ // src/generators/mcp/junie.ts
388
+ function generateJunieMcp(config) {
389
+ const junieConfig = {
390
+ mcpServers: {}
391
+ };
392
+ for (const [serverName, server] of Object.entries(config.mcpServers)) {
393
+ if (!shouldIncludeServer(server, "junie")) continue;
394
+ const junieServer = {
395
+ name: serverName
396
+ };
397
+ if (server.command) {
398
+ junieServer.command = server.command;
399
+ if (server.args) junieServer.args = server.args;
400
+ } else if (server.url || server.httpUrl) {
401
+ if (server.httpUrl) {
402
+ junieServer.httpUrl = server.httpUrl;
403
+ } else if (server.url) {
404
+ junieServer.url = server.url;
405
+ }
406
+ }
407
+ if (server.env) {
408
+ junieServer.env = server.env;
409
+ }
410
+ if (server.cwd) {
411
+ junieServer.workingDirectory = server.cwd;
412
+ }
413
+ if (server.timeout !== void 0) {
414
+ junieServer.timeout = server.timeout;
415
+ }
416
+ if (server.trust !== void 0) {
417
+ junieServer.trust = server.trust;
418
+ }
419
+ if (server.transport) {
420
+ if (String(server.transport) === "streamable-http") {
421
+ junieServer.transport = "http";
422
+ } else if (server.transport === "stdio" || server.transport === "http" || server.transport === "sse") {
423
+ junieServer.transport = server.transport;
424
+ }
425
+ } else if (server.command) {
426
+ junieServer.transport = "stdio";
427
+ }
428
+ junieConfig.mcpServers[serverName] = junieServer;
429
+ }
430
+ return JSON.stringify(junieConfig, null, 2);
431
+ }
432
+ var init_junie = __esm({
433
+ "src/generators/mcp/junie.ts"() {
434
+ "use strict";
435
+ init_mcp_helpers();
436
+ }
437
+ });
438
+
357
439
  // src/generators/mcp/kiro.ts
358
440
  function generateKiroMcp(config) {
359
441
  const kiroConfig = {
@@ -478,19 +560,11 @@ function getDefaultConfig() {
478
560
  claudecode: ".",
479
561
  roo: ".roo/rules",
480
562
  geminicli: ".gemini/memories",
481
- kiro: ".kiro/steering"
563
+ kiro: ".kiro/steering",
564
+ junie: "."
482
565
  },
483
566
  watchEnabled: false,
484
- defaultTargets: [
485
- "augmentcode",
486
- "copilot",
487
- "cursor",
488
- "cline",
489
- "claudecode",
490
- "roo",
491
- "geminicli",
492
- "kiro"
493
- ]
567
+ defaultTargets: ALL_TOOL_TARGETS.filter((tool) => tool !== "augmentcode-legacy")
494
568
  };
495
569
  }
496
570
  function resolveTargets(targets, config) {
@@ -537,267 +611,643 @@ async function addCommand(filename) {
537
611
  }
538
612
  }
539
613
 
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("");
614
+ // src/cli/commands/config.ts
615
+ var import_node_fs = require("fs");
616
+ var import_node_path2 = __toESM(require("path"), 1);
617
+
618
+ // src/types/claudecode.ts
619
+ var import_mini2 = require("zod/mini");
620
+ var ClaudeSettingsSchema = import_mini2.z.looseObject({
621
+ permissions: import_mini2.z._default(
622
+ import_mini2.z.looseObject({
623
+ deny: import_mini2.z._default(import_mini2.z.array(import_mini2.z.string()), [])
624
+ }),
625
+ { deny: [] }
626
+ )
627
+ });
628
+
629
+ // src/types/config.ts
630
+ var import_mini3 = require("zod/mini");
631
+ init_tool_targets();
632
+ var ConfigSchema = import_mini3.z.object({
633
+ aiRulesDir: import_mini3.z.string(),
634
+ outputPaths: import_mini3.z.record(ToolTargetSchema, import_mini3.z.string()),
635
+ watchEnabled: import_mini3.z.boolean(),
636
+ defaultTargets: ToolTargetsSchema
637
+ });
638
+
639
+ // src/types/config-options.ts
640
+ var import_mini4 = require("zod/mini");
641
+ init_tool_targets();
642
+ var OutputPathsSchema = import_mini4.z.object({
643
+ augmentcode: import_mini4.z.optional(import_mini4.z.string()),
644
+ "augmentcode-legacy": import_mini4.z.optional(import_mini4.z.string()),
645
+ copilot: import_mini4.z.optional(import_mini4.z.string()),
646
+ cursor: import_mini4.z.optional(import_mini4.z.string()),
647
+ cline: import_mini4.z.optional(import_mini4.z.string()),
648
+ claudecode: import_mini4.z.optional(import_mini4.z.string()),
649
+ roo: import_mini4.z.optional(import_mini4.z.string()),
650
+ geminicli: import_mini4.z.optional(import_mini4.z.string()),
651
+ kiro: import_mini4.z.optional(import_mini4.z.string()),
652
+ junie: import_mini4.z.optional(import_mini4.z.string())
653
+ });
654
+ var ConfigOptionsSchema = import_mini4.z.object({
655
+ aiRulesDir: import_mini4.z.optional(import_mini4.z.string()),
656
+ outputPaths: import_mini4.z.optional(OutputPathsSchema),
657
+ watchEnabled: import_mini4.z.optional(import_mini4.z.boolean()),
658
+ defaultTargets: import_mini4.z.optional(ToolTargetsSchema),
659
+ targets: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
660
+ exclude: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
661
+ verbose: import_mini4.z.optional(import_mini4.z.boolean()),
662
+ delete: import_mini4.z.optional(import_mini4.z.boolean()),
663
+ baseDir: import_mini4.z.optional(import_mini4.z.union([import_mini4.z.string(), import_mini4.z.array(import_mini4.z.string())])),
664
+ watch: import_mini4.z.optional(
665
+ import_mini4.z.object({
666
+ enabled: import_mini4.z.optional(import_mini4.z.boolean()),
667
+ interval: import_mini4.z.optional(import_mini4.z.number()),
668
+ ignore: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string()))
669
+ })
670
+ )
671
+ });
672
+ var MergedConfigSchema = import_mini4.z.object({
673
+ aiRulesDir: import_mini4.z.string(),
674
+ outputPaths: import_mini4.z.record(ToolTargetSchema, import_mini4.z.string()),
675
+ watchEnabled: import_mini4.z.boolean(),
676
+ defaultTargets: ToolTargetsSchema,
677
+ targets: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
678
+ exclude: import_mini4.z.optional(import_mini4.z.array(ToolTargetSchema)),
679
+ verbose: import_mini4.z.optional(import_mini4.z.boolean()),
680
+ delete: import_mini4.z.optional(import_mini4.z.boolean()),
681
+ baseDir: import_mini4.z.optional(import_mini4.z.union([import_mini4.z.string(), import_mini4.z.array(import_mini4.z.string())])),
682
+ configPath: import_mini4.z.optional(import_mini4.z.string()),
683
+ watch: import_mini4.z.optional(
684
+ import_mini4.z.object({
685
+ enabled: import_mini4.z.optional(import_mini4.z.boolean()),
686
+ interval: import_mini4.z.optional(import_mini4.z.number()),
687
+ ignore: import_mini4.z.optional(import_mini4.z.array(import_mini4.z.string()))
688
+ })
689
+ )
690
+ });
691
+
692
+ // src/types/mcp.ts
693
+ var import_mini5 = require("zod/mini");
694
+ init_tool_targets();
695
+ var McpTransportTypeSchema = import_mini5.z.enum(["stdio", "sse", "http"]);
696
+ var McpServerBaseSchema = import_mini5.z.object({
697
+ command: import_mini5.z.optional(import_mini5.z.string()),
698
+ args: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
699
+ url: import_mini5.z.optional(import_mini5.z.string()),
700
+ httpUrl: import_mini5.z.optional(import_mini5.z.string()),
701
+ env: import_mini5.z.optional(import_mini5.z.record(import_mini5.z.string(), import_mini5.z.string())),
702
+ disabled: import_mini5.z.optional(import_mini5.z.boolean()),
703
+ networkTimeout: import_mini5.z.optional(import_mini5.z.number()),
704
+ timeout: import_mini5.z.optional(import_mini5.z.number()),
705
+ trust: import_mini5.z.optional(import_mini5.z.boolean()),
706
+ cwd: import_mini5.z.optional(import_mini5.z.string()),
707
+ transport: import_mini5.z.optional(McpTransportTypeSchema),
708
+ type: import_mini5.z.optional(import_mini5.z.enum(["sse", "streamable-http"])),
709
+ alwaysAllow: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
710
+ tools: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
711
+ kiroAutoApprove: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string())),
712
+ kiroAutoBlock: import_mini5.z.optional(import_mini5.z.array(import_mini5.z.string()))
713
+ });
714
+ var RulesyncMcpServerSchema = import_mini5.z.extend(McpServerBaseSchema, {
715
+ targets: import_mini5.z.optional(RulesyncTargetsSchema)
716
+ });
717
+ var McpConfigSchema = import_mini5.z.object({
718
+ mcpServers: import_mini5.z.record(import_mini5.z.string(), McpServerBaseSchema)
719
+ });
720
+ var RulesyncMcpConfigSchema = import_mini5.z.object({
721
+ mcpServers: import_mini5.z.record(import_mini5.z.string(), RulesyncMcpServerSchema)
722
+ });
723
+
724
+ // src/types/rules.ts
725
+ var import_mini6 = require("zod/mini");
726
+ init_tool_targets();
727
+ var RuleFrontmatterSchema = import_mini6.z.object({
728
+ root: import_mini6.z.boolean(),
729
+ targets: RulesyncTargetsSchema,
730
+ description: import_mini6.z.string(),
731
+ globs: import_mini6.z.array(import_mini6.z.string()),
732
+ cursorRuleType: import_mini6.z.optional(import_mini6.z.enum(["always", "manual", "specificFiles", "intelligently"])),
733
+ tags: import_mini6.z.optional(import_mini6.z.array(import_mini6.z.string()))
734
+ });
735
+ var ParsedRuleSchema = import_mini6.z.object({
736
+ frontmatter: RuleFrontmatterSchema,
737
+ content: import_mini6.z.string(),
738
+ filename: import_mini6.z.string(),
739
+ filepath: import_mini6.z.string()
740
+ });
741
+ var GeneratedOutputSchema = import_mini6.z.object({
742
+ tool: ToolTargetSchema,
743
+ filepath: import_mini6.z.string(),
744
+ content: import_mini6.z.string()
745
+ });
746
+ var GenerateOptionsSchema = import_mini6.z.object({
747
+ targetTools: import_mini6.z.optional(ToolTargetsSchema),
748
+ outputDir: import_mini6.z.optional(import_mini6.z.string()),
749
+ watch: import_mini6.z.optional(import_mini6.z.boolean())
750
+ });
751
+
752
+ // src/types/index.ts
753
+ init_tool_targets();
754
+
755
+ // src/utils/config-loader.ts
756
+ var import_c12 = require("c12");
757
+ var import_core = require("zod/v4/core");
758
+ var MODULE_NAME = "rulesync";
759
+ async function loadConfig(options = {}) {
760
+ const defaultConfig = getDefaultConfig();
761
+ if (options.noConfig) {
762
+ return {
763
+ config: defaultConfig,
764
+ isEmpty: true
765
+ };
653
766
  }
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
- }
767
+ try {
768
+ const loadOptions = {
769
+ name: MODULE_NAME,
770
+ cwd: options.cwd || process.cwd(),
771
+ rcFile: false,
772
+ // Disable rc file lookup
773
+ configFile: "rulesync",
774
+ // Will look for rulesync.jsonc, rulesync.ts, etc.
775
+ defaults: defaultConfig
776
+ };
777
+ if (options.configPath) {
778
+ loadOptions.configFile = options.configPath;
779
+ }
780
+ const { config, configFile } = await (0, import_c12.loadConfig)(loadOptions);
781
+ if (!config || Object.keys(config).length === 0) {
782
+ return {
783
+ config: defaultConfig,
784
+ isEmpty: true
785
+ };
786
+ }
787
+ try {
788
+ ConfigOptionsSchema.parse(config);
789
+ } catch (error) {
790
+ if (error instanceof import_core.$ZodError) {
791
+ const issues = error.issues.map((issue) => ` - ${issue.path.join(".")}: ${issue.message}`).join("\n");
792
+ throw new Error(`Invalid configuration in ${configFile}:
793
+ ${issues}`);
683
794
  }
795
+ throw error;
684
796
  }
685
- const contentPatterns = extractAugmentCodeIgnorePatternsFromContent(rule.content);
686
- patterns.push(...contentPatterns);
797
+ const processedConfig = postProcessConfig(config);
798
+ const result = {
799
+ config: processedConfig,
800
+ isEmpty: false
801
+ };
802
+ if (configFile) {
803
+ result.filepath = configFile;
804
+ }
805
+ return result;
806
+ } catch (error) {
807
+ throw new Error(
808
+ `Failed to load configuration: ${error instanceof Error ? error.message : String(error)}`
809
+ );
687
810
  }
688
- return patterns;
689
811
  }
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
700
- "**/dist/**",
701
- "**/build/**",
702
- "**/coverage/**",
703
- // Configuration that might contain sensitive data
704
- "**/config/production/**",
705
- "**/config/secrets/**",
706
- "**/deploy/prod/**",
707
- // Internal documentation
708
- "**/internal-docs/**",
709
- "**/proprietary/**",
710
- "**/personal-notes/**",
711
- "**/private/**",
712
- "**/confidential/**"
713
- ];
714
- return excludePatterns.some((pattern) => {
715
- const regex = new RegExp(pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*"));
716
- return regex.test(glob);
717
- });
812
+ function postProcessConfig(config) {
813
+ const processed = { ...config };
814
+ if (processed.baseDir && !Array.isArray(processed.baseDir)) {
815
+ processed.baseDir = [processed.baseDir];
816
+ }
817
+ if (config.targets || config.exclude) {
818
+ const baseTargets = config.targets || processed.defaultTargets;
819
+ if (config.exclude && config.exclude.length > 0) {
820
+ processed.defaultTargets = baseTargets.filter(
821
+ (target) => config.exclude && !config.exclude.includes(target)
822
+ );
823
+ } else {
824
+ processed.defaultTargets = baseTargets;
825
+ }
826
+ }
827
+ return processed;
718
828
  }
719
- function extractAugmentCodeIgnorePatternsFromContent(content) {
720
- const patterns = [];
721
- const lines = content.split("\n");
722
- for (const line of lines) {
723
- const trimmed = line.trim();
724
- if (trimmed.startsWith("# AUGMENT_IGNORE:") || trimmed.startsWith("# augmentignore:")) {
725
- const pattern = trimmed.replace(/^# (AUGMENT_IGNORE|augmentignore):\s*/, "").trim();
726
- if (pattern) {
727
- patterns.push(pattern);
829
+ function generateMinimalConfig(options) {
830
+ if (!options || Object.keys(options).length === 0) {
831
+ return generateSampleConfig();
832
+ }
833
+ const lines = ["{"];
834
+ if (options.targets || options.exclude) {
835
+ lines.push(` // Available tools: ${ALL_TOOL_TARGETS.join(", ")}`);
836
+ }
837
+ if (options.targets) {
838
+ lines.push(` "targets": ${JSON.stringify(options.targets)}`);
839
+ }
840
+ if (options.exclude) {
841
+ const comma = lines.length > 1 ? "," : "";
842
+ if (comma) lines[lines.length - 1] += comma;
843
+ lines.push(` "exclude": ${JSON.stringify(options.exclude)}`);
844
+ }
845
+ if (options.aiRulesDir) {
846
+ const comma = lines.length > 1 ? "," : "";
847
+ if (comma) lines[lines.length - 1] += comma;
848
+ lines.push(` "aiRulesDir": "${options.aiRulesDir}"`);
849
+ }
850
+ if (options.outputPaths) {
851
+ const comma = lines.length > 1 ? "," : "";
852
+ if (comma) lines[lines.length - 1] += comma;
853
+ lines.push(
854
+ ` "outputPaths": ${JSON.stringify(options.outputPaths, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n")}`
855
+ );
856
+ }
857
+ if (options.baseDir) {
858
+ const comma = lines.length > 1 ? "," : "";
859
+ if (comma) lines[lines.length - 1] += comma;
860
+ lines.push(` "baseDir": ${JSON.stringify(options.baseDir)}`);
861
+ }
862
+ if (options.delete !== void 0) {
863
+ const comma = lines.length > 1 ? "," : "";
864
+ if (comma) lines[lines.length - 1] += comma;
865
+ lines.push(` "delete": ${options.delete}`);
866
+ }
867
+ if (options.verbose !== void 0) {
868
+ const comma = lines.length > 1 ? "," : "";
869
+ if (comma) lines[lines.length - 1] += comma;
870
+ lines.push(` "verbose": ${options.verbose}`);
871
+ }
872
+ if (options.watch) {
873
+ const comma = lines.length > 1 ? "," : "";
874
+ if (comma) lines[lines.length - 1] += comma;
875
+ lines.push(
876
+ ` "watch": ${JSON.stringify(options.watch, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n")}`
877
+ );
878
+ }
879
+ lines.push("}");
880
+ return lines.join("\n");
881
+ }
882
+ function generateSampleConfig(options) {
883
+ const targets = options?.targets || ALL_TOOL_TARGETS;
884
+ const excludeValue = options?.exclude ? JSON.stringify(options.exclude) : null;
885
+ const aiRulesDir = options?.aiRulesDir || null;
886
+ const baseDir = options?.baseDir || null;
887
+ const deleteFlag = options?.delete || false;
888
+ const verbose = options?.verbose !== void 0 ? options.verbose : true;
889
+ return `{
890
+ // List of tools to generate configurations for
891
+ // Available: ${ALL_TOOL_TARGETS.join(", ")}
892
+ "targets": ${JSON.stringify(targets)},
893
+
894
+ // Tools to exclude from generation (overrides targets)
895
+ ${excludeValue ? `"exclude": ${excludeValue},` : '// "exclude": ["roo"],'}
896
+ ${aiRulesDir ? `
897
+ // Directory containing AI rule files
898
+ "aiRulesDir": "${aiRulesDir}",` : ""}
899
+
900
+ // Custom output paths for specific tools
901
+ "outputPaths": {
902
+ "copilot": ".github/copilot-instructions.md"
903
+ },
904
+ ${baseDir ? `
905
+ // Base directory for generation
906
+ "baseDir": "${baseDir}",` : `
907
+ // Base directory or directories for generation
908
+ // "baseDir": "./packages",
909
+ // "baseDir": ["./packages/frontend", "./packages/backend"],`}
910
+
911
+ // Delete existing files before generating
912
+ "delete": ${deleteFlag},
913
+
914
+ // Enable verbose output
915
+ "verbose": ${verbose},
916
+
917
+ // Watch configuration
918
+ "watch": {
919
+ "enabled": false,
920
+ "interval": 1000,
921
+ "ignore": ["node_modules/**", "dist/**"]
922
+ }
923
+ }
924
+ `;
925
+ }
926
+ function mergeWithCliOptions(config, cliOptions) {
927
+ const merged = { ...config };
928
+ if (cliOptions.verbose !== void 0) {
929
+ merged.verbose = cliOptions.verbose;
930
+ }
931
+ if (cliOptions.delete !== void 0) {
932
+ merged.delete = cliOptions.delete;
933
+ }
934
+ if (cliOptions.baseDirs && cliOptions.baseDirs.length > 0) {
935
+ merged.baseDir = cliOptions.baseDirs;
936
+ }
937
+ if (cliOptions.tools && cliOptions.tools.length > 0) {
938
+ merged.defaultTargets = cliOptions.tools;
939
+ merged.exclude = void 0;
940
+ }
941
+ return merged;
942
+ }
943
+
944
+ // src/utils/file.ts
945
+ var import_promises2 = require("fs/promises");
946
+ var import_node_path = require("path");
947
+ async function ensureDir(dirPath) {
948
+ try {
949
+ await (0, import_promises2.stat)(dirPath);
950
+ } catch {
951
+ await (0, import_promises2.mkdir)(dirPath, { recursive: true });
952
+ }
953
+ }
954
+ async function readFileContent(filepath) {
955
+ return (0, import_promises2.readFile)(filepath, "utf-8");
956
+ }
957
+ async function writeFileContent(filepath, content) {
958
+ await ensureDir((0, import_node_path.dirname)(filepath));
959
+ await (0, import_promises2.writeFile)(filepath, content, "utf-8");
960
+ }
961
+ async function fileExists(filepath) {
962
+ try {
963
+ await (0, import_promises2.stat)(filepath);
964
+ return true;
965
+ } catch {
966
+ return false;
967
+ }
968
+ }
969
+ async function findFiles(dir, extension = ".md") {
970
+ try {
971
+ const files = await (0, import_promises2.readdir)(dir);
972
+ return files.filter((file) => file.endsWith(extension)).map((file) => (0, import_node_path.join)(dir, file));
973
+ } catch {
974
+ return [];
975
+ }
976
+ }
977
+ async function removeDirectory(dirPath) {
978
+ const dangerousPaths = [".", "/", "~", "src", "node_modules"];
979
+ if (dangerousPaths.includes(dirPath) || dirPath === "") {
980
+ console.warn(`Skipping deletion of dangerous path: ${dirPath}`);
981
+ return;
982
+ }
983
+ try {
984
+ if (await fileExists(dirPath)) {
985
+ await (0, import_promises2.rm)(dirPath, { recursive: true, force: true });
986
+ }
987
+ } catch (error) {
988
+ console.warn(`Failed to remove directory ${dirPath}:`, error);
989
+ }
990
+ }
991
+ async function removeFile(filepath) {
992
+ try {
993
+ if (await fileExists(filepath)) {
994
+ await (0, import_promises2.rm)(filepath);
995
+ }
996
+ } catch (error) {
997
+ console.warn(`Failed to remove file ${filepath}:`, error);
998
+ }
999
+ }
1000
+ async function removeClaudeGeneratedFiles() {
1001
+ const filesToRemove = ["CLAUDE.md", ".claude/memories"];
1002
+ for (const fileOrDir of filesToRemove) {
1003
+ if (fileOrDir.endsWith("/memories")) {
1004
+ await removeDirectory(fileOrDir);
1005
+ } else {
1006
+ await removeFile(fileOrDir);
1007
+ }
1008
+ }
1009
+ }
1010
+
1011
+ // src/utils/rules.ts
1012
+ function isToolSpecificRule(rule, targetTool) {
1013
+ const filename = rule.filename;
1014
+ const toolPatterns = {
1015
+ "augmentcode-legacy": /^specification-augmentcode-legacy-/i,
1016
+ augmentcode: /^specification-augmentcode-/i,
1017
+ copilot: /^specification-copilot-/i,
1018
+ cursor: /^specification-cursor-/i,
1019
+ cline: /^specification-cline-/i,
1020
+ claudecode: /^specification-claudecode-/i,
1021
+ roo: /^specification-roo-/i,
1022
+ geminicli: /^specification-geminicli-/i,
1023
+ kiro: /^specification-kiro-/i
1024
+ };
1025
+ for (const [tool, pattern] of Object.entries(toolPatterns)) {
1026
+ if (pattern.test(filename)) {
1027
+ return tool === targetTool;
1028
+ }
1029
+ }
1030
+ return true;
1031
+ }
1032
+
1033
+ // src/cli/commands/config.ts
1034
+ async function configCommand(options = {}) {
1035
+ if (options.init) {
1036
+ await initConfig(options);
1037
+ return;
1038
+ }
1039
+ await showConfig();
1040
+ }
1041
+ async function showConfig() {
1042
+ console.log("Loading configuration...\n");
1043
+ try {
1044
+ const result = await loadConfig();
1045
+ if (result.isEmpty) {
1046
+ console.log("No configuration file found. Using default configuration.\n");
1047
+ } else {
1048
+ console.log(`Configuration loaded from: ${result.filepath}
1049
+ `);
1050
+ }
1051
+ console.log("Current configuration:");
1052
+ console.log("=====================");
1053
+ const config = result.config;
1054
+ console.log(`
1055
+ AI Rules Directory: ${config.aiRulesDir}`);
1056
+ console.log(`
1057
+ Default Targets: ${config.defaultTargets.join(", ")}`);
1058
+ if (config.exclude && config.exclude.length > 0) {
1059
+ console.log(`Excluded Targets: ${config.exclude.join(", ")}`);
1060
+ }
1061
+ console.log("\nOutput Paths:");
1062
+ for (const [tool, outputPath] of Object.entries(config.outputPaths)) {
1063
+ console.log(` ${tool}: ${outputPath}`);
1064
+ }
1065
+ if (config.baseDir) {
1066
+ const dirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
1067
+ console.log(`
1068
+ Base Directories: ${dirs.join(", ")}`);
1069
+ }
1070
+ console.log(`
1071
+ Verbose: ${config.verbose || false}`);
1072
+ console.log(`Delete before generate: ${config.delete || false}`);
1073
+ if (config.watch) {
1074
+ console.log("\nWatch Configuration:");
1075
+ console.log(` Enabled: ${config.watch.enabled || false}`);
1076
+ if (config.watch.interval) {
1077
+ console.log(` Interval: ${config.watch.interval}ms`);
1078
+ }
1079
+ if (config.watch.ignore && config.watch.ignore.length > 0) {
1080
+ console.log(` Ignore patterns: ${config.watch.ignore.join(", ")}`);
728
1081
  }
729
1082
  }
730
- if (trimmed.startsWith("# AUGMENT_INCLUDE:") || trimmed.startsWith("# augmentinclude:")) {
731
- const pattern = trimmed.replace(/^# (AUGMENT_INCLUDE|augmentinclude):\s*/, "").trim();
732
- if (pattern) {
733
- patterns.push(`!${pattern}`);
1083
+ console.log("\nTip: Use 'rulesync config init' to create a configuration file.");
1084
+ } catch (error) {
1085
+ console.error(
1086
+ "\u274C Failed to load configuration:",
1087
+ error instanceof Error ? error.message : String(error)
1088
+ );
1089
+ process.exit(1);
1090
+ }
1091
+ }
1092
+ var FORMAT_CONFIG = {
1093
+ jsonc: {
1094
+ filename: "rulesync.jsonc",
1095
+ generator: generateJsoncConfig
1096
+ },
1097
+ ts: {
1098
+ filename: "rulesync.ts",
1099
+ generator: generateTsConfig
1100
+ }
1101
+ };
1102
+ async function initConfig(options) {
1103
+ const validFormats = Object.keys(FORMAT_CONFIG);
1104
+ const selectedFormat = options.format || "jsonc";
1105
+ if (!validFormats.includes(selectedFormat)) {
1106
+ console.error(
1107
+ `\u274C Invalid format: ${selectedFormat}. Valid formats are: ${validFormats.join(", ")}`
1108
+ );
1109
+ process.exit(1);
1110
+ }
1111
+ const formatConfig = FORMAT_CONFIG[selectedFormat];
1112
+ const filename = formatConfig.filename;
1113
+ const configOptions = {};
1114
+ if (options.targets) {
1115
+ const targets = options.targets.split(",").map((t) => t.trim());
1116
+ const validTargets = [];
1117
+ for (const target of targets) {
1118
+ const result = ToolTargetSchema.safeParse(target);
1119
+ if (result.success) {
1120
+ validTargets.push(result.data);
1121
+ } else {
1122
+ console.error(`\u274C Invalid target: ${target}`);
1123
+ process.exit(1);
734
1124
  }
735
1125
  }
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);
738
- if (matches) {
739
- patterns.push(...matches.map((m) => m.replace(/['"`]/g, "")));
1126
+ configOptions.targets = validTargets;
1127
+ }
1128
+ if (options.exclude) {
1129
+ const excludes = options.exclude.split(",").map((t) => t.trim());
1130
+ const validExcludes = [];
1131
+ for (const exclude of excludes) {
1132
+ const result = ToolTargetSchema.safeParse(exclude);
1133
+ if (result.success) {
1134
+ validExcludes.push(result.data);
1135
+ } else {
1136
+ console.error(`\u274C Invalid exclude target: ${exclude}`);
1137
+ process.exit(1);
740
1138
  }
741
1139
  }
1140
+ configOptions.exclude = validExcludes;
1141
+ }
1142
+ if (options.aiRulesDir) {
1143
+ configOptions.aiRulesDir = options.aiRulesDir;
1144
+ }
1145
+ if (options.baseDir) {
1146
+ configOptions.baseDir = options.baseDir;
1147
+ }
1148
+ if (options.verbose !== void 0) {
1149
+ configOptions.verbose = options.verbose;
1150
+ }
1151
+ if (options.delete !== void 0) {
1152
+ configOptions.delete = options.delete;
1153
+ }
1154
+ const content = formatConfig.generator(configOptions);
1155
+ const filepath = import_node_path2.default.join(process.cwd(), filename);
1156
+ try {
1157
+ const fs2 = await import("fs/promises");
1158
+ await fs2.access(filepath);
1159
+ console.error(`\u274C Configuration file already exists: ${filepath}`);
1160
+ console.log("Remove the existing file or choose a different format.");
1161
+ process.exit(1);
1162
+ } catch {
1163
+ }
1164
+ try {
1165
+ (0, import_node_fs.writeFileSync)(filepath, content, "utf-8");
1166
+ console.log(`\u2705 Created configuration file: ${filepath}`);
1167
+ console.log("\nYou can now customize the configuration to fit your needs.");
1168
+ console.log("Run 'rulesync generate' to use the new configuration.");
1169
+ } catch (error) {
1170
+ console.error(
1171
+ `\u274C Failed to create configuration file: ${error instanceof Error ? error.message : String(error)}`
1172
+ );
1173
+ process.exit(1);
742
1174
  }
743
- return patterns;
744
1175
  }
1176
+ function generateJsoncConfig(options) {
1177
+ if (options && Object.keys(options).length > 0) {
1178
+ return generateMinimalConfig(options);
1179
+ }
1180
+ return generateSampleConfig(options);
1181
+ }
1182
+ function generateTsConfig(options) {
1183
+ if (!options || Object.keys(options).length === 0) {
1184
+ return `import type { ConfigOptions } from "rulesync";
1185
+
1186
+ const config: ConfigOptions = {
1187
+ // List of tools to generate configurations for
1188
+ // Available: ${ALL_TOOL_TARGETS.join(", ")}
1189
+ targets: ${JSON.stringify(ALL_TOOL_TARGETS)},
1190
+
1191
+ // Custom output paths for specific tools
1192
+ // outputPaths: {
1193
+ // copilot: ".github/copilot-instructions.md",
1194
+ // },
1195
+
1196
+ // Delete existing files before generating
1197
+ // delete: false,
1198
+
1199
+ // Enable verbose output
1200
+ verbose: true,
1201
+ };
745
1202
 
746
- // src/generators/ignore/kiro.ts
747
- var import_node_path2 = require("path");
748
- async function generateKiroIgnoreFiles(rules, config, baseDir) {
749
- const outputs = [];
750
- const aiignoreContent = generateAiignoreContent(rules);
751
- const outputPath = baseDir || process.cwd();
752
- const filepath = (0, import_node_path2.join)(outputPath, ".aiignore");
753
- outputs.push({
754
- tool: "kiro",
755
- filepath,
756
- content: aiignoreContent
757
- });
758
- return outputs;
759
- }
760
- function generateAiignoreContent(rules) {
761
- const lines = [
762
- "# Generated by rulesync - Kiro AI-specific exclusions",
763
- "# This file excludes files that can be in Git but shouldn't be read by the AI",
764
- ""
765
- ];
766
- lines.push(
767
- "# Data files AI shouldn't process",
768
- "*.csv",
769
- "*.tsv",
770
- "*.sqlite",
771
- "*.db",
772
- "",
773
- "# Large binary files",
774
- "*.zip",
775
- "*.tar.gz",
776
- "*.rar",
777
- "",
778
- "# Sensitive documentation",
779
- "internal-docs/",
780
- "confidential/",
781
- "",
782
- "# Test data that might confuse AI",
783
- "test/fixtures/large-*.json",
784
- "benchmark-results/",
785
- "",
786
- "# Reinforce critical exclusions from .gitignore",
787
- "*.pem",
788
- "*.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("");
1203
+ export default config;`;
797
1204
  }
798
- return lines.join("\n");
1205
+ const configLines = [];
1206
+ if (options.targets) {
1207
+ configLines.push(` targets: ${JSON.stringify(options.targets)}`);
1208
+ }
1209
+ if (options.exclude) {
1210
+ configLines.push(` exclude: ${JSON.stringify(options.exclude)}`);
1211
+ }
1212
+ if (options.aiRulesDir) {
1213
+ configLines.push(` aiRulesDir: "${options.aiRulesDir}"`);
1214
+ }
1215
+ if (options.outputPaths) {
1216
+ const pathsStr = JSON.stringify(options.outputPaths, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
1217
+ configLines.push(` outputPaths: ${pathsStr}`);
1218
+ }
1219
+ if (options.baseDir) {
1220
+ configLines.push(` baseDir: ${JSON.stringify(options.baseDir)}`);
1221
+ }
1222
+ if (options.delete !== void 0) {
1223
+ configLines.push(` delete: ${options.delete}`);
1224
+ }
1225
+ if (options.verbose !== void 0) {
1226
+ configLines.push(` verbose: ${options.verbose}`);
1227
+ }
1228
+ if (options.watch) {
1229
+ const watchStr = JSON.stringify(options.watch, null, 4).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
1230
+ configLines.push(` watch: ${watchStr}`);
1231
+ }
1232
+ const configContent = `import type { ConfigOptions } from "rulesync";
1233
+
1234
+ const config: ConfigOptions = {
1235
+ ${configLines.join(",\n")},
1236
+ };
1237
+
1238
+ export default config;
1239
+ `;
1240
+ return configContent;
799
1241
  }
800
- function extractIgnorePatternsFromRules2(rules) {
1242
+
1243
+ // src/cli/commands/generate.ts
1244
+ var import_node_path12 = require("path");
1245
+
1246
+ // src/generators/ignore/shared-factory.ts
1247
+ var import_node_path3 = require("path");
1248
+
1249
+ // src/generators/ignore/shared-helpers.ts
1250
+ function extractIgnorePatternsFromRules(rules) {
801
1251
  const patterns = [];
802
1252
  for (const rule of rules) {
803
1253
  if (rule.frontmatter.globs && rule.frontmatter.globs.length > 0) {
@@ -815,20 +1265,28 @@ function extractIgnorePatternsFromRules2(rules) {
815
1265
  }
816
1266
  function shouldExcludeFromAI(glob) {
817
1267
  const excludePatterns = [
818
- // Test and fixture files that might be large or confusing
819
- "**/test/fixtures/**",
1268
+ // Large generated files that slow indexing
1269
+ "**/assets/generated/**",
1270
+ "**/public/build/**",
1271
+ // Test fixtures with potentially sensitive data
820
1272
  "**/tests/fixtures/**",
1273
+ "**/test/fixtures/**",
821
1274
  "**/*.fixture.*",
822
- // Build and generated files
1275
+ // Build outputs that provide little value for AI context
823
1276
  "**/dist/**",
824
1277
  "**/build/**",
825
1278
  "**/coverage/**",
826
1279
  // Configuration that might contain sensitive data
827
1280
  "**/config/production/**",
1281
+ "**/config/secrets/**",
828
1282
  "**/config/prod/**",
1283
+ "**/deploy/prod/**",
829
1284
  "**/*.prod.*",
830
- // Documentation that might be sensitive
1285
+ // Internal documentation that might be sensitive
831
1286
  "**/internal/**",
1287
+ "**/internal-docs/**",
1288
+ "**/proprietary/**",
1289
+ "**/personal-notes/**",
832
1290
  "**/private/**",
833
1291
  "**/confidential/**"
834
1292
  ];
@@ -848,6 +1306,18 @@ function extractIgnorePatternsFromContent(content) {
848
1306
  patterns.push(pattern);
849
1307
  }
850
1308
  }
1309
+ if (trimmed.startsWith("# AUGMENT_IGNORE:") || trimmed.startsWith("# augmentignore:")) {
1310
+ const pattern = trimmed.replace(/^# (AUGMENT_IGNORE|augmentignore):\s*/, "").trim();
1311
+ if (pattern) {
1312
+ patterns.push(pattern);
1313
+ }
1314
+ }
1315
+ if (trimmed.startsWith("# AUGMENT_INCLUDE:") || trimmed.startsWith("# augmentinclude:")) {
1316
+ const pattern = trimmed.replace(/^# (AUGMENT_INCLUDE|augmentinclude):\s*/, "").trim();
1317
+ if (pattern) {
1318
+ patterns.push(`!${pattern}`);
1319
+ }
1320
+ }
851
1321
  if (trimmed.includes("exclude") || trimmed.includes("ignore")) {
852
1322
  const matches = trimmed.match(/['"`]([^'"`]+\.(log|tmp|cache|temp))['"`]/g);
853
1323
  if (matches) {
@@ -855,7 +1325,369 @@ function extractIgnorePatternsFromContent(content) {
855
1325
  }
856
1326
  }
857
1327
  }
858
- return patterns;
1328
+ return patterns;
1329
+ }
1330
+ function extractAugmentCodeIgnorePatternsFromContent(content) {
1331
+ const patterns = [];
1332
+ const lines = content.split("\n");
1333
+ for (const line of lines) {
1334
+ const trimmed = line.trim();
1335
+ if (trimmed.startsWith("# AUGMENT_IGNORE:") || trimmed.startsWith("# augmentignore:")) {
1336
+ const pattern = trimmed.replace(/^# (AUGMENT_IGNORE|augmentignore):\s*/, "").trim();
1337
+ if (pattern) {
1338
+ patterns.push(pattern);
1339
+ }
1340
+ }
1341
+ if (trimmed.startsWith("# AUGMENT_INCLUDE:") || trimmed.startsWith("# augmentinclude:")) {
1342
+ const pattern = trimmed.replace(/^# (AUGMENT_INCLUDE|augmentinclude):\s*/, "").trim();
1343
+ if (pattern) {
1344
+ patterns.push(`!${pattern}`);
1345
+ }
1346
+ }
1347
+ if (trimmed.includes("large file") || trimmed.includes("binary") || trimmed.includes("media")) {
1348
+ const regex = /['"`]([^'"`]+\.(mp4|avi|zip|tar\.gz|rar|pdf|doc|xlsx))['"`]/g;
1349
+ let match;
1350
+ while ((match = regex.exec(trimmed)) !== null) {
1351
+ if (match[1]) {
1352
+ patterns.push(match[1]);
1353
+ }
1354
+ }
1355
+ }
1356
+ }
1357
+ return patterns;
1358
+ }
1359
+
1360
+ // src/generators/ignore/shared-factory.ts
1361
+ function generateIgnoreFile(rules, config, ignoreConfig, baseDir) {
1362
+ const outputs = [];
1363
+ const content = generateIgnoreContent(rules, ignoreConfig);
1364
+ const outputPath = baseDir || process.cwd();
1365
+ const filepath = (0, import_node_path3.join)(outputPath, ignoreConfig.filename);
1366
+ outputs.push({
1367
+ tool: ignoreConfig.tool,
1368
+ filepath,
1369
+ content
1370
+ });
1371
+ return outputs;
1372
+ }
1373
+ function generateIgnoreContent(rules, config) {
1374
+ const lines = [];
1375
+ lines.push(...config.header);
1376
+ lines.push("");
1377
+ if (config.includeCommonPatterns) {
1378
+ lines.push(...getCommonIgnorePatterns());
1379
+ }
1380
+ if (config.corePatterns.length > 0) {
1381
+ lines.push(...config.corePatterns);
1382
+ lines.push("");
1383
+ }
1384
+ const rulePatterns = extractIgnorePatternsFromRules(rules);
1385
+ const customPatterns = config.customPatternProcessor ? config.customPatternProcessor(rules) : [];
1386
+ const allPatterns = [...rulePatterns, ...customPatterns];
1387
+ if (allPatterns.length > 0) {
1388
+ const headerText = config.projectPatternsHeader || "# \u2500\u2500\u2500\u2500\u2500 Project-specific exclusions from rulesync rules \u2500\u2500\u2500\u2500\u2500";
1389
+ lines.push(headerText);
1390
+ lines.push(...allPatterns);
1391
+ lines.push("");
1392
+ }
1393
+ return lines.join("\n");
1394
+ }
1395
+ function getCommonIgnorePatterns() {
1396
+ return [
1397
+ "# \u2500\u2500\u2500\u2500\u2500 Source Control Metadata \u2500\u2500\u2500\u2500\u2500",
1398
+ ".git/",
1399
+ ".svn/",
1400
+ ".hg/",
1401
+ ".idea/",
1402
+ "*.iml",
1403
+ ".vscode/settings.json",
1404
+ "",
1405
+ "# \u2500\u2500\u2500\u2500\u2500 Build Artifacts \u2500\u2500\u2500\u2500\u2500",
1406
+ "/out/",
1407
+ "/dist/",
1408
+ "/target/",
1409
+ "/build/",
1410
+ "*.class",
1411
+ "*.jar",
1412
+ "*.war",
1413
+ "",
1414
+ "# \u2500\u2500\u2500\u2500\u2500 Secrets & Credentials \u2500\u2500\u2500\u2500\u2500",
1415
+ "# Environment files",
1416
+ ".env",
1417
+ ".env.*",
1418
+ "!.env.example",
1419
+ "",
1420
+ "# Key material",
1421
+ "*.pem",
1422
+ "*.key",
1423
+ "*.crt",
1424
+ "*.p12",
1425
+ "*.pfx",
1426
+ "*.der",
1427
+ "id_rsa*",
1428
+ "id_dsa*",
1429
+ "*.ppk",
1430
+ "",
1431
+ "# Cloud and service configs",
1432
+ "aws-credentials.json",
1433
+ "gcp-service-account*.json",
1434
+ "azure-credentials.json",
1435
+ "secrets/**",
1436
+ "config/secrets/",
1437
+ "**/secrets/",
1438
+ "",
1439
+ "# Database credentials",
1440
+ "database.yml",
1441
+ "**/database/config.*",
1442
+ "",
1443
+ "# API keys and tokens",
1444
+ "**/apikeys/",
1445
+ "**/*_token*",
1446
+ "**/*_secret*",
1447
+ "**/*api_key*",
1448
+ "",
1449
+ "# \u2500\u2500\u2500\u2500\u2500 Infrastructure & Deployment \u2500\u2500\u2500\u2500\u2500",
1450
+ "# Terraform state",
1451
+ "*.tfstate",
1452
+ "*.tfstate.*",
1453
+ ".terraform/",
1454
+ "",
1455
+ "# Kubernetes secrets",
1456
+ "**/k8s/**/secret*.yaml",
1457
+ "**/kubernetes/**/secret*.yaml",
1458
+ "",
1459
+ "# Docker secrets",
1460
+ "docker-compose.override.yml",
1461
+ "**/docker/secrets/",
1462
+ "",
1463
+ "# \u2500\u2500\u2500\u2500\u2500 Logs & Runtime Data \u2500\u2500\u2500\u2500\u2500",
1464
+ "*.log",
1465
+ "*.tmp",
1466
+ "*.cache",
1467
+ "logs/",
1468
+ "/var/log/",
1469
+ "coverage/",
1470
+ ".nyc_output/",
1471
+ "",
1472
+ "# \u2500\u2500\u2500\u2500\u2500 Large Data Files \u2500\u2500\u2500\u2500\u2500",
1473
+ "*.csv",
1474
+ "*.xlsx",
1475
+ "*.sqlite",
1476
+ "*.db",
1477
+ "*.dump",
1478
+ "data/",
1479
+ "datasets/",
1480
+ "",
1481
+ "# \u2500\u2500\u2500\u2500\u2500 Node.js Specific \u2500\u2500\u2500\u2500\u2500",
1482
+ "node_modules/",
1483
+ ".pnpm-store/",
1484
+ ".yarn/",
1485
+ ".next/",
1486
+ ".nuxt/",
1487
+ ".cache/",
1488
+ ".parcel-cache/",
1489
+ "",
1490
+ "# \u2500\u2500\u2500\u2500\u2500 Python Specific \u2500\u2500\u2500\u2500\u2500",
1491
+ "__pycache__/",
1492
+ "*.pyc",
1493
+ "*.pyo",
1494
+ "*.pyd",
1495
+ ".Python",
1496
+ "venv/",
1497
+ ".venv/",
1498
+ "env/",
1499
+ ".env/",
1500
+ "",
1501
+ "# \u2500\u2500\u2500\u2500\u2500 Java Specific \u2500\u2500\u2500\u2500\u2500",
1502
+ "*.class",
1503
+ "*.jar",
1504
+ "*.war",
1505
+ "target/",
1506
+ ""
1507
+ ];
1508
+ }
1509
+ var ignoreConfigs = {
1510
+ junie: {
1511
+ tool: "junie",
1512
+ filename: ".aiignore",
1513
+ header: [
1514
+ "# Generated by rulesync - JetBrains Junie AI ignore file",
1515
+ "# This file controls which files the AI can access automatically",
1516
+ "# AI must ask before reading or editing matched files/directories"
1517
+ ],
1518
+ corePatterns: [
1519
+ "# \u2500\u2500\u2500\u2500\u2500 Allow specific source files (uncomment as needed) \u2500\u2500\u2500\u2500\u2500",
1520
+ "# !src/**/*.ts",
1521
+ "# !src/**/*.js",
1522
+ "# !lib/**/*.py",
1523
+ "# !src/main/**/*.java"
1524
+ ],
1525
+ includeCommonPatterns: true
1526
+ },
1527
+ kiro: {
1528
+ tool: "kiro",
1529
+ filename: ".aiignore",
1530
+ header: [
1531
+ "# Generated by rulesync - Kiro AI-specific exclusions",
1532
+ "# This file excludes files that can be in Git but shouldn't be read by the AI"
1533
+ ],
1534
+ corePatterns: [
1535
+ "# Data files AI shouldn't process",
1536
+ "*.csv",
1537
+ "*.tsv",
1538
+ "*.sqlite",
1539
+ "*.db",
1540
+ "",
1541
+ "# Large binary files",
1542
+ "*.zip",
1543
+ "*.tar.gz",
1544
+ "*.rar",
1545
+ "",
1546
+ "# Sensitive documentation",
1547
+ "internal-docs/",
1548
+ "confidential/",
1549
+ "",
1550
+ "# Test data that might confuse AI",
1551
+ "test/fixtures/large-*.json",
1552
+ "benchmark-results/",
1553
+ "",
1554
+ "# Reinforce critical exclusions from .gitignore",
1555
+ "*.pem",
1556
+ "*.key",
1557
+ ".env*"
1558
+ ],
1559
+ includeCommonPatterns: false,
1560
+ projectPatternsHeader: "# Project-specific exclusions from rulesync rules"
1561
+ },
1562
+ augmentcode: {
1563
+ tool: "augmentcode",
1564
+ filename: ".augmentignore",
1565
+ header: [
1566
+ "# Generated by rulesync - AugmentCode ignore patterns",
1567
+ "# AugmentCode uses a two-tier approach: .gitignore first, then .augmentignore",
1568
+ "# This file provides Augment-specific exclusions and re-inclusions"
1569
+ ],
1570
+ corePatterns: [
1571
+ "# Security and Secrets (critical exclusions)",
1572
+ "# Environment files",
1573
+ ".env*",
1574
+ "",
1575
+ "# Private keys and certificates",
1576
+ "*.pem",
1577
+ "*.key",
1578
+ "*.p12",
1579
+ "*.crt",
1580
+ "*.der",
1581
+ "",
1582
+ "# SSH keys",
1583
+ "id_rsa*",
1584
+ "id_dsa*",
1585
+ "",
1586
+ "# AWS credentials",
1587
+ ".aws/",
1588
+ "aws-exports.js",
1589
+ "",
1590
+ "# API keys and tokens",
1591
+ "**/apikeys/",
1592
+ "**/*_token*",
1593
+ "**/*_secret*",
1594
+ "",
1595
+ "# Build Artifacts and Dependencies",
1596
+ "# Build outputs",
1597
+ "dist/",
1598
+ "build/",
1599
+ "out/",
1600
+ "target/",
1601
+ "",
1602
+ "# Dependencies",
1603
+ "node_modules/",
1604
+ "venv/",
1605
+ "*.egg-info/",
1606
+ "",
1607
+ "# Logs",
1608
+ "*.log",
1609
+ "logs/",
1610
+ "",
1611
+ "# Temporary files",
1612
+ "*.tmp",
1613
+ "*.swp",
1614
+ "*.swo",
1615
+ "*~",
1616
+ "",
1617
+ "# Large Files and Media",
1618
+ "# Binary files",
1619
+ "*.jar",
1620
+ "*.png",
1621
+ "*.jpg",
1622
+ "*.jpeg",
1623
+ "*.gif",
1624
+ "*.mp4",
1625
+ "*.avi",
1626
+ "*.zip",
1627
+ "*.tar.gz",
1628
+ "*.rar",
1629
+ "",
1630
+ "# Database files",
1631
+ "*.sqlite",
1632
+ "*.db",
1633
+ "*.mdb",
1634
+ "",
1635
+ "# Data files",
1636
+ "*.csv",
1637
+ "*.tsv",
1638
+ "*.xlsx",
1639
+ "",
1640
+ "# Performance Optimization",
1641
+ "# Exclude files that are too large for effective AI processing",
1642
+ "**/*.{mp4,avi,mov,mkv}",
1643
+ "**/*.{zip,tar,gz,rar}",
1644
+ "**/*.{pdf,doc,docx}",
1645
+ "**/logs/**/*.log",
1646
+ "",
1647
+ "# But include small configuration files",
1648
+ "!**/config.{json,yaml,yml}",
1649
+ "",
1650
+ "# Team Collaboration",
1651
+ "# Exclude personal IDE settings",
1652
+ ".vscode/settings.json",
1653
+ ".idea/workspace.xml",
1654
+ "",
1655
+ "# But include shared team settings",
1656
+ "!.vscode/extensions.json",
1657
+ "!.idea/codeStyles/",
1658
+ "",
1659
+ "# Exclude test fixtures with sensitive data",
1660
+ "tests/fixtures/real-data/**",
1661
+ "",
1662
+ "# Re-include important documentation",
1663
+ "!vendor/*/README.md",
1664
+ "!third-party/*/LICENSE"
1665
+ ],
1666
+ includeCommonPatterns: false,
1667
+ projectPatternsHeader: "# Project-specific patterns from rulesync rules",
1668
+ customPatternProcessor: (rules) => {
1669
+ const augmentPatterns = [];
1670
+ for (const rule of rules) {
1671
+ augmentPatterns.push(...extractAugmentCodeIgnorePatternsFromContent(rule.content));
1672
+ }
1673
+ return augmentPatterns;
1674
+ }
1675
+ }
1676
+ };
1677
+
1678
+ // src/generators/ignore/augmentcode.ts
1679
+ async function generateAugmentCodeIgnoreFiles(rules, config, baseDir) {
1680
+ return generateIgnoreFile(rules, config, ignoreConfigs.augmentcode, baseDir);
1681
+ }
1682
+
1683
+ // src/generators/ignore/junie.ts
1684
+ async function generateJunieIgnoreFiles(rules, config, baseDir) {
1685
+ return generateIgnoreFile(rules, config, ignoreConfigs.junie, baseDir);
1686
+ }
1687
+
1688
+ // src/generators/ignore/kiro.ts
1689
+ async function generateKiroIgnoreFiles(rules, config, baseDir) {
1690
+ return generateIgnoreFile(rules, config, ignoreConfigs.kiro, baseDir);
859
1691
  }
860
1692
 
861
1693
  // src/generators/rules/augmentcode.ts
@@ -867,75 +1699,6 @@ var import_node_path5 = require("path");
867
1699
  // src/utils/ignore.ts
868
1700
  var import_node_path4 = require("path");
869
1701
  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
1702
  var cachedIgnorePatterns = null;
940
1703
  async function loadIgnorePatterns(baseDir = process.cwd()) {
941
1704
  if (cachedIgnorePatterns) {
@@ -1014,16 +1777,60 @@ async function generateRulesConfig(rules, config, generatorConfig, baseDir) {
1014
1777
  const ignorePatterns = await loadIgnorePatterns(baseDir);
1015
1778
  if (ignorePatterns.patterns.length > 0) {
1016
1779
  const ignorePath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
1017
- const ignoreContent = generateIgnoreFile(ignorePatterns.patterns, generatorConfig.tool);
1780
+ const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1781
+ outputs.push({
1782
+ tool: generatorConfig.tool,
1783
+ filepath: ignorePath,
1784
+ content: ignoreContent
1785
+ });
1786
+ }
1787
+ return outputs;
1788
+ }
1789
+ async function generateComplexRules(rules, config, generatorConfig, baseDir) {
1790
+ const outputs = [];
1791
+ const rootRules = rules.filter((r) => r.frontmatter.root === true);
1792
+ const detailRules = rules.filter((r) => r.frontmatter.root === false);
1793
+ const rootRule = rootRules[0];
1794
+ if (generatorConfig.generateDetailContent && generatorConfig.detailSubDir) {
1795
+ for (const rule of detailRules) {
1796
+ const content = generatorConfig.generateDetailContent(rule);
1797
+ const filepath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.detailSubDir, `${rule.filename}.md`) : (0, import_node_path5.join)(generatorConfig.detailSubDir, `${rule.filename}.md`);
1798
+ outputs.push({
1799
+ tool: generatorConfig.tool,
1800
+ filepath,
1801
+ content
1802
+ });
1803
+ }
1804
+ }
1805
+ if (generatorConfig.generateRootContent && generatorConfig.rootFilePath) {
1806
+ const rootContent = generatorConfig.generateRootContent(rootRule, detailRules, baseDir);
1807
+ const rootFilepath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.rootFilePath) : generatorConfig.rootFilePath;
1808
+ outputs.push({
1809
+ tool: generatorConfig.tool,
1810
+ filepath: rootFilepath,
1811
+ content: rootContent
1812
+ });
1813
+ }
1814
+ const ignorePatterns = await loadIgnorePatterns(baseDir);
1815
+ if (ignorePatterns.patterns.length > 0) {
1816
+ const ignorePath = baseDir ? (0, import_node_path5.join)(baseDir, generatorConfig.ignoreFileName) : generatorConfig.ignoreFileName;
1817
+ const ignoreContent = generateIgnoreFile2(ignorePatterns.patterns, generatorConfig.tool);
1018
1818
  outputs.push({
1019
1819
  tool: generatorConfig.tool,
1020
1820
  filepath: ignorePath,
1021
1821
  content: ignoreContent
1022
1822
  });
1823
+ if (generatorConfig.updateAdditionalConfig) {
1824
+ const additionalOutputs = await generatorConfig.updateAdditionalConfig(
1825
+ ignorePatterns.patterns,
1826
+ baseDir
1827
+ );
1828
+ outputs.push(...additionalOutputs);
1829
+ }
1023
1830
  }
1024
1831
  return outputs;
1025
1832
  }
1026
- function generateIgnoreFile(patterns, tool) {
1833
+ function generateIgnoreFile2(patterns, tool) {
1027
1834
  const lines = [
1028
1835
  "# Generated by rulesync from .rulesyncignore",
1029
1836
  "# This file is automatically generated. Do not edit manually."
@@ -1110,19 +1917,6 @@ function generateLegacyGuidelinesFile(allRules) {
1110
1917
 
1111
1918
  // src/generators/rules/claudecode.ts
1112
1919
  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
1126
1920
  async function generateClaudecodeConfig(rules, config, baseDir) {
1127
1921
  const outputs = [];
1128
1922
  const rootRules = rules.filter((r) => r.frontmatter.root === true);
@@ -1327,39 +2121,18 @@ function determineCursorRuleType(frontmatter) {
1327
2121
  }
1328
2122
 
1329
2123
  // src/generators/rules/geminicli.ts
1330
- var import_node_path10 = require("path");
1331
2124
  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({
2125
+ const generatorConfig = {
1348
2126
  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;
2127
+ fileExtension: ".md",
2128
+ ignoreFileName: ".aiexclude",
2129
+ generateContent: generateGeminiMemoryMarkdown,
2130
+ generateDetailContent: generateGeminiMemoryMarkdown,
2131
+ generateRootContent: generateGeminiRootMarkdown,
2132
+ rootFilePath: "GEMINI.md",
2133
+ detailSubDir: ".gemini/memories"
2134
+ };
2135
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
1363
2136
  }
1364
2137
  function generateGeminiMemoryMarkdown(rule) {
1365
2138
  return rule.content.trim();
@@ -1388,24 +2161,42 @@ function generateGeminiRootMarkdown(rootRule, memoryRules, _baseDir) {
1388
2161
  }
1389
2162
  return lines.join("\n");
1390
2163
  }
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");
2164
+
2165
+ // src/generators/rules/junie.ts
2166
+ async function generateJunieConfig(rules, config, baseDir) {
2167
+ const generatorConfig = {
2168
+ tool: "junie",
2169
+ fileExtension: ".md",
2170
+ ignoreFileName: ".aiignore",
2171
+ generateContent: (rule) => rule.content.trim(),
2172
+ generateRootContent: generateGuidelinesMarkdown,
2173
+ rootFilePath: ".junie/guidelines.md"
2174
+ };
2175
+ return generateComplexRules(rules, config, generatorConfig, baseDir);
2176
+ }
2177
+ function generateGuidelinesMarkdown(rootRule, detailRules) {
2178
+ const lines = [];
2179
+ if (rootRule) {
2180
+ lines.push(rootRule.content);
2181
+ lines.push("");
2182
+ }
2183
+ if (detailRules.length > 0) {
2184
+ for (const rule of detailRules) {
2185
+ lines.push(rule.content);
2186
+ lines.push("");
2187
+ }
2188
+ }
2189
+ return lines.join("\n").trim();
1399
2190
  }
1400
2191
 
1401
2192
  // src/generators/rules/kiro.ts
1402
- var import_node_path11 = require("path");
2193
+ var import_node_path10 = require("path");
1403
2194
  async function generateKiroConfig(rules, config, baseDir) {
1404
2195
  const outputs = [];
1405
2196
  for (const rule of rules) {
1406
2197
  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`);
2198
+ const outputDir = baseDir ? (0, import_node_path10.join)(baseDir, config.outputPaths.kiro) : config.outputPaths.kiro;
2199
+ const filepath = (0, import_node_path10.join)(outputDir, `${rule.filename}.md`);
1409
2200
  outputs.push({
1410
2201
  tool: "kiro",
1411
2202
  filepath,
@@ -1459,7 +2250,10 @@ async function generateConfigurations(rules, config, targetTools, baseDir) {
1459
2250
  function filterRulesForTool(rules, tool, config) {
1460
2251
  return rules.filter((rule) => {
1461
2252
  const targets = resolveTargets(rule.frontmatter.targets, config);
1462
- return targets.includes(tool);
2253
+ if (!targets.includes(tool)) {
2254
+ return false;
2255
+ }
2256
+ return isToolSpecificRule(rule, tool);
1463
2257
  });
1464
2258
  }
1465
2259
  async function generateForTool(tool, rules, config, baseDir) {
@@ -1490,6 +2284,11 @@ async function generateForTool(tool, rules, config, baseDir) {
1490
2284
  return generateRooConfig(rules, config, baseDir);
1491
2285
  case "geminicli":
1492
2286
  return generateGeminiConfig(rules, config, baseDir);
2287
+ case "junie": {
2288
+ const junieRulesOutputs = await generateJunieConfig(rules, config, baseDir);
2289
+ const junieIgnoreOutputs = await generateJunieIgnoreFiles(rules, config, baseDir);
2290
+ return [...junieRulesOutputs, ...junieIgnoreOutputs];
2291
+ }
1493
2292
  case "kiro": {
1494
2293
  const kiroRulesOutputs = await generateKiroConfig(rules, config, baseDir);
1495
2294
  const kiroIgnoreOutputs = await generateKiroIgnoreFiles(rules, config, baseDir);
@@ -1502,83 +2301,8 @@ async function generateForTool(tool, rules, config, baseDir) {
1502
2301
  }
1503
2302
 
1504
2303
  // src/core/parser.ts
1505
- var import_node_path12 = require("path");
2304
+ var import_node_path11 = require("path");
1506
2305
  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
2306
  async function parseRulesFromDirectory(aiRulesDir) {
1583
2307
  const ignorePatterns = await loadIgnorePatterns();
1584
2308
  const allRuleFiles = await findFiles(aiRulesDir, ".md");
@@ -1615,7 +2339,7 @@ async function parseRuleFile(filepath) {
1615
2339
  const parsed = (0, import_gray_matter.default)(content);
1616
2340
  try {
1617
2341
  const frontmatter = RuleFrontmatterSchema.parse(parsed.data);
1618
- const filename = (0, import_node_path12.basename)(filepath, ".md");
2342
+ const filename = (0, import_node_path11.basename)(filepath, ".md");
1619
2343
  return {
1620
2344
  frontmatter,
1621
2345
  content: parsed.content,
@@ -1680,7 +2404,7 @@ async function validateRule(rule) {
1680
2404
  }
1681
2405
 
1682
2406
  // src/core/mcp-generator.ts
1683
- var path3 = __toESM(require("path"), 1);
2407
+ var path4 = __toESM(require("path"), 1);
1684
2408
 
1685
2409
  // src/generators/mcp/index.ts
1686
2410
  init_augmentcode();
@@ -1689,14 +2413,15 @@ init_cline();
1689
2413
  init_copilot();
1690
2414
  init_cursor();
1691
2415
  init_geminicli();
2416
+ init_junie();
1692
2417
  init_kiro();
1693
2418
  init_roo();
1694
2419
 
1695
2420
  // src/core/mcp-parser.ts
1696
2421
  var fs = __toESM(require("fs"), 1);
1697
- var path2 = __toESM(require("path"), 1);
2422
+ var path3 = __toESM(require("path"), 1);
1698
2423
  function parseMcpConfig(projectRoot) {
1699
- const mcpPath = path2.join(projectRoot, ".rulesync", ".mcp.json");
2424
+ const mcpPath = path3.join(projectRoot, ".rulesync", ".mcp.json");
1700
2425
  if (!fs.existsSync(mcpPath)) {
1701
2426
  return null;
1702
2427
  }
@@ -1720,7 +2445,7 @@ function parseMcpConfig(projectRoot) {
1720
2445
  }
1721
2446
 
1722
2447
  // src/core/mcp-generator.ts
1723
- async function generateMcpConfigs(projectRoot, baseDir) {
2448
+ async function generateMcpConfigs(projectRoot, baseDir, targetTools) {
1724
2449
  const results = [];
1725
2450
  const targetRoot = baseDir || projectRoot;
1726
2451
  const config = parseMcpConfig(projectRoot);
@@ -1730,55 +2455,70 @@ async function generateMcpConfigs(projectRoot, baseDir) {
1730
2455
  const generators = [
1731
2456
  {
1732
2457
  tool: "augmentcode-project",
1733
- path: path3.join(targetRoot, ".mcp.json"),
2458
+ path: path4.join(targetRoot, ".mcp.json"),
1734
2459
  generate: () => generateAugmentcodeMcp(config)
1735
2460
  },
1736
2461
  {
1737
2462
  tool: "augmentcode-legacy-project",
1738
- path: path3.join(targetRoot, ".mcp.json"),
2463
+ path: path4.join(targetRoot, ".mcp.json"),
1739
2464
  generate: () => generateAugmentcodeMcp(config)
1740
2465
  },
1741
2466
  {
1742
2467
  tool: "claude-project",
1743
- path: path3.join(targetRoot, ".mcp.json"),
2468
+ path: path4.join(targetRoot, ".mcp.json"),
1744
2469
  generate: () => generateClaudeMcp(config)
1745
2470
  },
1746
2471
  {
1747
2472
  tool: "copilot-editor",
1748
- path: path3.join(targetRoot, ".vscode", "mcp.json"),
2473
+ path: path4.join(targetRoot, ".vscode", "mcp.json"),
1749
2474
  generate: () => generateCopilotMcp(config, "editor")
1750
2475
  },
1751
2476
  {
1752
2477
  tool: "cursor-project",
1753
- path: path3.join(targetRoot, ".cursor", "mcp.json"),
2478
+ path: path4.join(targetRoot, ".cursor", "mcp.json"),
1754
2479
  generate: () => generateCursorMcp(config)
1755
2480
  },
1756
2481
  {
1757
2482
  tool: "cline-project",
1758
- path: path3.join(targetRoot, ".cline", "mcp.json"),
2483
+ path: path4.join(targetRoot, ".cline", "mcp.json"),
1759
2484
  generate: () => generateClineMcp(config)
1760
2485
  },
1761
2486
  {
1762
2487
  tool: "gemini-project",
1763
- path: path3.join(targetRoot, ".gemini", "settings.json"),
2488
+ path: path4.join(targetRoot, ".gemini", "settings.json"),
1764
2489
  generate: () => generateGeminiCliMcp(config)
1765
2490
  },
2491
+ {
2492
+ tool: "junie-project",
2493
+ path: path4.join(targetRoot, ".junie", "mcp-config.json"),
2494
+ generate: () => generateJunieMcp(config)
2495
+ },
1766
2496
  {
1767
2497
  tool: "kiro-project",
1768
- path: path3.join(targetRoot, ".kiro", "mcp.json"),
2498
+ path: path4.join(targetRoot, ".kiro", "mcp.json"),
1769
2499
  generate: () => generateKiroMcp(config)
1770
2500
  },
1771
2501
  {
1772
2502
  tool: "roo-project",
1773
- path: path3.join(targetRoot, ".roo", "mcp.json"),
2503
+ path: path4.join(targetRoot, ".roo", "mcp.json"),
1774
2504
  generate: () => generateRooMcp(config)
1775
2505
  }
1776
2506
  ];
1777
- for (const generator of generators) {
2507
+ const filteredGenerators = targetTools ? generators.filter((g) => {
2508
+ const baseTool = g.tool.split("-")[0];
2509
+ if (!isToolTarget(baseTool)) {
2510
+ return false;
2511
+ }
2512
+ if (baseTool === "augmentcode") {
2513
+ return targetTools.includes("augmentcode") || targetTools.includes("augmentcode-legacy");
2514
+ }
2515
+ return targetTools.includes(baseTool);
2516
+ }) : generators;
2517
+ for (const generator of filteredGenerators) {
1778
2518
  try {
1779
2519
  const content = generator.generate();
1780
2520
  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")) {
2521
+ 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
2522
  if (!parsed.mcpServers || Object.keys(parsed.mcpServers).length === 0) {
1783
2523
  results.push({
1784
2524
  tool: generator.tool,
@@ -1818,15 +2558,58 @@ async function generateMcpConfigs(projectRoot, baseDir) {
1818
2558
 
1819
2559
  // src/cli/commands/generate.ts
1820
2560
  async function generateCommand(options = {}) {
1821
- const config = getDefaultConfig();
1822
- const baseDirs = options.baseDirs || [process.cwd()];
2561
+ const configLoaderOptions = {
2562
+ ...options.config !== void 0 && { configPath: options.config },
2563
+ ...options.noConfig !== void 0 && { noConfig: options.noConfig }
2564
+ };
2565
+ const configResult = await loadConfig(configLoaderOptions);
2566
+ const cliOptions = {
2567
+ ...options.tools !== void 0 && { tools: options.tools },
2568
+ ...options.verbose !== void 0 && { verbose: options.verbose },
2569
+ ...options.delete !== void 0 && { delete: options.delete },
2570
+ ...options.baseDirs !== void 0 && { baseDirs: options.baseDirs }
2571
+ };
2572
+ const config = mergeWithCliOptions(configResult.config, cliOptions);
2573
+ if (options.tools && options.tools.length > 0) {
2574
+ const configTargets = config.defaultTargets;
2575
+ const cliTools = options.tools;
2576
+ const cliToolsSet = new Set(cliTools);
2577
+ const configTargetsSet = new Set(configTargets);
2578
+ const notInConfig = cliTools.filter((tool) => !configTargetsSet.has(tool));
2579
+ const notInCli = configTargets.filter((tool) => !cliToolsSet.has(tool));
2580
+ if (notInConfig.length > 0 || notInCli.length > 0) {
2581
+ console.warn("\u26A0\uFE0F Warning: CLI tool selection differs from configuration!");
2582
+ console.warn(` Config targets: ${configTargets.join(", ")}`);
2583
+ console.warn(` CLI specified: ${cliTools.join(", ")}`);
2584
+ if (notInConfig.length > 0) {
2585
+ console.warn(` Tools specified but not in config: ${notInConfig.join(", ")}`);
2586
+ }
2587
+ if (notInCli.length > 0) {
2588
+ console.warn(` Tools in config but not specified: ${notInCli.join(", ")}`);
2589
+ }
2590
+ console.warn("\n The configuration file targets will be used.");
2591
+ console.warn(" To change targets, update your rulesync config file.");
2592
+ console.warn("");
2593
+ }
2594
+ }
2595
+ let baseDirs;
2596
+ if (config.baseDir) {
2597
+ baseDirs = Array.isArray(config.baseDir) ? config.baseDir : [config.baseDir];
2598
+ } else if (options.baseDirs) {
2599
+ baseDirs = options.baseDirs;
2600
+ } else {
2601
+ baseDirs = [process.cwd()];
2602
+ }
2603
+ if (config.verbose && configResult.filepath) {
2604
+ console.log(`Loaded configuration from: ${configResult.filepath}`);
2605
+ }
1823
2606
  console.log("Generating configuration files...");
1824
2607
  if (!await fileExists(config.aiRulesDir)) {
1825
2608
  console.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
1826
2609
  process.exit(1);
1827
2610
  }
1828
2611
  try {
1829
- if (options.verbose) {
2612
+ if (config.verbose) {
1830
2613
  console.log(`Parsing rules from ${config.aiRulesDir}...`);
1831
2614
  }
1832
2615
  const rules = await parseRulesFromDirectory(config.aiRulesDir);
@@ -1834,18 +2617,26 @@ async function generateCommand(options = {}) {
1834
2617
  console.warn("\u26A0\uFE0F No rules found in .rulesync directory");
1835
2618
  return;
1836
2619
  }
1837
- if (options.verbose) {
2620
+ if (config.verbose) {
1838
2621
  console.log(`Found ${rules.length} rule(s)`);
1839
2622
  console.log(`Base directories: ${baseDirs.join(", ")}`);
1840
2623
  }
1841
- if (options.delete) {
1842
- if (options.verbose) {
2624
+ if (config.delete) {
2625
+ if (config.verbose) {
1843
2626
  console.log("Deleting existing output directories...");
1844
2627
  }
1845
- const targetTools = options.tools || config.defaultTargets;
2628
+ const targetTools = config.defaultTargets;
1846
2629
  const deleteTasks = [];
1847
2630
  for (const tool of targetTools) {
1848
2631
  switch (tool) {
2632
+ case "augmentcode":
2633
+ deleteTasks.push(removeDirectory((0, import_node_path12.join)(".augment", "rules")));
2634
+ deleteTasks.push(removeDirectory((0, import_node_path12.join)(".augment", "ignore")));
2635
+ break;
2636
+ case "augmentcode-legacy":
2637
+ deleteTasks.push(removeClaudeGeneratedFiles());
2638
+ deleteTasks.push(removeDirectory((0, import_node_path12.join)(".augment", "ignore")));
2639
+ break;
1849
2640
  case "copilot":
1850
2641
  deleteTasks.push(removeDirectory(config.outputPaths.copilot));
1851
2642
  break;
@@ -1870,19 +2661,19 @@ async function generateCommand(options = {}) {
1870
2661
  }
1871
2662
  }
1872
2663
  await Promise.all(deleteTasks);
1873
- if (options.verbose) {
2664
+ if (config.verbose) {
1874
2665
  console.log("Deleted existing output directories");
1875
2666
  }
1876
2667
  }
1877
2668
  let totalOutputs = 0;
1878
2669
  for (const baseDir of baseDirs) {
1879
- if (options.verbose) {
2670
+ if (config.verbose) {
1880
2671
  console.log(`
1881
2672
  Generating configurations for base directory: ${baseDir}`);
1882
2673
  }
1883
- const outputs = await generateConfigurations(rules, config, options.tools, baseDir);
2674
+ const outputs = await generateConfigurations(rules, config, config.defaultTargets, baseDir);
1884
2675
  if (outputs.length === 0) {
1885
- if (options.verbose) {
2676
+ if (config.verbose) {
1886
2677
  console.warn(`\u26A0\uFE0F No configurations generated for ${baseDir}`);
1887
2678
  }
1888
2679
  continue;
@@ -1897,17 +2688,18 @@ Generating configurations for base directory: ${baseDir}`);
1897
2688
  console.warn("\u26A0\uFE0F No configurations generated");
1898
2689
  return;
1899
2690
  }
1900
- if (options.verbose) {
2691
+ if (config.verbose) {
1901
2692
  console.log("\nGenerating MCP configurations...");
1902
2693
  }
1903
2694
  let totalMcpOutputs = 0;
1904
2695
  for (const baseDir of baseDirs) {
1905
2696
  const mcpResults = await generateMcpConfigs(
1906
2697
  process.cwd(),
1907
- baseDir === process.cwd() ? void 0 : baseDir
2698
+ baseDir === process.cwd() ? void 0 : baseDir,
2699
+ config.defaultTargets
1908
2700
  );
1909
2701
  if (mcpResults.length === 0) {
1910
- if (options.verbose) {
2702
+ if (config.verbose) {
1911
2703
  console.log(`No MCP configuration found for ${baseDir}`);
1912
2704
  }
1913
2705
  continue;
@@ -1918,7 +2710,7 @@ Generating configurations for base directory: ${baseDir}`);
1918
2710
  totalMcpOutputs++;
1919
2711
  } else if (result.status === "error") {
1920
2712
  console.error(`\u274C Failed to generate ${result.tool} MCP configuration: ${result.error}`);
1921
- } else if (options.verbose && result.status === "skipped") {
2713
+ } else if (config.verbose && result.status === "skipped") {
1922
2714
  console.log(`\u23ED\uFE0F Skipped ${result.tool} MCP configuration (no servers configured)`);
1923
2715
  }
1924
2716
  }
@@ -1937,7 +2729,7 @@ Generating configurations for base directory: ${baseDir}`);
1937
2729
  }
1938
2730
 
1939
2731
  // src/cli/commands/gitignore.ts
1940
- var import_node_fs = require("fs");
2732
+ var import_node_fs2 = require("fs");
1941
2733
  var import_node_path13 = require("path");
1942
2734
  var gitignoreCommand = async () => {
1943
2735
  const gitignorePath = (0, import_node_path13.join)(process.cwd(), ".gitignore");
@@ -1962,6 +2754,8 @@ var gitignoreCommand = async () => {
1962
2754
  "**/.kiro/steering/",
1963
2755
  "**/.augment/rules/",
1964
2756
  "**/.augment-guidelines",
2757
+ "**/.junie/guidelines.md",
2758
+ "**/.noai",
1965
2759
  "**/.mcp.json",
1966
2760
  "!.rulesync/.mcp.json",
1967
2761
  "**/.cursor/mcp.json",
@@ -1971,8 +2765,8 @@ var gitignoreCommand = async () => {
1971
2765
  "**/.roo/mcp.json"
1972
2766
  ];
1973
2767
  let gitignoreContent = "";
1974
- if ((0, import_node_fs.existsSync)(gitignorePath)) {
1975
- gitignoreContent = (0, import_node_fs.readFileSync)(gitignorePath, "utf-8");
2768
+ if ((0, import_node_fs2.existsSync)(gitignorePath)) {
2769
+ gitignoreContent = (0, import_node_fs2.readFileSync)(gitignorePath, "utf-8");
1976
2770
  }
1977
2771
  const linesToAdd = [];
1978
2772
  for (const rule of rulesFilesToIgnore) {
@@ -1989,7 +2783,7 @@ var gitignoreCommand = async () => {
1989
2783
  ${linesToAdd.join("\n")}
1990
2784
  ` : `${linesToAdd.join("\n")}
1991
2785
  `;
1992
- (0, import_node_fs.writeFileSync)(gitignorePath, newContent);
2786
+ (0, import_node_fs2.writeFileSync)(gitignorePath, newContent);
1993
2787
  console.log(`\u2705 Added ${linesToAdd.length} rules to .gitignore:`);
1994
2788
  for (const line of linesToAdd) {
1995
2789
  if (!line.startsWith("#")) {
@@ -1999,7 +2793,7 @@ ${linesToAdd.join("\n")}
1999
2793
  };
2000
2794
 
2001
2795
  // src/core/importer.ts
2002
- var import_node_path18 = require("path");
2796
+ var import_node_path19 = require("path");
2003
2797
  var import_gray_matter5 = __toESM(require("gray-matter"), 1);
2004
2798
 
2005
2799
  // src/parsers/augmentcode.ts
@@ -2448,7 +3242,7 @@ async function parseCopilotConfiguration(baseDir = process.cwd()) {
2448
3242
  var import_node_path17 = require("path");
2449
3243
  var import_gray_matter4 = __toESM(require("gray-matter"), 1);
2450
3244
  var import_js_yaml = require("js-yaml");
2451
- var import_mini6 = require("zod/mini");
3245
+ var import_mini7 = require("zod/mini");
2452
3246
  var customMatterOptions = {
2453
3247
  engines: {
2454
3248
  yaml: {
@@ -2476,7 +3270,7 @@ var customMatterOptions = {
2476
3270
  }
2477
3271
  };
2478
3272
  function convertCursorMdcFrontmatter(cursorFrontmatter, _filename) {
2479
- const FrontmatterSchema = import_mini6.z.record(import_mini6.z.string(), import_mini6.z.unknown());
3273
+ const FrontmatterSchema = import_mini7.z.record(import_mini7.z.string(), import_mini7.z.unknown());
2480
3274
  const parseResult = FrontmatterSchema.safeParse(cursorFrontmatter);
2481
3275
  if (!parseResult.success) {
2482
3276
  return {
@@ -2688,6 +3482,42 @@ async function parseGeminiConfiguration(baseDir = process.cwd()) {
2688
3482
  });
2689
3483
  }
2690
3484
 
3485
+ // src/parsers/junie.ts
3486
+ var import_node_path18 = require("path");
3487
+ async function parseJunieConfiguration(baseDir = process.cwd()) {
3488
+ const errors = [];
3489
+ const rules = [];
3490
+ const guidelinesPath = (0, import_node_path18.join)(baseDir, ".junie", "guidelines.md");
3491
+ if (!await fileExists(guidelinesPath)) {
3492
+ errors.push(".junie/guidelines.md file not found");
3493
+ return { rules, errors };
3494
+ }
3495
+ try {
3496
+ const content = await readFileContent(guidelinesPath);
3497
+ if (content.trim()) {
3498
+ const frontmatter = {
3499
+ root: false,
3500
+ targets: ["junie"],
3501
+ description: "Junie project guidelines",
3502
+ globs: ["**/*"]
3503
+ };
3504
+ rules.push({
3505
+ frontmatter,
3506
+ content: content.trim(),
3507
+ filename: "junie-guidelines",
3508
+ filepath: guidelinesPath
3509
+ });
3510
+ }
3511
+ } catch (error) {
3512
+ const errorMessage = error instanceof Error ? error.message : String(error);
3513
+ errors.push(`Failed to parse .junie/guidelines.md: ${errorMessage}`);
3514
+ }
3515
+ if (rules.length === 0) {
3516
+ errors.push("No valid Junie configuration found");
3517
+ }
3518
+ return { rules, errors };
3519
+ }
3520
+
2691
3521
  // src/parsers/roo.ts
2692
3522
  async function parseRooConfiguration(baseDir = process.cwd()) {
2693
3523
  return parseConfigurationFiles(baseDir, {
@@ -2774,6 +3604,12 @@ async function importConfiguration(options) {
2774
3604
  mcpServers = geminiResult.mcpServers;
2775
3605
  break;
2776
3606
  }
3607
+ case "junie": {
3608
+ const junieResult = await parseJunieConfiguration(baseDir);
3609
+ rules = junieResult.rules;
3610
+ errors.push(...junieResult.errors);
3611
+ break;
3612
+ }
2777
3613
  default:
2778
3614
  errors.push(`Unsupported tool: ${tool}`);
2779
3615
  return { success: false, rulesCreated: 0, errors };
@@ -2786,7 +3622,7 @@ async function importConfiguration(options) {
2786
3622
  if (rules.length === 0 && !ignorePatterns && !mcpServers) {
2787
3623
  return { success: false, rulesCreated: 0, errors };
2788
3624
  }
2789
- const rulesDirPath = (0, import_node_path18.join)(baseDir, rulesDir);
3625
+ const rulesDirPath = (0, import_node_path19.join)(baseDir, rulesDir);
2790
3626
  try {
2791
3627
  const { mkdir: mkdir3 } = await import("fs/promises");
2792
3628
  await mkdir3(rulesDirPath, { recursive: true });
@@ -2798,9 +3634,9 @@ async function importConfiguration(options) {
2798
3634
  let rulesCreated = 0;
2799
3635
  for (const rule of rules) {
2800
3636
  try {
2801
- const baseFilename = `${tool}__${rule.filename}`;
3637
+ const baseFilename = rule.filename;
2802
3638
  const filename = await generateUniqueFilename(rulesDirPath, baseFilename);
2803
- const filePath = (0, import_node_path18.join)(rulesDirPath, `${filename}.md`);
3639
+ const filePath = (0, import_node_path19.join)(rulesDirPath, `${filename}.md`);
2804
3640
  const content = generateRuleFileContent(rule);
2805
3641
  await writeFileContent(filePath, content);
2806
3642
  rulesCreated++;
@@ -2815,7 +3651,7 @@ async function importConfiguration(options) {
2815
3651
  let ignoreFileCreated = false;
2816
3652
  if (ignorePatterns && ignorePatterns.length > 0) {
2817
3653
  try {
2818
- const rulesyncignorePath = (0, import_node_path18.join)(baseDir, ".rulesyncignore");
3654
+ const rulesyncignorePath = (0, import_node_path19.join)(baseDir, ".rulesyncignore");
2819
3655
  const ignoreContent = `${ignorePatterns.join("\n")}
2820
3656
  `;
2821
3657
  await writeFileContent(rulesyncignorePath, ignoreContent);
@@ -2831,7 +3667,7 @@ async function importConfiguration(options) {
2831
3667
  let mcpFileCreated = false;
2832
3668
  if (mcpServers && Object.keys(mcpServers).length > 0) {
2833
3669
  try {
2834
- const mcpPath = (0, import_node_path18.join)(baseDir, rulesDir, ".mcp.json");
3670
+ const mcpPath = (0, import_node_path19.join)(baseDir, rulesDir, ".mcp.json");
2835
3671
  const mcpContent = `${JSON.stringify({ mcpServers }, null, 2)}
2836
3672
  `;
2837
3673
  await writeFileContent(mcpPath, mcpContent);
@@ -2859,7 +3695,7 @@ function generateRuleFileContent(rule) {
2859
3695
  async function generateUniqueFilename(rulesDir, baseFilename) {
2860
3696
  let filename = baseFilename;
2861
3697
  let counter = 1;
2862
- while (await fileExists((0, import_node_path18.join)(rulesDir, `${filename}.md`))) {
3698
+ while (await fileExists((0, import_node_path19.join)(rulesDir, `${filename}.md`))) {
2863
3699
  filename = `${baseFilename}-${counter}`;
2864
3700
  counter++;
2865
3701
  }
@@ -2870,7 +3706,7 @@ async function generateUniqueFilename(rulesDir, baseFilename) {
2870
3706
  async function importCommand(options = {}) {
2871
3707
  const tools = [];
2872
3708
  if (options.augmentcode) tools.push("augmentcode");
2873
- if (options.augmentcodeLegacy) tools.push("augmentcode-legacy");
3709
+ if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
2874
3710
  if (options.claudecode) tools.push("claudecode");
2875
3711
  if (options.cursor) tools.push("cursor");
2876
3712
  if (options.copilot) tools.push("copilot");
@@ -2879,7 +3715,7 @@ async function importCommand(options = {}) {
2879
3715
  if (options.geminicli) tools.push("geminicli");
2880
3716
  if (tools.length === 0) {
2881
3717
  console.error(
2882
- "\u274C Please specify one tool to import from (--augmentcode, --augmentcodeLegacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
3718
+ "\u274C Please specify one tool to import from (--augmentcode, --augmentcode-legacy, --claudecode, --cursor, --copilot, --cline, --roo, --geminicli)"
2883
3719
  );
2884
3720
  process.exit(1);
2885
3721
  }
@@ -2926,7 +3762,7 @@ async function importCommand(options = {}) {
2926
3762
  }
2927
3763
 
2928
3764
  // src/cli/commands/init.ts
2929
- var import_node_path19 = require("path");
3765
+ var import_node_path20 = require("path");
2930
3766
  async function initCommand() {
2931
3767
  const aiRulesDir = ".rulesync";
2932
3768
  console.log("Initializing rulesync...");
@@ -2973,7 +3809,7 @@ globs: ["**/*"]
2973
3809
  - Follow single responsibility principle
2974
3810
  `
2975
3811
  };
2976
- const filepath = (0, import_node_path19.join)(aiRulesDir, sampleFile.filename);
3812
+ const filepath = (0, import_node_path20.join)(aiRulesDir, sampleFile.filename);
2977
3813
  if (!await fileExists(filepath)) {
2978
3814
  await writeFileContent(filepath, sampleFile.content);
2979
3815
  console.log(`Created ${filepath}`);
@@ -3087,11 +3923,11 @@ async function watchCommand() {
3087
3923
  persistent: true
3088
3924
  });
3089
3925
  let isGenerating = false;
3090
- const handleChange = async (path4) => {
3926
+ const handleChange = async (path5) => {
3091
3927
  if (isGenerating) return;
3092
3928
  isGenerating = true;
3093
3929
  console.log(`
3094
- \u{1F4DD} Detected change in ${path4}`);
3930
+ \u{1F4DD} Detected change in ${path5}`);
3095
3931
  try {
3096
3932
  await generateCommand({ verbose: false });
3097
3933
  console.log("\u2705 Regenerated configuration files");
@@ -3101,10 +3937,10 @@ async function watchCommand() {
3101
3937
  isGenerating = false;
3102
3938
  }
3103
3939
  };
3104
- watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path4) => {
3940
+ watcher.on("change", handleChange).on("add", handleChange).on("unlink", (path5) => {
3105
3941
  console.log(`
3106
- \u{1F5D1}\uFE0F Removed ${path4}`);
3107
- handleChange(path4);
3942
+ \u{1F5D1}\uFE0F Removed ${path5}`);
3943
+ handleChange(path5);
3108
3944
  }).on("error", (error) => {
3109
3945
  console.error("\u274C Watcher error:", error);
3110
3946
  });
@@ -3117,28 +3953,31 @@ async function watchCommand() {
3117
3953
 
3118
3954
  // src/cli/index.ts
3119
3955
  var program = new import_commander.Command();
3120
- program.name("rulesync").description("Unified AI rules management CLI tool").version("0.49.0");
3956
+ program.name("rulesync").description("Unified AI rules management CLI tool").version("0.52.0");
3121
3957
  program.command("init").description("Initialize rulesync in current directory").action(initCommand);
3122
3958
  program.command("add <filename>").description("Add a new rule file").action(addCommand);
3123
3959
  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(
3960
+ 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);
3961
+ 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
3962
  "-b, --base-dir <paths>",
3127
3963
  "Base directories to generate files (comma-separated for multiple paths)"
3128
- ).option("-v, --verbose", "Verbose output").action(async (options) => {
3964
+ ).option("-v, --verbose", "Verbose output").option("-c, --config <path>", "Path to configuration file").option("--no-config", "Disable configuration file loading").action(async (options) => {
3129
3965
  const tools = [];
3130
3966
  if (options.augmentcode) tools.push("augmentcode");
3131
- if (options.augmentcodeLegacy) tools.push("augmentcode-legacy");
3967
+ if (options["augmentcode-legacy"]) tools.push("augmentcode-legacy");
3132
3968
  if (options.copilot) tools.push("copilot");
3133
3969
  if (options.cursor) tools.push("cursor");
3134
3970
  if (options.cline) tools.push("cline");
3135
3971
  if (options.claudecode) tools.push("claudecode");
3136
3972
  if (options.roo) tools.push("roo");
3137
3973
  if (options.geminicli) tools.push("geminicli");
3974
+ if (options.junie) tools.push("junie");
3138
3975
  if (options.kiro) tools.push("kiro");
3139
3976
  const generateOptions = {
3140
3977
  verbose: options.verbose,
3141
- delete: options.delete
3978
+ delete: options.delete,
3979
+ config: options.config,
3980
+ noConfig: options.noConfig
3142
3981
  };
3143
3982
  if (tools.length > 0) {
3144
3983
  generateOptions.tools = tools;
@@ -3151,4 +3990,5 @@ program.command("generate").description("Generate configuration files for AI too
3151
3990
  program.command("validate").description("Validate rulesync configuration").action(validateCommand);
3152
3991
  program.command("status").description("Show current status of rulesync").action(statusCommand);
3153
3992
  program.command("watch").description("Watch for changes and auto-generate configurations").action(watchCommand);
3993
+ 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
3994
  program.parse();