rulesync 7.0.0 → 7.2.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.
Files changed (4) hide show
  1. package/README.md +155 -2
  2. package/dist/index.cjs +1280 -671
  3. package/dist/index.js +1276 -667
  4. package/package.json +14 -12
package/dist/index.js CHANGED
@@ -106,7 +106,7 @@ var logger = new Logger();
106
106
 
107
107
  // src/lib/fetch.ts
108
108
  import { Semaphore } from "es-toolkit/promise";
109
- import { join as join107 } from "path";
109
+ import { join as join108 } from "path";
110
110
 
111
111
  // src/constants/rulesync-paths.ts
112
112
  import { join } from "path";
@@ -123,9 +123,15 @@ var RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join(RULESYNC_RELATIVE_DIR_PATH, ".ai
123
123
  var RULESYNC_IGNORE_RELATIVE_FILE_PATH = ".rulesyncignore";
124
124
  var RULESYNC_OVERVIEW_FILE_NAME = "overview.md";
125
125
  var RULESYNC_SKILLS_RELATIVE_DIR_PATH = join(RULESYNC_RELATIVE_DIR_PATH, "skills");
126
+ var RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join(
127
+ RULESYNC_SKILLS_RELATIVE_DIR_PATH,
128
+ ".curated"
129
+ );
130
+ var RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync.lock";
126
131
  var RULESYNC_MCP_FILE_NAME = "mcp.json";
127
132
  var RULESYNC_HOOKS_FILE_NAME = "hooks.json";
128
133
  var MAX_FILE_SIZE = 10 * 1024 * 1024;
134
+ var FETCH_CONCURRENCY_LIMIT = 10;
129
135
 
130
136
  // src/features/commands/commands-processor.ts
131
137
  import { basename as basename16, join as join18 } from "path";
@@ -298,10 +304,11 @@ var FeatureProcessor = class {
298
304
  }
299
305
  /**
300
306
  * Once converted to rulesync/tool files, write them to the filesystem.
301
- * Returns the number of files written.
307
+ * Returns the count and paths of files written.
302
308
  */
303
309
  async writeAiFiles(aiFiles) {
304
310
  let changedCount = 0;
311
+ const changedPaths = [];
305
312
  for (const aiFile of aiFiles) {
306
313
  const filePath = aiFile.getFilePath();
307
314
  const contentWithNewline = addTrailingNewline(aiFile.getFileContent());
@@ -315,8 +322,9 @@ var FeatureProcessor = class {
315
322
  await writeFileContent(filePath, contentWithNewline);
316
323
  }
317
324
  changedCount++;
325
+ changedPaths.push(aiFile.getRelativePathFromCwd());
318
326
  }
319
- return changedCount;
327
+ return { count: changedCount, paths: changedPaths };
320
328
  }
321
329
  async removeAiFiles(aiFiles) {
322
330
  for (const aiFile of aiFiles) {
@@ -1656,7 +1664,7 @@ var GeminiCliCommand = class _GeminiCliCommand extends ToolCommand {
1656
1664
  const result = GeminiCliCommandFrontmatterSchema.safeParse(parsed);
1657
1665
  if (!result.success) {
1658
1666
  throw new Error(
1659
- `Invalid frontmatter in Gemini CLI command file: ${formatError(result.error)}`
1667
+ `Invalid frontmatter in ${join13(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
1660
1668
  );
1661
1669
  }
1662
1670
  return {
@@ -1664,7 +1672,10 @@ var GeminiCliCommand = class _GeminiCliCommand extends ToolCommand {
1664
1672
  description: result.data.description || ""
1665
1673
  };
1666
1674
  } catch (error) {
1667
- throw new Error(`Failed to parse TOML command file: ${error}`, { cause: error });
1675
+ throw new Error(
1676
+ `Failed to parse TOML command file (${join13(this.relativeDirPath, this.relativeFilePath)}): ${error}`,
1677
+ { cause: error }
1678
+ );
1668
1679
  }
1669
1680
  }
1670
1681
  getBody() {
@@ -2419,7 +2430,7 @@ var CommandsProcessor = class extends FeatureProcessor {
2419
2430
  (path4) => RulesyncCommand.fromFile({ relativeFilePath: basename16(path4) })
2420
2431
  )
2421
2432
  );
2422
- logger.info(`Successfully loaded ${rulesyncCommands.length} rulesync commands`);
2433
+ logger.debug(`Successfully loaded ${rulesyncCommands.length} rulesync commands`);
2423
2434
  return rulesyncCommands;
2424
2435
  }
2425
2436
  /**
@@ -2443,7 +2454,7 @@ var CommandsProcessor = class extends FeatureProcessor {
2443
2454
  global: this.global
2444
2455
  })
2445
2456
  ).filter((cmd) => cmd.isDeletable());
2446
- logger.info(`Successfully loaded ${toolCommands2.length} ${paths.relativeDirPath} commands`);
2457
+ logger.debug(`Successfully loaded ${toolCommands2.length} ${paths.relativeDirPath} commands`);
2447
2458
  return toolCommands2;
2448
2459
  }
2449
2460
  const toolCommands = await Promise.all(
@@ -2455,7 +2466,7 @@ var CommandsProcessor = class extends FeatureProcessor {
2455
2466
  })
2456
2467
  )
2457
2468
  );
2458
- logger.info(`Successfully loaded ${toolCommands.length} ${paths.relativeDirPath} commands`);
2469
+ logger.debug(`Successfully loaded ${toolCommands.length} ${paths.relativeDirPath} commands`);
2459
2470
  return toolCommands;
2460
2471
  }
2461
2472
  /**
@@ -2792,10 +2803,7 @@ var ClaudecodeHooks = class _ClaudecodeHooks extends ToolHooks {
2792
2803
  }) {
2793
2804
  const paths = _ClaudecodeHooks.getSettablePaths({ global });
2794
2805
  const filePath = join20(baseDir, paths.relativeDirPath, paths.relativeFilePath);
2795
- const fileContent = await readOrInitializeFileContent(
2796
- filePath,
2797
- JSON.stringify({ hooks: {} }, null, 2)
2798
- );
2806
+ const fileContent = await readFileContentOrNull(filePath) ?? '{"hooks":{}}';
2799
2807
  return new _ClaudecodeHooks({
2800
2808
  baseDir,
2801
2809
  relativeDirPath: paths.relativeDirPath,
@@ -2842,9 +2850,12 @@ var ClaudecodeHooks = class _ClaudecodeHooks extends ToolHooks {
2842
2850
  try {
2843
2851
  settings = JSON.parse(this.getFileContent());
2844
2852
  } catch (error) {
2845
- throw new Error(`Failed to parse Claude hooks content: ${formatError(error)}`, {
2846
- cause: error
2847
- });
2853
+ throw new Error(
2854
+ `Failed to parse Claude hooks content in ${join20(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`,
2855
+ {
2856
+ cause: error
2857
+ }
2858
+ );
2848
2859
  }
2849
2860
  const hooks = claudeHooksToCanonical(settings.hooks);
2850
2861
  return this.toRulesyncHooksDefault({
@@ -3067,10 +3078,7 @@ var FactorydroidHooks = class _FactorydroidHooks extends ToolHooks {
3067
3078
  }) {
3068
3079
  const paths = _FactorydroidHooks.getSettablePaths({ global });
3069
3080
  const filePath = join22(baseDir, paths.relativeDirPath, paths.relativeFilePath);
3070
- const fileContent = await readOrInitializeFileContent(
3071
- filePath,
3072
- JSON.stringify({ hooks: {} }, null, 2)
3073
- );
3081
+ const fileContent = await readFileContentOrNull(filePath) ?? '{"hooks":{}}';
3074
3082
  return new _FactorydroidHooks({
3075
3083
  baseDir,
3076
3084
  relativeDirPath: paths.relativeDirPath,
@@ -3117,9 +3125,12 @@ var FactorydroidHooks = class _FactorydroidHooks extends ToolHooks {
3117
3125
  try {
3118
3126
  settings = JSON.parse(this.getFileContent());
3119
3127
  } catch (error) {
3120
- throw new Error(`Failed to parse Factory Droid hooks content: ${formatError(error)}`, {
3121
- cause: error
3122
- });
3128
+ throw new Error(
3129
+ `Failed to parse Factory Droid hooks content in ${join22(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`,
3130
+ {
3131
+ cause: error
3132
+ }
3133
+ );
3123
3134
  }
3124
3135
  const hooks = factorydroidHooksToCanonical(settings.hooks);
3125
3136
  return this.toRulesyncHooksDefault({
@@ -3372,7 +3383,9 @@ var HooksProcessor = class extends FeatureProcessor {
3372
3383
  })
3373
3384
  ];
3374
3385
  } catch (error) {
3375
- logger.error(`Failed to load Rulesync hooks file: ${formatError(error)}`);
3386
+ logger.error(
3387
+ `Failed to load Rulesync hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`
3388
+ );
3376
3389
  return [];
3377
3390
  }
3378
3391
  }
@@ -3389,7 +3402,7 @@ var HooksProcessor = class extends FeatureProcessor {
3389
3402
  global: this.global
3390
3403
  });
3391
3404
  const list = toolHooks2.isDeletable?.() !== false ? [toolHooks2] : [];
3392
- logger.info(
3405
+ logger.debug(
3393
3406
  `Successfully loaded ${list.length} ${this.toolTarget} hooks files for deletion`
3394
3407
  );
3395
3408
  return list;
@@ -3399,7 +3412,7 @@ var HooksProcessor = class extends FeatureProcessor {
3399
3412
  validate: true,
3400
3413
  global: this.global
3401
3414
  });
3402
- logger.info(`Successfully loaded 1 ${this.toolTarget} hooks file`);
3415
+ logger.debug(`Successfully loaded 1 ${this.toolTarget} hooks file`);
3403
3416
  return [toolHooks];
3404
3417
  } catch (error) {
3405
3418
  const msg = `Failed to load hooks files for tool target: ${this.toolTarget}: ${formatError(error)}`;
@@ -4463,7 +4476,9 @@ var IgnoreProcessor = class extends FeatureProcessor {
4463
4476
  try {
4464
4477
  return [await RulesyncIgnore.fromFile()];
4465
4478
  } catch (error) {
4466
- logger.error(`Failed to load rulesync files: ${formatError(error)}`);
4479
+ logger.error(
4480
+ `Failed to load rulesync ignore file (${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}): ${formatError(error)}`
4481
+ );
4467
4482
  return [];
4468
4483
  }
4469
4484
  }
@@ -4489,7 +4504,7 @@ var IgnoreProcessor = class extends FeatureProcessor {
4489
4504
  const toolIgnores = await this.loadToolIgnores();
4490
4505
  return toolIgnores;
4491
4506
  } catch (error) {
4492
- const errorMessage = `Failed to load tool files: ${formatError(error)}`;
4507
+ const errorMessage = `Failed to load tool files for ${this.toolTarget}: ${formatError(error)}`;
4493
4508
  if (error instanceof Error && error.message.includes("no such file or directory")) {
4494
4509
  logger.debug(errorMessage);
4495
4510
  } else {
@@ -4754,10 +4769,7 @@ var ClaudecodeMcp = class _ClaudecodeMcp extends ToolMcp {
4754
4769
  global = false
4755
4770
  }) {
4756
4771
  const paths = this.getSettablePaths({ global });
4757
- const fileContent = await readOrInitializeFileContent(
4758
- join38(baseDir, paths.relativeDirPath, paths.relativeFilePath),
4759
- JSON.stringify({ mcpServers: {} }, null, 2)
4760
- );
4772
+ const fileContent = await readFileContentOrNull(join38(baseDir, paths.relativeDirPath, paths.relativeFilePath)) ?? '{"mcpServers":{}}';
4761
4773
  const json = JSON.parse(fileContent);
4762
4774
  const newJson = { ...json, mcpServers: json.mcpServers ?? {} };
4763
4775
  return new _ClaudecodeMcp({
@@ -5310,10 +5322,7 @@ var GeminiCliMcp = class _GeminiCliMcp extends ToolMcp {
5310
5322
  global = false
5311
5323
  }) {
5312
5324
  const paths = this.getSettablePaths({ global });
5313
- const fileContent = await readOrInitializeFileContent(
5314
- join44(baseDir, paths.relativeDirPath, paths.relativeFilePath),
5315
- JSON.stringify({ mcpServers: {} }, null, 2)
5316
- );
5325
+ const fileContent = await readFileContentOrNull(join44(baseDir, paths.relativeDirPath, paths.relativeFilePath)) ?? '{"mcpServers":{}}';
5317
5326
  const json = JSON.parse(fileContent);
5318
5327
  const newJson = { ...json, mcpServers: json.mcpServers ?? {} };
5319
5328
  return new _GeminiCliMcp({
@@ -5468,10 +5477,7 @@ var KiloMcp = class _KiloMcp extends ToolMcp {
5468
5477
  validate = true
5469
5478
  }) {
5470
5479
  const paths = this.getSettablePaths();
5471
- const fileContent = await readOrInitializeFileContent(
5472
- join46(baseDir, paths.relativeDirPath, paths.relativeFilePath),
5473
- JSON.stringify({ mcpServers: {} }, null, 2)
5474
- );
5480
+ const fileContent = await readFileContentOrNull(join46(baseDir, paths.relativeDirPath, paths.relativeFilePath)) ?? '{"mcpServers":{}}';
5475
5481
  return new _KiloMcp({
5476
5482
  baseDir,
5477
5483
  relativeDirPath: paths.relativeDirPath,
@@ -5540,10 +5546,7 @@ var KiroMcp = class _KiroMcp extends ToolMcp {
5540
5546
  validate = true
5541
5547
  }) {
5542
5548
  const paths = this.getSettablePaths();
5543
- const fileContent = await readOrInitializeFileContent(
5544
- join47(baseDir, paths.relativeDirPath, paths.relativeFilePath),
5545
- JSON.stringify({ mcpServers: {} }, null, 2)
5546
- );
5549
+ const fileContent = await readFileContentOrNull(join47(baseDir, paths.relativeDirPath, paths.relativeFilePath)) ?? '{"mcpServers":{}}';
5547
5550
  return new _KiroMcp({
5548
5551
  baseDir,
5549
5552
  relativeDirPath: paths.relativeDirPath,
@@ -5714,10 +5717,7 @@ var OpencodeMcp = class _OpencodeMcp extends ToolMcp {
5714
5717
  global = false
5715
5718
  }) {
5716
5719
  const paths = this.getSettablePaths({ global });
5717
- const fileContent = await readOrInitializeFileContent(
5718
- join48(baseDir, paths.relativeDirPath, paths.relativeFilePath),
5719
- JSON.stringify({ mcp: {} }, null, 2)
5720
- );
5720
+ const fileContent = await readFileContentOrNull(join48(baseDir, paths.relativeDirPath, paths.relativeFilePath)) ?? '{"mcp":{}}';
5721
5721
  const json = JSON.parse(fileContent);
5722
5722
  const newJson = { ...json, mcp: json.mcp ?? {} };
5723
5723
  return new _OpencodeMcp({
@@ -6045,7 +6045,9 @@ var McpProcessor = class extends FeatureProcessor {
6045
6045
  try {
6046
6046
  return [await RulesyncMcp.fromFile({})];
6047
6047
  } catch (error) {
6048
- logger.error(`Failed to load a Rulesync MCP file: ${formatError(error)}`);
6048
+ logger.error(
6049
+ `Failed to load a Rulesync MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`
6050
+ );
6049
6051
  return [];
6050
6052
  }
6051
6053
  }
@@ -6067,7 +6069,7 @@ var McpProcessor = class extends FeatureProcessor {
6067
6069
  global: this.global
6068
6070
  });
6069
6071
  const toolMcps2 = toolMcp.isDeletable() ? [toolMcp] : [];
6070
- logger.info(`Successfully loaded ${toolMcps2.length} ${this.toolTarget} MCP files`);
6072
+ logger.debug(`Successfully loaded ${toolMcps2.length} ${this.toolTarget} MCP files`);
6071
6073
  return toolMcps2;
6072
6074
  }
6073
6075
  const toolMcps = [
@@ -6077,7 +6079,7 @@ var McpProcessor = class extends FeatureProcessor {
6077
6079
  global: this.global
6078
6080
  })
6079
6081
  ];
6080
- logger.info(`Successfully loaded ${toolMcps.length} ${this.toolTarget} MCP files`);
6082
+ logger.debug(`Successfully loaded ${toolMcps.length} ${this.toolTarget} MCP files`);
6081
6083
  return toolMcps;
6082
6084
  } catch (error) {
6083
6085
  const errorMessage = `Failed to load MCP files for tool target: ${this.toolTarget}: ${formatError(error)}`;
@@ -6137,7 +6139,7 @@ var McpProcessor = class extends FeatureProcessor {
6137
6139
 
6138
6140
  // src/features/rules/rules-processor.ts
6139
6141
  import { encode } from "@toon-format/toon";
6140
- import { basename as basename23, join as join106, relative as relative4 } from "path";
6142
+ import { basename as basename24, join as join107, relative as relative4 } from "path";
6141
6143
  import { z as z48 } from "zod/mini";
6142
6144
 
6143
6145
  // src/constants/general.ts
@@ -6600,7 +6602,7 @@ var FactorydroidSkill = class _FactorydroidSkill extends SimulatedSkill {
6600
6602
  };
6601
6603
 
6602
6604
  // src/features/skills/skills-processor.ts
6603
- import { basename as basename18, join as join69 } from "path";
6605
+ import { basename as basename19, join as join70 } from "path";
6604
6606
  import { z as z33 } from "zod/mini";
6605
6607
 
6606
6608
  // src/types/dir-feature-processor.ts
@@ -6628,6 +6630,7 @@ var DirFeatureProcessor = class {
6628
6630
  */
6629
6631
  async writeAiDirs(aiDirs) {
6630
6632
  let changedCount = 0;
6633
+ const changedPaths = [];
6631
6634
  for (const aiDir of aiDirs) {
6632
6635
  const dirPath = aiDir.getDirPath();
6633
6636
  let dirHasChanges = false;
@@ -6658,19 +6661,23 @@ var DirFeatureProcessor = class {
6658
6661
  if (!dirHasChanges) {
6659
6662
  continue;
6660
6663
  }
6664
+ const relativeDir = aiDir.getRelativePathFromCwd();
6661
6665
  if (this.dryRun) {
6662
6666
  logger.info(`[DRY RUN] Would create directory: ${dirPath}`);
6663
6667
  if (mainFile) {
6664
6668
  logger.info(`[DRY RUN] Would write: ${join55(dirPath, mainFile.name)}`);
6669
+ changedPaths.push(join55(relativeDir, mainFile.name));
6665
6670
  }
6666
6671
  for (const file of otherFiles) {
6667
6672
  logger.info(`[DRY RUN] Would write: ${join55(dirPath, file.relativeFilePathToDirPath)}`);
6673
+ changedPaths.push(join55(relativeDir, file.relativeFilePathToDirPath));
6668
6674
  }
6669
6675
  } else {
6670
6676
  await ensureDir(dirPath);
6671
6677
  if (mainFile && mainFileContent) {
6672
6678
  const mainFilePath = join55(dirPath, mainFile.name);
6673
6679
  await writeFileContent(mainFilePath, mainFileContent);
6680
+ changedPaths.push(join55(relativeDir, mainFile.name));
6674
6681
  }
6675
6682
  for (const [i, file] of otherFiles.entries()) {
6676
6683
  const filePath = join55(dirPath, file.relativeFilePathToDirPath);
@@ -6681,11 +6688,12 @@ var DirFeatureProcessor = class {
6681
6688
  );
6682
6689
  }
6683
6690
  await writeFileContent(filePath, content);
6691
+ changedPaths.push(join55(relativeDir, file.relativeFilePathToDirPath));
6684
6692
  }
6685
6693
  }
6686
6694
  changedCount++;
6687
6695
  }
6688
- return changedCount;
6696
+ return { count: changedCount, paths: changedPaths };
6689
6697
  }
6690
6698
  async removeAiDirs(aiDirs) {
6691
6699
  for (const aiDir of aiDirs) {
@@ -6782,7 +6790,7 @@ var RulesyncSkill = class _RulesyncSkill extends AiDir {
6782
6790
  }
6783
6791
  getFrontmatter() {
6784
6792
  if (!this.mainFile?.frontmatter) {
6785
- throw new Error("Frontmatter is not defined");
6793
+ throw new Error(`Frontmatter is not defined in ${join56(this.relativeDirPath, this.dirName)}`);
6786
6794
  }
6787
6795
  const result = RulesyncSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
6788
6796
  return result;
@@ -6883,7 +6891,7 @@ var AgentsSkillsSkill = class _AgentsSkillsSkill extends ToolSkill {
6883
6891
  }
6884
6892
  getFrontmatter() {
6885
6893
  if (!this.mainFile?.frontmatter) {
6886
- throw new Error("Frontmatter is not defined");
6894
+ throw new Error(`Frontmatter is not defined in ${join57(this.relativeDirPath, this.dirName)}`);
6887
6895
  }
6888
6896
  const result = AgentsSkillsSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
6889
6897
  return result;
@@ -7047,7 +7055,7 @@ var AntigravitySkill = class _AntigravitySkill extends ToolSkill {
7047
7055
  }
7048
7056
  getFrontmatter() {
7049
7057
  if (!this.mainFile?.frontmatter) {
7050
- throw new Error("Frontmatter is not defined");
7058
+ throw new Error(`Frontmatter is not defined in ${join58(this.relativeDirPath, this.dirName)}`);
7051
7059
  }
7052
7060
  const result = AntigravitySkillFrontmatterSchema.parse(this.mainFile.frontmatter);
7053
7061
  return result;
@@ -7206,7 +7214,7 @@ var ClaudecodeSkill = class _ClaudecodeSkill extends ToolSkill {
7206
7214
  }
7207
7215
  getFrontmatter() {
7208
7216
  if (!this.mainFile?.frontmatter) {
7209
- throw new Error("Frontmatter is not defined");
7217
+ throw new Error(`Frontmatter is not defined in ${join59(this.relativeDirPath, this.dirName)}`);
7210
7218
  }
7211
7219
  const result = ClaudecodeSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
7212
7220
  return result;
@@ -7375,7 +7383,7 @@ var CodexCliSkill = class _CodexCliSkill extends ToolSkill {
7375
7383
  }
7376
7384
  getFrontmatter() {
7377
7385
  if (!this.mainFile?.frontmatter) {
7378
- throw new Error("Frontmatter is not defined");
7386
+ throw new Error(`Frontmatter is not defined in ${join60(this.relativeDirPath, this.dirName)}`);
7379
7387
  }
7380
7388
  const result = CodexCliSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
7381
7389
  return result;
@@ -7545,7 +7553,7 @@ var CopilotSkill = class _CopilotSkill extends ToolSkill {
7545
7553
  }
7546
7554
  getFrontmatter() {
7547
7555
  if (!this.mainFile?.frontmatter) {
7548
- throw new Error("Frontmatter is not defined");
7556
+ throw new Error(`Frontmatter is not defined in ${join61(this.relativeDirPath, this.dirName)}`);
7549
7557
  }
7550
7558
  const result = CopilotSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
7551
7559
  return result;
@@ -7708,7 +7716,7 @@ var CursorSkill = class _CursorSkill extends ToolSkill {
7708
7716
  }
7709
7717
  getFrontmatter() {
7710
7718
  if (!this.mainFile?.frontmatter) {
7711
- throw new Error("Frontmatter is not defined");
7719
+ throw new Error(`Frontmatter is not defined in ${join62(this.relativeDirPath, this.dirName)}`);
7712
7720
  }
7713
7721
  const result = CursorSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
7714
7722
  return result;
@@ -7867,7 +7875,7 @@ var GeminiCliSkill = class _GeminiCliSkill extends ToolSkill {
7867
7875
  }
7868
7876
  getFrontmatter() {
7869
7877
  if (!this.mainFile?.frontmatter) {
7870
- throw new Error("Frontmatter is not defined");
7878
+ throw new Error(`Frontmatter is not defined in ${join63(this.relativeDirPath, this.dirName)}`);
7871
7879
  }
7872
7880
  const result = GeminiCliSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
7873
7881
  return result;
@@ -8026,7 +8034,7 @@ var KiloSkill = class _KiloSkill extends ToolSkill {
8026
8034
  }
8027
8035
  getFrontmatter() {
8028
8036
  if (!this.mainFile?.frontmatter) {
8029
- throw new Error("Frontmatter is not defined");
8037
+ throw new Error(`Frontmatter is not defined in ${join64(this.relativeDirPath, this.dirName)}`);
8030
8038
  }
8031
8039
  const result = KiloSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
8032
8040
  return result;
@@ -8204,7 +8212,7 @@ var KiroSkill = class _KiroSkill extends ToolSkill {
8204
8212
  }
8205
8213
  getFrontmatter() {
8206
8214
  if (!this.mainFile?.frontmatter) {
8207
- throw new Error("Frontmatter is not defined");
8215
+ throw new Error(`Frontmatter is not defined in ${join65(this.relativeDirPath, this.dirName)}`);
8208
8216
  }
8209
8217
  const result = KiroSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
8210
8218
  return result;
@@ -8381,7 +8389,7 @@ var OpenCodeSkill = class _OpenCodeSkill extends ToolSkill {
8381
8389
  }
8382
8390
  getFrontmatter() {
8383
8391
  if (!this.mainFile?.frontmatter) {
8384
- throw new Error("Frontmatter is not defined");
8392
+ throw new Error(`Frontmatter is not defined in ${join66(this.relativeDirPath, this.dirName)}`);
8385
8393
  }
8386
8394
  const result = OpenCodeSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
8387
8395
  return result;
@@ -8546,7 +8554,7 @@ var ReplitSkill = class _ReplitSkill extends ToolSkill {
8546
8554
  }
8547
8555
  getFrontmatter() {
8548
8556
  if (!this.mainFile?.frontmatter) {
8549
- throw new Error("Frontmatter is not defined");
8557
+ throw new Error(`Frontmatter is not defined in ${join67(this.relativeDirPath, this.dirName)}`);
8550
8558
  }
8551
8559
  const result = ReplitSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
8552
8560
  return result;
@@ -8705,7 +8713,7 @@ var RooSkill = class _RooSkill extends ToolSkill {
8705
8713
  }
8706
8714
  getFrontmatter() {
8707
8715
  if (!this.mainFile?.frontmatter) {
8708
- throw new Error("Frontmatter is not defined");
8716
+ throw new Error(`Frontmatter is not defined in ${join68(this.relativeDirPath, this.dirName)}`);
8709
8717
  }
8710
8718
  const result = RooSkillFrontmatterSchema.parse(this.mainFile.frontmatter);
8711
8719
  return result;
@@ -8836,6 +8844,23 @@ var RooSkill = class _RooSkill extends ToolSkill {
8836
8844
  }
8837
8845
  };
8838
8846
 
8847
+ // src/features/skills/skills-utils.ts
8848
+ import { basename as basename18, join as join69 } from "path";
8849
+ async function getLocalSkillDirNames(baseDir) {
8850
+ const skillsDir = join69(baseDir, RULESYNC_SKILLS_RELATIVE_DIR_PATH);
8851
+ const names = /* @__PURE__ */ new Set();
8852
+ if (!await directoryExists(skillsDir)) {
8853
+ return names;
8854
+ }
8855
+ const dirPaths = await findFilesByGlobs(join69(skillsDir, "*"), { type: "dir" });
8856
+ for (const dirPath of dirPaths) {
8857
+ const name = basename18(dirPath);
8858
+ if (name === basename18(RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH)) continue;
8859
+ names.add(name);
8860
+ }
8861
+ return names;
8862
+ }
8863
+
8839
8864
  // src/features/skills/skills-processor.ts
8840
8865
  var skillsProcessorToolTargetTuple = [
8841
8866
  "agentsmd",
@@ -9037,19 +9062,46 @@ var SkillsProcessor = class extends DirFeatureProcessor {
9037
9062
  /**
9038
9063
  * Implementation of abstract method from DirFeatureProcessor
9039
9064
  * Load and parse rulesync skill directories from .rulesync/skills/ directory
9065
+ * and also from .rulesync/skills/.curated/ for remote skills.
9066
+ * Local skills take precedence over curated skills with the same name.
9040
9067
  */
9041
9068
  async loadRulesyncDirs() {
9042
- const paths = RulesyncSkill.getSettablePaths();
9043
- const rulesyncSkillsDirPath = join69(this.baseDir, paths.relativeDirPath);
9044
- const dirPaths = await findFilesByGlobs(join69(rulesyncSkillsDirPath, "*"), { type: "dir" });
9045
- const dirNames = dirPaths.map((path4) => basename18(path4));
9046
- const rulesyncSkills = await Promise.all(
9047
- dirNames.map(
9069
+ const localDirNames = [...await getLocalSkillDirNames(this.baseDir)];
9070
+ const localSkills = await Promise.all(
9071
+ localDirNames.map(
9048
9072
  (dirName) => RulesyncSkill.fromDir({ baseDir: this.baseDir, dirName, global: this.global })
9049
9073
  )
9050
9074
  );
9051
- logger.info(`Successfully loaded ${rulesyncSkills.length} rulesync skills`);
9052
- return rulesyncSkills;
9075
+ const localSkillNames = new Set(localDirNames);
9076
+ const curatedDirPath = join70(this.baseDir, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);
9077
+ let curatedSkills = [];
9078
+ if (await directoryExists(curatedDirPath)) {
9079
+ const curatedDirPaths = await findFilesByGlobs(join70(curatedDirPath, "*"), { type: "dir" });
9080
+ const curatedDirNames = curatedDirPaths.map((path4) => basename19(path4));
9081
+ const nonConflicting = curatedDirNames.filter((name) => {
9082
+ if (localSkillNames.has(name)) {
9083
+ logger.debug(`Skipping curated skill "${name}": local skill takes precedence.`);
9084
+ return false;
9085
+ }
9086
+ return true;
9087
+ });
9088
+ const curatedRelativeDirPath = RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH;
9089
+ curatedSkills = await Promise.all(
9090
+ nonConflicting.map(
9091
+ (dirName) => RulesyncSkill.fromDir({
9092
+ baseDir: this.baseDir,
9093
+ relativeDirPath: curatedRelativeDirPath,
9094
+ dirName,
9095
+ global: this.global
9096
+ })
9097
+ )
9098
+ );
9099
+ }
9100
+ const allSkills = [...localSkills, ...curatedSkills];
9101
+ logger.debug(
9102
+ `Successfully loaded ${allSkills.length} rulesync skills (${localSkills.length} local, ${curatedSkills.length} curated)`
9103
+ );
9104
+ return allSkills;
9053
9105
  }
9054
9106
  /**
9055
9107
  * Implementation of abstract method from DirFeatureProcessor
@@ -9058,9 +9110,9 @@ var SkillsProcessor = class extends DirFeatureProcessor {
9058
9110
  async loadToolDirs() {
9059
9111
  const factory = this.getFactory(this.toolTarget);
9060
9112
  const paths = factory.class.getSettablePaths({ global: this.global });
9061
- const skillsDirPath = join69(this.baseDir, paths.relativeDirPath);
9062
- const dirPaths = await findFilesByGlobs(join69(skillsDirPath, "*"), { type: "dir" });
9063
- const dirNames = dirPaths.map((path4) => basename18(path4));
9113
+ const skillsDirPath = join70(this.baseDir, paths.relativeDirPath);
9114
+ const dirPaths = await findFilesByGlobs(join70(skillsDirPath, "*"), { type: "dir" });
9115
+ const dirNames = dirPaths.map((path4) => basename19(path4));
9064
9116
  const toolSkills = await Promise.all(
9065
9117
  dirNames.map(
9066
9118
  (dirName) => factory.class.fromDir({
@@ -9070,15 +9122,15 @@ var SkillsProcessor = class extends DirFeatureProcessor {
9070
9122
  })
9071
9123
  )
9072
9124
  );
9073
- logger.info(`Successfully loaded ${toolSkills.length} ${paths.relativeDirPath} skills`);
9125
+ logger.debug(`Successfully loaded ${toolSkills.length} ${paths.relativeDirPath} skills`);
9074
9126
  return toolSkills;
9075
9127
  }
9076
9128
  async loadToolDirsToDelete() {
9077
9129
  const factory = this.getFactory(this.toolTarget);
9078
9130
  const paths = factory.class.getSettablePaths({ global: this.global });
9079
- const skillsDirPath = join69(this.baseDir, paths.relativeDirPath);
9080
- const dirPaths = await findFilesByGlobs(join69(skillsDirPath, "*"), { type: "dir" });
9081
- const dirNames = dirPaths.map((path4) => basename18(path4));
9131
+ const skillsDirPath = join70(this.baseDir, paths.relativeDirPath);
9132
+ const dirPaths = await findFilesByGlobs(join70(skillsDirPath, "*"), { type: "dir" });
9133
+ const dirNames = dirPaths.map((path4) => basename19(path4));
9082
9134
  const toolSkills = dirNames.map(
9083
9135
  (dirName) => factory.class.forDeletion({
9084
9136
  baseDir: this.baseDir,
@@ -9087,7 +9139,7 @@ var SkillsProcessor = class extends DirFeatureProcessor {
9087
9139
  global: this.global
9088
9140
  })
9089
9141
  );
9090
- logger.info(
9142
+ logger.debug(
9091
9143
  `Successfully loaded ${toolSkills.length} ${paths.relativeDirPath} skills for deletion`
9092
9144
  );
9093
9145
  return toolSkills;
@@ -9139,10 +9191,10 @@ var SkillsProcessor = class extends DirFeatureProcessor {
9139
9191
  };
9140
9192
 
9141
9193
  // src/features/subagents/agentsmd-subagent.ts
9142
- import { join as join71 } from "path";
9194
+ import { join as join72 } from "path";
9143
9195
 
9144
9196
  // src/features/subagents/simulated-subagent.ts
9145
- import { basename as basename19, join as join70 } from "path";
9197
+ import { basename as basename20, join as join71 } from "path";
9146
9198
  import { z as z34 } from "zod/mini";
9147
9199
 
9148
9200
  // src/features/subagents/tool-subagent.ts
@@ -9198,7 +9250,7 @@ var SimulatedSubagent = class extends ToolSubagent {
9198
9250
  const result = SimulatedSubagentFrontmatterSchema.safeParse(frontmatter);
9199
9251
  if (!result.success) {
9200
9252
  throw new Error(
9201
- `Invalid frontmatter in ${join70(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
9253
+ `Invalid frontmatter in ${join71(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
9202
9254
  );
9203
9255
  }
9204
9256
  }
@@ -9249,7 +9301,7 @@ var SimulatedSubagent = class extends ToolSubagent {
9249
9301
  return {
9250
9302
  success: false,
9251
9303
  error: new Error(
9252
- `Invalid frontmatter in ${join70(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
9304
+ `Invalid frontmatter in ${join71(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
9253
9305
  )
9254
9306
  };
9255
9307
  }
@@ -9259,7 +9311,7 @@ var SimulatedSubagent = class extends ToolSubagent {
9259
9311
  relativeFilePath,
9260
9312
  validate = true
9261
9313
  }) {
9262
- const filePath = join70(baseDir, this.getSettablePaths().relativeDirPath, relativeFilePath);
9314
+ const filePath = join71(baseDir, this.getSettablePaths().relativeDirPath, relativeFilePath);
9263
9315
  const fileContent = await readFileContent(filePath);
9264
9316
  const { frontmatter, body: content } = parseFrontmatter(fileContent);
9265
9317
  const result = SimulatedSubagentFrontmatterSchema.safeParse(frontmatter);
@@ -9269,7 +9321,7 @@ var SimulatedSubagent = class extends ToolSubagent {
9269
9321
  return {
9270
9322
  baseDir,
9271
9323
  relativeDirPath: this.getSettablePaths().relativeDirPath,
9272
- relativeFilePath: basename19(relativeFilePath),
9324
+ relativeFilePath: basename20(relativeFilePath),
9273
9325
  frontmatter: result.data,
9274
9326
  body: content.trim(),
9275
9327
  validate
@@ -9295,7 +9347,7 @@ var SimulatedSubagent = class extends ToolSubagent {
9295
9347
  var AgentsmdSubagent = class _AgentsmdSubagent extends SimulatedSubagent {
9296
9348
  static getSettablePaths() {
9297
9349
  return {
9298
- relativeDirPath: join71(".agents", "subagents")
9350
+ relativeDirPath: join72(".agents", "subagents")
9299
9351
  };
9300
9352
  }
9301
9353
  static async fromFile(params) {
@@ -9318,11 +9370,11 @@ var AgentsmdSubagent = class _AgentsmdSubagent extends SimulatedSubagent {
9318
9370
  };
9319
9371
 
9320
9372
  // src/features/subagents/codexcli-subagent.ts
9321
- import { join as join72 } from "path";
9373
+ import { join as join73 } from "path";
9322
9374
  var CodexCliSubagent = class _CodexCliSubagent extends SimulatedSubagent {
9323
9375
  static getSettablePaths() {
9324
9376
  return {
9325
- relativeDirPath: join72(".codex", "subagents")
9377
+ relativeDirPath: join73(".codex", "subagents")
9326
9378
  };
9327
9379
  }
9328
9380
  static async fromFile(params) {
@@ -9345,11 +9397,11 @@ var CodexCliSubagent = class _CodexCliSubagent extends SimulatedSubagent {
9345
9397
  };
9346
9398
 
9347
9399
  // src/features/subagents/factorydroid-subagent.ts
9348
- import { join as join73 } from "path";
9400
+ import { join as join74 } from "path";
9349
9401
  var FactorydroidSubagent = class _FactorydroidSubagent extends SimulatedSubagent {
9350
9402
  static getSettablePaths(_options) {
9351
9403
  return {
9352
- relativeDirPath: join73(".factory", "droids")
9404
+ relativeDirPath: join74(".factory", "droids")
9353
9405
  };
9354
9406
  }
9355
9407
  static async fromFile(params) {
@@ -9372,11 +9424,11 @@ var FactorydroidSubagent = class _FactorydroidSubagent extends SimulatedSubagent
9372
9424
  };
9373
9425
 
9374
9426
  // src/features/subagents/geminicli-subagent.ts
9375
- import { join as join74 } from "path";
9427
+ import { join as join75 } from "path";
9376
9428
  var GeminiCliSubagent = class _GeminiCliSubagent extends SimulatedSubagent {
9377
9429
  static getSettablePaths() {
9378
9430
  return {
9379
- relativeDirPath: join74(".gemini", "subagents")
9431
+ relativeDirPath: join75(".gemini", "subagents")
9380
9432
  };
9381
9433
  }
9382
9434
  static async fromFile(params) {
@@ -9399,11 +9451,11 @@ var GeminiCliSubagent = class _GeminiCliSubagent extends SimulatedSubagent {
9399
9451
  };
9400
9452
 
9401
9453
  // src/features/subagents/roo-subagent.ts
9402
- import { join as join75 } from "path";
9454
+ import { join as join76 } from "path";
9403
9455
  var RooSubagent = class _RooSubagent extends SimulatedSubagent {
9404
9456
  static getSettablePaths() {
9405
9457
  return {
9406
- relativeDirPath: join75(".roo", "subagents")
9458
+ relativeDirPath: join76(".roo", "subagents")
9407
9459
  };
9408
9460
  }
9409
9461
  static async fromFile(params) {
@@ -9426,15 +9478,15 @@ var RooSubagent = class _RooSubagent extends SimulatedSubagent {
9426
9478
  };
9427
9479
 
9428
9480
  // src/features/subagents/subagents-processor.ts
9429
- import { basename as basename22, join as join82 } from "path";
9481
+ import { basename as basename23, join as join83 } from "path";
9430
9482
  import { z as z41 } from "zod/mini";
9431
9483
 
9432
9484
  // src/features/subagents/claudecode-subagent.ts
9433
- import { join as join77 } from "path";
9485
+ import { join as join78 } from "path";
9434
9486
  import { z as z36 } from "zod/mini";
9435
9487
 
9436
9488
  // src/features/subagents/rulesync-subagent.ts
9437
- import { basename as basename20, join as join76 } from "path";
9489
+ import { basename as basename21, join as join77 } from "path";
9438
9490
  import { z as z35 } from "zod/mini";
9439
9491
  var RulesyncSubagentFrontmatterSchema = z35.looseObject({
9440
9492
  targets: z35._default(RulesyncTargetsSchema, ["*"]),
@@ -9448,7 +9500,7 @@ var RulesyncSubagent = class _RulesyncSubagent extends RulesyncFile {
9448
9500
  const parseResult = RulesyncSubagentFrontmatterSchema.safeParse(frontmatter);
9449
9501
  if (!parseResult.success && rest.validate !== false) {
9450
9502
  throw new Error(
9451
- `Invalid frontmatter in ${join76(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(parseResult.error)}`
9503
+ `Invalid frontmatter in ${join77(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(parseResult.error)}`
9452
9504
  );
9453
9505
  }
9454
9506
  const parsedFrontmatter = parseResult.success ? { ...frontmatter, ...parseResult.data } : { ...frontmatter, targets: frontmatter?.targets ?? ["*"] };
@@ -9481,7 +9533,7 @@ var RulesyncSubagent = class _RulesyncSubagent extends RulesyncFile {
9481
9533
  return {
9482
9534
  success: false,
9483
9535
  error: new Error(
9484
- `Invalid frontmatter in ${join76(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
9536
+ `Invalid frontmatter in ${join77(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
9485
9537
  )
9486
9538
  };
9487
9539
  }
@@ -9490,14 +9542,14 @@ var RulesyncSubagent = class _RulesyncSubagent extends RulesyncFile {
9490
9542
  relativeFilePath
9491
9543
  }) {
9492
9544
  const fileContent = await readFileContent(
9493
- join76(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, relativeFilePath)
9545
+ join77(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, relativeFilePath)
9494
9546
  );
9495
9547
  const { frontmatter, body: content } = parseFrontmatter(fileContent);
9496
9548
  const result = RulesyncSubagentFrontmatterSchema.safeParse(frontmatter);
9497
9549
  if (!result.success) {
9498
9550
  throw new Error(`Invalid frontmatter in ${relativeFilePath}: ${formatError(result.error)}`);
9499
9551
  }
9500
- const filename = basename20(relativeFilePath);
9552
+ const filename = basename21(relativeFilePath);
9501
9553
  return new _RulesyncSubagent({
9502
9554
  baseDir: process.cwd(),
9503
9555
  relativeDirPath: this.getSettablePaths().relativeDirPath,
@@ -9525,7 +9577,7 @@ var ClaudecodeSubagent = class _ClaudecodeSubagent extends ToolSubagent {
9525
9577
  const result = ClaudecodeSubagentFrontmatterSchema.safeParse(frontmatter);
9526
9578
  if (!result.success) {
9527
9579
  throw new Error(
9528
- `Invalid frontmatter in ${join77(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
9580
+ `Invalid frontmatter in ${join78(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
9529
9581
  );
9530
9582
  }
9531
9583
  }
@@ -9537,7 +9589,7 @@ var ClaudecodeSubagent = class _ClaudecodeSubagent extends ToolSubagent {
9537
9589
  }
9538
9590
  static getSettablePaths(_options = {}) {
9539
9591
  return {
9540
- relativeDirPath: join77(".claude", "agents")
9592
+ relativeDirPath: join78(".claude", "agents")
9541
9593
  };
9542
9594
  }
9543
9595
  getFrontmatter() {
@@ -9584,7 +9636,9 @@ var ClaudecodeSubagent = class _ClaudecodeSubagent extends ToolSubagent {
9584
9636
  };
9585
9637
  const result = ClaudecodeSubagentFrontmatterSchema.safeParse(rawClaudecodeFrontmatter);
9586
9638
  if (!result.success) {
9587
- throw new Error(`Invalid claudecode subagent frontmatter: ${formatError(result.error)}`);
9639
+ throw new Error(
9640
+ `Invalid claudecode subagent frontmatter in ${rulesyncSubagent.getRelativeFilePath()}: ${formatError(result.error)}`
9641
+ );
9588
9642
  }
9589
9643
  const claudecodeFrontmatter = result.data;
9590
9644
  const body = rulesyncSubagent.getBody();
@@ -9611,7 +9665,7 @@ var ClaudecodeSubagent = class _ClaudecodeSubagent extends ToolSubagent {
9611
9665
  return {
9612
9666
  success: false,
9613
9667
  error: new Error(
9614
- `Invalid frontmatter in ${join77(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
9668
+ `Invalid frontmatter in ${join78(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
9615
9669
  )
9616
9670
  };
9617
9671
  }
@@ -9629,7 +9683,7 @@ var ClaudecodeSubagent = class _ClaudecodeSubagent extends ToolSubagent {
9629
9683
  global = false
9630
9684
  }) {
9631
9685
  const paths = this.getSettablePaths({ global });
9632
- const filePath = join77(baseDir, paths.relativeDirPath, relativeFilePath);
9686
+ const filePath = join78(baseDir, paths.relativeDirPath, relativeFilePath);
9633
9687
  const fileContent = await readFileContent(filePath);
9634
9688
  const { frontmatter, body: content } = parseFrontmatter(fileContent);
9635
9689
  const result = ClaudecodeSubagentFrontmatterSchema.safeParse(frontmatter);
@@ -9664,7 +9718,7 @@ var ClaudecodeSubagent = class _ClaudecodeSubagent extends ToolSubagent {
9664
9718
  };
9665
9719
 
9666
9720
  // src/features/subagents/copilot-subagent.ts
9667
- import { join as join78 } from "path";
9721
+ import { join as join79 } from "path";
9668
9722
  import { z as z37 } from "zod/mini";
9669
9723
  var REQUIRED_TOOL = "agent/runSubagent";
9670
9724
  var CopilotSubagentFrontmatterSchema = z37.looseObject({
@@ -9690,7 +9744,7 @@ var CopilotSubagent = class _CopilotSubagent extends ToolSubagent {
9690
9744
  const result = CopilotSubagentFrontmatterSchema.safeParse(frontmatter);
9691
9745
  if (!result.success) {
9692
9746
  throw new Error(
9693
- `Invalid frontmatter in ${join78(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
9747
+ `Invalid frontmatter in ${join79(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
9694
9748
  );
9695
9749
  }
9696
9750
  }
@@ -9702,7 +9756,7 @@ var CopilotSubagent = class _CopilotSubagent extends ToolSubagent {
9702
9756
  }
9703
9757
  static getSettablePaths(_options = {}) {
9704
9758
  return {
9705
- relativeDirPath: join78(".github", "agents")
9759
+ relativeDirPath: join79(".github", "agents")
9706
9760
  };
9707
9761
  }
9708
9762
  getFrontmatter() {
@@ -9776,7 +9830,7 @@ var CopilotSubagent = class _CopilotSubagent extends ToolSubagent {
9776
9830
  return {
9777
9831
  success: false,
9778
9832
  error: new Error(
9779
- `Invalid frontmatter in ${join78(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
9833
+ `Invalid frontmatter in ${join79(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
9780
9834
  )
9781
9835
  };
9782
9836
  }
@@ -9794,7 +9848,7 @@ var CopilotSubagent = class _CopilotSubagent extends ToolSubagent {
9794
9848
  global = false
9795
9849
  }) {
9796
9850
  const paths = this.getSettablePaths({ global });
9797
- const filePath = join78(baseDir, paths.relativeDirPath, relativeFilePath);
9851
+ const filePath = join79(baseDir, paths.relativeDirPath, relativeFilePath);
9798
9852
  const fileContent = await readFileContent(filePath);
9799
9853
  const { frontmatter, body: content } = parseFrontmatter(fileContent);
9800
9854
  const result = CopilotSubagentFrontmatterSchema.safeParse(frontmatter);
@@ -9830,7 +9884,7 @@ var CopilotSubagent = class _CopilotSubagent extends ToolSubagent {
9830
9884
  };
9831
9885
 
9832
9886
  // src/features/subagents/cursor-subagent.ts
9833
- import { join as join79 } from "path";
9887
+ import { join as join80 } from "path";
9834
9888
  import { z as z38 } from "zod/mini";
9835
9889
  var CursorSubagentFrontmatterSchema = z38.looseObject({
9836
9890
  name: z38.string(),
@@ -9844,7 +9898,7 @@ var CursorSubagent = class _CursorSubagent extends ToolSubagent {
9844
9898
  const result = CursorSubagentFrontmatterSchema.safeParse(frontmatter);
9845
9899
  if (!result.success) {
9846
9900
  throw new Error(
9847
- `Invalid frontmatter in ${join79(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
9901
+ `Invalid frontmatter in ${join80(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
9848
9902
  );
9849
9903
  }
9850
9904
  }
@@ -9856,7 +9910,7 @@ var CursorSubagent = class _CursorSubagent extends ToolSubagent {
9856
9910
  }
9857
9911
  static getSettablePaths(_options = {}) {
9858
9912
  return {
9859
- relativeDirPath: join79(".cursor", "agents")
9913
+ relativeDirPath: join80(".cursor", "agents")
9860
9914
  };
9861
9915
  }
9862
9916
  getFrontmatter() {
@@ -9923,7 +9977,7 @@ var CursorSubagent = class _CursorSubagent extends ToolSubagent {
9923
9977
  return {
9924
9978
  success: false,
9925
9979
  error: new Error(
9926
- `Invalid frontmatter in ${join79(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
9980
+ `Invalid frontmatter in ${join80(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
9927
9981
  )
9928
9982
  };
9929
9983
  }
@@ -9941,7 +9995,7 @@ var CursorSubagent = class _CursorSubagent extends ToolSubagent {
9941
9995
  global = false
9942
9996
  }) {
9943
9997
  const paths = this.getSettablePaths({ global });
9944
- const filePath = join79(baseDir, paths.relativeDirPath, relativeFilePath);
9998
+ const filePath = join80(baseDir, paths.relativeDirPath, relativeFilePath);
9945
9999
  const fileContent = await readFileContent(filePath);
9946
10000
  const { frontmatter, body: content } = parseFrontmatter(fileContent);
9947
10001
  const result = CursorSubagentFrontmatterSchema.safeParse(frontmatter);
@@ -9977,7 +10031,7 @@ var CursorSubagent = class _CursorSubagent extends ToolSubagent {
9977
10031
  };
9978
10032
 
9979
10033
  // src/features/subagents/kiro-subagent.ts
9980
- import { join as join80 } from "path";
10034
+ import { join as join81 } from "path";
9981
10035
  import { z as z39 } from "zod/mini";
9982
10036
  var KiroCliSubagentJsonSchema = z39.looseObject({
9983
10037
  name: z39.string(),
@@ -10005,7 +10059,7 @@ var KiroSubagent = class _KiroSubagent extends ToolSubagent {
10005
10059
  }
10006
10060
  static getSettablePaths(_options = {}) {
10007
10061
  return {
10008
- relativeDirPath: join80(".kiro", "agents")
10062
+ relativeDirPath: join81(".kiro", "agents")
10009
10063
  };
10010
10064
  }
10011
10065
  getBody() {
@@ -10085,7 +10139,7 @@ var KiroSubagent = class _KiroSubagent extends ToolSubagent {
10085
10139
  global = false
10086
10140
  }) {
10087
10141
  const paths = this.getSettablePaths({ global });
10088
- const filePath = join80(baseDir, paths.relativeDirPath, relativeFilePath);
10142
+ const filePath = join81(baseDir, paths.relativeDirPath, relativeFilePath);
10089
10143
  const fileContent = await readFileContent(filePath);
10090
10144
  return new _KiroSubagent({
10091
10145
  baseDir,
@@ -10114,11 +10168,11 @@ var KiroSubagent = class _KiroSubagent extends ToolSubagent {
10114
10168
  };
10115
10169
 
10116
10170
  // src/features/subagents/opencode-subagent.ts
10117
- import { basename as basename21, join as join81 } from "path";
10171
+ import { basename as basename22, join as join82 } from "path";
10118
10172
  import { z as z40 } from "zod/mini";
10119
10173
  var OpenCodeSubagentFrontmatterSchema = z40.looseObject({
10120
10174
  description: z40.string(),
10121
- mode: z40.literal("subagent"),
10175
+ mode: z40.optional(z40._default(z40.string(), "subagent")),
10122
10176
  name: z40.optional(z40.string())
10123
10177
  });
10124
10178
  var OpenCodeSubagent = class _OpenCodeSubagent extends ToolSubagent {
@@ -10129,7 +10183,7 @@ var OpenCodeSubagent = class _OpenCodeSubagent extends ToolSubagent {
10129
10183
  const result = OpenCodeSubagentFrontmatterSchema.safeParse(frontmatter);
10130
10184
  if (!result.success) {
10131
10185
  throw new Error(
10132
- `Invalid frontmatter in ${join81(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
10186
+ `Invalid frontmatter in ${join82(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
10133
10187
  );
10134
10188
  }
10135
10189
  }
@@ -10143,7 +10197,7 @@ var OpenCodeSubagent = class _OpenCodeSubagent extends ToolSubagent {
10143
10197
  global = false
10144
10198
  } = {}) {
10145
10199
  return {
10146
- relativeDirPath: global ? join81(".config", "opencode", "agent") : join81(".opencode", "agent")
10200
+ relativeDirPath: global ? join82(".config", "opencode", "agent") : join82(".opencode", "agent")
10147
10201
  };
10148
10202
  }
10149
10203
  getFrontmatter() {
@@ -10156,7 +10210,7 @@ var OpenCodeSubagent = class _OpenCodeSubagent extends ToolSubagent {
10156
10210
  const { description, mode, name, ...opencodeSection } = this.frontmatter;
10157
10211
  const rulesyncFrontmatter = {
10158
10212
  targets: ["*"],
10159
- name: name ?? basename21(this.getRelativeFilePath(), ".md"),
10213
+ name: name ?? basename22(this.getRelativeFilePath(), ".md"),
10160
10214
  description,
10161
10215
  opencode: { mode, ...opencodeSection }
10162
10216
  };
@@ -10209,7 +10263,7 @@ var OpenCodeSubagent = class _OpenCodeSubagent extends ToolSubagent {
10209
10263
  return {
10210
10264
  success: false,
10211
10265
  error: new Error(
10212
- `Invalid frontmatter in ${join81(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
10266
+ `Invalid frontmatter in ${join82(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
10213
10267
  )
10214
10268
  };
10215
10269
  }
@@ -10226,7 +10280,7 @@ var OpenCodeSubagent = class _OpenCodeSubagent extends ToolSubagent {
10226
10280
  global = false
10227
10281
  }) {
10228
10282
  const paths = this.getSettablePaths({ global });
10229
- const filePath = join81(baseDir, paths.relativeDirPath, relativeFilePath);
10283
+ const filePath = join82(baseDir, paths.relativeDirPath, relativeFilePath);
10230
10284
  const fileContent = await readFileContent(filePath);
10231
10285
  const { frontmatter, body: content } = parseFrontmatter(fileContent);
10232
10286
  const result = OpenCodeSubagentFrontmatterSchema.safeParse(frontmatter);
@@ -10437,7 +10491,7 @@ var SubagentsProcessor = class extends FeatureProcessor {
10437
10491
  * Load and parse rulesync subagent files from .rulesync/subagents/ directory
10438
10492
  */
10439
10493
  async loadRulesyncFiles() {
10440
- const subagentsDir = join82(this.baseDir, RulesyncSubagent.getSettablePaths().relativeDirPath);
10494
+ const subagentsDir = join83(this.baseDir, RulesyncSubagent.getSettablePaths().relativeDirPath);
10441
10495
  const dirExists = await directoryExists(subagentsDir);
10442
10496
  if (!dirExists) {
10443
10497
  logger.debug(`Rulesync subagents directory not found: ${subagentsDir}`);
@@ -10449,10 +10503,10 @@ var SubagentsProcessor = class extends FeatureProcessor {
10449
10503
  logger.debug(`No markdown files found in rulesync subagents directory: ${subagentsDir}`);
10450
10504
  return [];
10451
10505
  }
10452
- logger.info(`Found ${mdFiles.length} subagent files in ${subagentsDir}`);
10506
+ logger.debug(`Found ${mdFiles.length} subagent files in ${subagentsDir}`);
10453
10507
  const rulesyncSubagents = [];
10454
10508
  for (const mdFile of mdFiles) {
10455
- const filepath = join82(subagentsDir, mdFile);
10509
+ const filepath = join83(subagentsDir, mdFile);
10456
10510
  try {
10457
10511
  const rulesyncSubagent = await RulesyncSubagent.fromFile({
10458
10512
  relativeFilePath: mdFile,
@@ -10461,7 +10515,7 @@ var SubagentsProcessor = class extends FeatureProcessor {
10461
10515
  rulesyncSubagents.push(rulesyncSubagent);
10462
10516
  logger.debug(`Successfully loaded subagent: ${mdFile}`);
10463
10517
  } catch (error) {
10464
- logger.warn(`Failed to load subagent file ${filepath}:`, error);
10518
+ logger.warn(`Failed to load subagent file ${filepath}: ${formatError(error)}`);
10465
10519
  continue;
10466
10520
  }
10467
10521
  }
@@ -10469,7 +10523,7 @@ var SubagentsProcessor = class extends FeatureProcessor {
10469
10523
  logger.debug(`No valid subagents found in ${subagentsDir}`);
10470
10524
  return [];
10471
10525
  }
10472
- logger.info(`Successfully loaded ${rulesyncSubagents.length} rulesync subagents`);
10526
+ logger.debug(`Successfully loaded ${rulesyncSubagents.length} rulesync subagents`);
10473
10527
  return rulesyncSubagents;
10474
10528
  }
10475
10529
  /**
@@ -10482,30 +10536,32 @@ var SubagentsProcessor = class extends FeatureProcessor {
10482
10536
  const factory = this.getFactory(this.toolTarget);
10483
10537
  const paths = factory.class.getSettablePaths({ global: this.global });
10484
10538
  const subagentFilePaths = await findFilesByGlobs(
10485
- join82(this.baseDir, paths.relativeDirPath, factory.meta.filePattern)
10539
+ join83(this.baseDir, paths.relativeDirPath, factory.meta.filePattern)
10486
10540
  );
10487
10541
  if (forDeletion) {
10488
10542
  const toolSubagents2 = subagentFilePaths.map(
10489
10543
  (path4) => factory.class.forDeletion({
10490
10544
  baseDir: this.baseDir,
10491
10545
  relativeDirPath: paths.relativeDirPath,
10492
- relativeFilePath: basename22(path4),
10546
+ relativeFilePath: basename23(path4),
10493
10547
  global: this.global
10494
10548
  })
10495
10549
  ).filter((subagent) => subagent.isDeletable());
10496
- logger.info(`Successfully loaded ${toolSubagents2.length} ${paths.relativeDirPath} subagents`);
10550
+ logger.debug(
10551
+ `Successfully loaded ${toolSubagents2.length} ${paths.relativeDirPath} subagents`
10552
+ );
10497
10553
  return toolSubagents2;
10498
10554
  }
10499
10555
  const toolSubagents = await Promise.all(
10500
10556
  subagentFilePaths.map(
10501
10557
  (path4) => factory.class.fromFile({
10502
10558
  baseDir: this.baseDir,
10503
- relativeFilePath: basename22(path4),
10559
+ relativeFilePath: basename23(path4),
10504
10560
  global: this.global
10505
10561
  })
10506
10562
  )
10507
10563
  );
10508
- logger.info(`Successfully loaded ${toolSubagents.length} ${paths.relativeDirPath} subagents`);
10564
+ logger.debug(`Successfully loaded ${toolSubagents.length} ${paths.relativeDirPath} subagents`);
10509
10565
  return toolSubagents;
10510
10566
  }
10511
10567
  /**
@@ -10545,13 +10601,13 @@ var SubagentsProcessor = class extends FeatureProcessor {
10545
10601
  };
10546
10602
 
10547
10603
  // src/features/rules/agentsmd-rule.ts
10548
- import { join as join85 } from "path";
10604
+ import { join as join86 } from "path";
10549
10605
 
10550
10606
  // src/features/rules/tool-rule.ts
10551
- import { join as join84 } from "path";
10607
+ import { join as join85 } from "path";
10552
10608
 
10553
10609
  // src/features/rules/rulesync-rule.ts
10554
- import { join as join83 } from "path";
10610
+ import { join as join84 } from "path";
10555
10611
  import { z as z42 } from "zod/mini";
10556
10612
  var RulesyncRuleFrontmatterSchema = z42.object({
10557
10613
  root: z42.optional(z42.boolean()),
@@ -10598,7 +10654,7 @@ var RulesyncRule = class _RulesyncRule extends RulesyncFile {
10598
10654
  const parseResult = RulesyncRuleFrontmatterSchema.safeParse(frontmatter);
10599
10655
  if (!parseResult.success && rest.validate !== false) {
10600
10656
  throw new Error(
10601
- `Invalid frontmatter in ${join83(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(parseResult.error)}`
10657
+ `Invalid frontmatter in ${join84(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(parseResult.error)}`
10602
10658
  );
10603
10659
  }
10604
10660
  const parsedFrontmatter = parseResult.success ? parseResult.data : { ...frontmatter, targets: frontmatter.targets ?? ["*"] };
@@ -10633,7 +10689,7 @@ var RulesyncRule = class _RulesyncRule extends RulesyncFile {
10633
10689
  return {
10634
10690
  success: false,
10635
10691
  error: new Error(
10636
- `Invalid frontmatter in ${join83(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
10692
+ `Invalid frontmatter in ${join84(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
10637
10693
  )
10638
10694
  };
10639
10695
  }
@@ -10642,7 +10698,7 @@ var RulesyncRule = class _RulesyncRule extends RulesyncFile {
10642
10698
  relativeFilePath,
10643
10699
  validate = true
10644
10700
  }) {
10645
- const filePath = join83(
10701
+ const filePath = join84(
10646
10702
  process.cwd(),
10647
10703
  this.getSettablePaths().recommended.relativeDirPath,
10648
10704
  relativeFilePath
@@ -10726,7 +10782,7 @@ var ToolRule = class extends ToolFile {
10726
10782
  };
10727
10783
  }
10728
10784
  if (!nonRootPath) {
10729
- throw new Error("nonRootPath is not set");
10785
+ throw new Error(`nonRootPath is not set for ${rulesyncRule.getRelativeFilePath()}`);
10730
10786
  }
10731
10787
  return {
10732
10788
  baseDir,
@@ -10744,7 +10800,7 @@ var ToolRule = class extends ToolFile {
10744
10800
  rulesyncRule,
10745
10801
  validate = true,
10746
10802
  rootPath = { relativeDirPath: ".", relativeFilePath: "AGENTS.md" },
10747
- nonRootPath = { relativeDirPath: join84(".agents", "memories") }
10803
+ nonRootPath = { relativeDirPath: join85(".agents", "memories") }
10748
10804
  }) {
10749
10805
  const params = this.buildToolRuleParamsDefault({
10750
10806
  baseDir,
@@ -10755,7 +10811,7 @@ var ToolRule = class extends ToolFile {
10755
10811
  });
10756
10812
  const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
10757
10813
  if (!rulesyncFrontmatter.root && rulesyncFrontmatter.agentsmd?.subprojectPath) {
10758
- params.relativeDirPath = join84(rulesyncFrontmatter.agentsmd.subprojectPath);
10814
+ params.relativeDirPath = join85(rulesyncFrontmatter.agentsmd.subprojectPath);
10759
10815
  params.relativeFilePath = "AGENTS.md";
10760
10816
  }
10761
10817
  return params;
@@ -10804,7 +10860,7 @@ var ToolRule = class extends ToolFile {
10804
10860
  }
10805
10861
  };
10806
10862
  function buildToolPath(toolDir, subDir, excludeToolDir) {
10807
- return excludeToolDir ? subDir : join84(toolDir, subDir);
10863
+ return excludeToolDir ? subDir : join85(toolDir, subDir);
10808
10864
  }
10809
10865
 
10810
10866
  // src/features/rules/agentsmd-rule.ts
@@ -10833,8 +10889,8 @@ var AgentsMdRule = class _AgentsMdRule extends ToolRule {
10833
10889
  validate = true
10834
10890
  }) {
10835
10891
  const isRoot = relativeFilePath === "AGENTS.md";
10836
- const relativePath = isRoot ? "AGENTS.md" : join85(".agents", "memories", relativeFilePath);
10837
- const fileContent = await readFileContent(join85(baseDir, relativePath));
10892
+ const relativePath = isRoot ? "AGENTS.md" : join86(".agents", "memories", relativeFilePath);
10893
+ const fileContent = await readFileContent(join86(baseDir, relativePath));
10838
10894
  return new _AgentsMdRule({
10839
10895
  baseDir,
10840
10896
  relativeDirPath: isRoot ? this.getSettablePaths().root.relativeDirPath : this.getSettablePaths().nonRoot.relativeDirPath,
@@ -10889,7 +10945,7 @@ var AgentsMdRule = class _AgentsMdRule extends ToolRule {
10889
10945
  };
10890
10946
 
10891
10947
  // src/features/rules/antigravity-rule.ts
10892
- import { join as join86 } from "path";
10948
+ import { join as join87 } from "path";
10893
10949
  import { z as z43 } from "zod/mini";
10894
10950
  var AntigravityRuleFrontmatterSchema = z43.looseObject({
10895
10951
  trigger: z43.optional(
@@ -11048,7 +11104,7 @@ var AntigravityRule = class _AntigravityRule extends ToolRule {
11048
11104
  const result = AntigravityRuleFrontmatterSchema.safeParse(frontmatter);
11049
11105
  if (!result.success) {
11050
11106
  throw new Error(
11051
- `Invalid frontmatter in ${join86(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
11107
+ `Invalid frontmatter in ${join87(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
11052
11108
  );
11053
11109
  }
11054
11110
  }
@@ -11072,7 +11128,7 @@ var AntigravityRule = class _AntigravityRule extends ToolRule {
11072
11128
  relativeFilePath,
11073
11129
  validate = true
11074
11130
  }) {
11075
- const filePath = join86(
11131
+ const filePath = join87(
11076
11132
  baseDir,
11077
11133
  this.getSettablePaths().nonRoot.relativeDirPath,
11078
11134
  relativeFilePath
@@ -11213,7 +11269,7 @@ var AntigravityRule = class _AntigravityRule extends ToolRule {
11213
11269
  };
11214
11270
 
11215
11271
  // src/features/rules/augmentcode-legacy-rule.ts
11216
- import { join as join87 } from "path";
11272
+ import { join as join88 } from "path";
11217
11273
  var AugmentcodeLegacyRule = class _AugmentcodeLegacyRule extends ToolRule {
11218
11274
  toRulesyncRule() {
11219
11275
  const rulesyncFrontmatter = {
@@ -11274,8 +11330,8 @@ var AugmentcodeLegacyRule = class _AugmentcodeLegacyRule extends ToolRule {
11274
11330
  }) {
11275
11331
  const settablePaths = this.getSettablePaths();
11276
11332
  const isRoot = relativeFilePath === settablePaths.root.relativeFilePath;
11277
- const relativePath = isRoot ? settablePaths.root.relativeFilePath : join87(settablePaths.nonRoot.relativeDirPath, relativeFilePath);
11278
- const fileContent = await readFileContent(join87(baseDir, relativePath));
11333
+ const relativePath = isRoot ? settablePaths.root.relativeFilePath : join88(settablePaths.nonRoot.relativeDirPath, relativeFilePath);
11334
+ const fileContent = await readFileContent(join88(baseDir, relativePath));
11279
11335
  return new _AugmentcodeLegacyRule({
11280
11336
  baseDir,
11281
11337
  relativeDirPath: isRoot ? settablePaths.root.relativeDirPath : settablePaths.nonRoot.relativeDirPath,
@@ -11304,7 +11360,7 @@ var AugmentcodeLegacyRule = class _AugmentcodeLegacyRule extends ToolRule {
11304
11360
  };
11305
11361
 
11306
11362
  // src/features/rules/augmentcode-rule.ts
11307
- import { join as join88 } from "path";
11363
+ import { join as join89 } from "path";
11308
11364
  var AugmentcodeRule = class _AugmentcodeRule extends ToolRule {
11309
11365
  toRulesyncRule() {
11310
11366
  return this.toRulesyncRuleDefault();
@@ -11336,7 +11392,7 @@ var AugmentcodeRule = class _AugmentcodeRule extends ToolRule {
11336
11392
  validate = true
11337
11393
  }) {
11338
11394
  const fileContent = await readFileContent(
11339
- join88(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
11395
+ join89(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
11340
11396
  );
11341
11397
  const { body: content } = parseFrontmatter(fileContent);
11342
11398
  return new _AugmentcodeRule({
@@ -11372,7 +11428,7 @@ var AugmentcodeRule = class _AugmentcodeRule extends ToolRule {
11372
11428
  };
11373
11429
 
11374
11430
  // src/features/rules/claudecode-legacy-rule.ts
11375
- import { join as join89 } from "path";
11431
+ import { join as join90 } from "path";
11376
11432
  var ClaudecodeLegacyRule = class _ClaudecodeLegacyRule extends ToolRule {
11377
11433
  static getSettablePaths({
11378
11434
  global,
@@ -11407,7 +11463,7 @@ var ClaudecodeLegacyRule = class _ClaudecodeLegacyRule extends ToolRule {
11407
11463
  if (isRoot) {
11408
11464
  const relativePath2 = paths.root.relativeFilePath;
11409
11465
  const fileContent2 = await readFileContent(
11410
- join89(baseDir, paths.root.relativeDirPath, relativePath2)
11466
+ join90(baseDir, paths.root.relativeDirPath, relativePath2)
11411
11467
  );
11412
11468
  return new _ClaudecodeLegacyRule({
11413
11469
  baseDir,
@@ -11419,10 +11475,10 @@ var ClaudecodeLegacyRule = class _ClaudecodeLegacyRule extends ToolRule {
11419
11475
  });
11420
11476
  }
11421
11477
  if (!paths.nonRoot) {
11422
- throw new Error("nonRoot path is not set");
11478
+ throw new Error(`nonRoot path is not set for ${relativeFilePath}`);
11423
11479
  }
11424
- const relativePath = join89(paths.nonRoot.relativeDirPath, relativeFilePath);
11425
- const fileContent = await readFileContent(join89(baseDir, relativePath));
11480
+ const relativePath = join90(paths.nonRoot.relativeDirPath, relativeFilePath);
11481
+ const fileContent = await readFileContent(join90(baseDir, relativePath));
11426
11482
  return new _ClaudecodeLegacyRule({
11427
11483
  baseDir,
11428
11484
  relativeDirPath: paths.nonRoot.relativeDirPath,
@@ -11481,7 +11537,7 @@ var ClaudecodeLegacyRule = class _ClaudecodeLegacyRule extends ToolRule {
11481
11537
  };
11482
11538
 
11483
11539
  // src/features/rules/claudecode-rule.ts
11484
- import { join as join90 } from "path";
11540
+ import { join as join91 } from "path";
11485
11541
  import { z as z44 } from "zod/mini";
11486
11542
  var ClaudecodeRuleFrontmatterSchema = z44.object({
11487
11543
  paths: z44.optional(z44.array(z44.string()))
@@ -11516,7 +11572,7 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
11516
11572
  const result = ClaudecodeRuleFrontmatterSchema.safeParse(frontmatter);
11517
11573
  if (!result.success) {
11518
11574
  throw new Error(
11519
- `Invalid frontmatter in ${join90(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
11575
+ `Invalid frontmatter in ${join91(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
11520
11576
  );
11521
11577
  }
11522
11578
  }
@@ -11544,7 +11600,7 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
11544
11600
  const isRoot = relativeFilePath === paths.root.relativeFilePath;
11545
11601
  if (isRoot) {
11546
11602
  const fileContent2 = await readFileContent(
11547
- join90(baseDir, paths.root.relativeDirPath, paths.root.relativeFilePath)
11603
+ join91(baseDir, paths.root.relativeDirPath, paths.root.relativeFilePath)
11548
11604
  );
11549
11605
  return new _ClaudecodeRule({
11550
11606
  baseDir,
@@ -11557,15 +11613,15 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
11557
11613
  });
11558
11614
  }
11559
11615
  if (!paths.nonRoot) {
11560
- throw new Error("nonRoot path is not set");
11616
+ throw new Error(`nonRoot path is not set for ${relativeFilePath}`);
11561
11617
  }
11562
- const relativePath = join90(paths.nonRoot.relativeDirPath, relativeFilePath);
11563
- const fileContent = await readFileContent(join90(baseDir, relativePath));
11618
+ const relativePath = join91(paths.nonRoot.relativeDirPath, relativeFilePath);
11619
+ const fileContent = await readFileContent(join91(baseDir, relativePath));
11564
11620
  const { frontmatter, body: content } = parseFrontmatter(fileContent);
11565
11621
  const result = ClaudecodeRuleFrontmatterSchema.safeParse(frontmatter);
11566
11622
  if (!result.success) {
11567
11623
  throw new Error(
11568
- `Invalid frontmatter in ${join90(baseDir, relativePath)}: ${formatError(result.error)}`
11624
+ `Invalid frontmatter in ${join91(baseDir, relativePath)}: ${formatError(result.error)}`
11569
11625
  );
11570
11626
  }
11571
11627
  return new _ClaudecodeRule({
@@ -11624,7 +11680,7 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
11624
11680
  });
11625
11681
  }
11626
11682
  if (!paths.nonRoot) {
11627
- throw new Error("nonRoot path is not set");
11683
+ throw new Error(`nonRoot path is not set for ${rulesyncRule.getRelativeFilePath()}`);
11628
11684
  }
11629
11685
  return new _ClaudecodeRule({
11630
11686
  baseDir,
@@ -11672,7 +11728,7 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
11672
11728
  return {
11673
11729
  success: false,
11674
11730
  error: new Error(
11675
- `Invalid frontmatter in ${join90(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
11731
+ `Invalid frontmatter in ${join91(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
11676
11732
  )
11677
11733
  };
11678
11734
  }
@@ -11692,7 +11748,7 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
11692
11748
  };
11693
11749
 
11694
11750
  // src/features/rules/cline-rule.ts
11695
- import { join as join91 } from "path";
11751
+ import { join as join92 } from "path";
11696
11752
  import { z as z45 } from "zod/mini";
11697
11753
  var ClineRuleFrontmatterSchema = z45.object({
11698
11754
  description: z45.string()
@@ -11738,7 +11794,7 @@ var ClineRule = class _ClineRule extends ToolRule {
11738
11794
  validate = true
11739
11795
  }) {
11740
11796
  const fileContent = await readFileContent(
11741
- join91(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
11797
+ join92(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
11742
11798
  );
11743
11799
  return new _ClineRule({
11744
11800
  baseDir,
@@ -11764,7 +11820,7 @@ var ClineRule = class _ClineRule extends ToolRule {
11764
11820
  };
11765
11821
 
11766
11822
  // src/features/rules/codexcli-rule.ts
11767
- import { join as join92 } from "path";
11823
+ import { join as join93 } from "path";
11768
11824
  var CodexcliRule = class _CodexcliRule extends ToolRule {
11769
11825
  static getSettablePaths({
11770
11826
  global,
@@ -11799,7 +11855,7 @@ var CodexcliRule = class _CodexcliRule extends ToolRule {
11799
11855
  if (isRoot) {
11800
11856
  const relativePath2 = paths.root.relativeFilePath;
11801
11857
  const fileContent2 = await readFileContent(
11802
- join92(baseDir, paths.root.relativeDirPath, relativePath2)
11858
+ join93(baseDir, paths.root.relativeDirPath, relativePath2)
11803
11859
  );
11804
11860
  return new _CodexcliRule({
11805
11861
  baseDir,
@@ -11811,10 +11867,10 @@ var CodexcliRule = class _CodexcliRule extends ToolRule {
11811
11867
  });
11812
11868
  }
11813
11869
  if (!paths.nonRoot) {
11814
- throw new Error("nonRoot path is not set");
11870
+ throw new Error(`nonRoot path is not set for ${relativeFilePath}`);
11815
11871
  }
11816
- const relativePath = join92(paths.nonRoot.relativeDirPath, relativeFilePath);
11817
- const fileContent = await readFileContent(join92(baseDir, relativePath));
11872
+ const relativePath = join93(paths.nonRoot.relativeDirPath, relativeFilePath);
11873
+ const fileContent = await readFileContent(join93(baseDir, relativePath));
11818
11874
  return new _CodexcliRule({
11819
11875
  baseDir,
11820
11876
  relativeDirPath: paths.nonRoot.relativeDirPath,
@@ -11873,7 +11929,7 @@ var CodexcliRule = class _CodexcliRule extends ToolRule {
11873
11929
  };
11874
11930
 
11875
11931
  // src/features/rules/copilot-rule.ts
11876
- import { join as join93 } from "path";
11932
+ import { join as join94 } from "path";
11877
11933
  import { z as z46 } from "zod/mini";
11878
11934
  var CopilotRuleFrontmatterSchema = z46.object({
11879
11935
  description: z46.optional(z46.string()),
@@ -11899,7 +11955,7 @@ var CopilotRule = class _CopilotRule extends ToolRule {
11899
11955
  const result = CopilotRuleFrontmatterSchema.safeParse(frontmatter);
11900
11956
  if (!result.success) {
11901
11957
  throw new Error(
11902
- `Invalid frontmatter in ${join93(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
11958
+ `Invalid frontmatter in ${join94(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
11903
11959
  );
11904
11960
  }
11905
11961
  }
@@ -11981,11 +12037,11 @@ var CopilotRule = class _CopilotRule extends ToolRule {
11981
12037
  validate = true
11982
12038
  }) {
11983
12039
  const isRoot = relativeFilePath === "copilot-instructions.md";
11984
- const relativePath = isRoot ? join93(
12040
+ const relativePath = isRoot ? join94(
11985
12041
  this.getSettablePaths().root.relativeDirPath,
11986
12042
  this.getSettablePaths().root.relativeFilePath
11987
- ) : join93(this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath);
11988
- const fileContent = await readFileContent(join93(baseDir, relativePath));
12043
+ ) : join94(this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath);
12044
+ const fileContent = await readFileContent(join94(baseDir, relativePath));
11989
12045
  if (isRoot) {
11990
12046
  return new _CopilotRule({
11991
12047
  baseDir,
@@ -12001,7 +12057,7 @@ var CopilotRule = class _CopilotRule extends ToolRule {
12001
12057
  const result = CopilotRuleFrontmatterSchema.safeParse(frontmatter);
12002
12058
  if (!result.success) {
12003
12059
  throw new Error(
12004
- `Invalid frontmatter in ${join93(baseDir, relativeFilePath)}: ${formatError(result.error)}`
12060
+ `Invalid frontmatter in ${join94(baseDir, relativeFilePath)}: ${formatError(result.error)}`
12005
12061
  );
12006
12062
  }
12007
12063
  return new _CopilotRule({
@@ -12041,7 +12097,7 @@ var CopilotRule = class _CopilotRule extends ToolRule {
12041
12097
  return {
12042
12098
  success: false,
12043
12099
  error: new Error(
12044
- `Invalid frontmatter in ${join93(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
12100
+ `Invalid frontmatter in ${join94(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
12045
12101
  )
12046
12102
  };
12047
12103
  }
@@ -12061,7 +12117,7 @@ var CopilotRule = class _CopilotRule extends ToolRule {
12061
12117
  };
12062
12118
 
12063
12119
  // src/features/rules/cursor-rule.ts
12064
- import { join as join94 } from "path";
12120
+ import { join as join95 } from "path";
12065
12121
  import { z as z47 } from "zod/mini";
12066
12122
  var CursorRuleFrontmatterSchema = z47.object({
12067
12123
  description: z47.optional(z47.string()),
@@ -12083,7 +12139,7 @@ var CursorRule = class _CursorRule extends ToolRule {
12083
12139
  const result = CursorRuleFrontmatterSchema.safeParse(frontmatter);
12084
12140
  if (!result.success) {
12085
12141
  throw new Error(
12086
- `Invalid frontmatter in ${join94(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
12142
+ `Invalid frontmatter in ${join95(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`
12087
12143
  );
12088
12144
  }
12089
12145
  }
@@ -12200,13 +12256,13 @@ var CursorRule = class _CursorRule extends ToolRule {
12200
12256
  validate = true
12201
12257
  }) {
12202
12258
  const fileContent = await readFileContent(
12203
- join94(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
12259
+ join95(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
12204
12260
  );
12205
12261
  const { frontmatter, body: content } = _CursorRule.parseCursorFrontmatter(fileContent);
12206
12262
  const result = CursorRuleFrontmatterSchema.safeParse(frontmatter);
12207
12263
  if (!result.success) {
12208
12264
  throw new Error(
12209
- `Invalid frontmatter in ${join94(baseDir, relativeFilePath)}: ${formatError(result.error)}`
12265
+ `Invalid frontmatter in ${join95(baseDir, relativeFilePath)}: ${formatError(result.error)}`
12210
12266
  );
12211
12267
  }
12212
12268
  return new _CursorRule({
@@ -12243,7 +12299,7 @@ var CursorRule = class _CursorRule extends ToolRule {
12243
12299
  return {
12244
12300
  success: false,
12245
12301
  error: new Error(
12246
- `Invalid frontmatter in ${join94(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
12302
+ `Invalid frontmatter in ${join95(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`
12247
12303
  )
12248
12304
  };
12249
12305
  }
@@ -12263,7 +12319,7 @@ var CursorRule = class _CursorRule extends ToolRule {
12263
12319
  };
12264
12320
 
12265
12321
  // src/features/rules/factorydroid-rule.ts
12266
- import { join as join95 } from "path";
12322
+ import { join as join96 } from "path";
12267
12323
  var FactorydroidRule = class _FactorydroidRule extends ToolRule {
12268
12324
  constructor({ fileContent, root, ...rest }) {
12269
12325
  super({
@@ -12303,8 +12359,8 @@ var FactorydroidRule = class _FactorydroidRule extends ToolRule {
12303
12359
  const paths = this.getSettablePaths({ global });
12304
12360
  const isRoot = relativeFilePath === paths.root.relativeFilePath;
12305
12361
  if (isRoot) {
12306
- const relativePath2 = join95(paths.root.relativeDirPath, paths.root.relativeFilePath);
12307
- const fileContent2 = await readFileContent(join95(baseDir, relativePath2));
12362
+ const relativePath2 = join96(paths.root.relativeDirPath, paths.root.relativeFilePath);
12363
+ const fileContent2 = await readFileContent(join96(baseDir, relativePath2));
12308
12364
  return new _FactorydroidRule({
12309
12365
  baseDir,
12310
12366
  relativeDirPath: paths.root.relativeDirPath,
@@ -12315,10 +12371,10 @@ var FactorydroidRule = class _FactorydroidRule extends ToolRule {
12315
12371
  });
12316
12372
  }
12317
12373
  if (!paths.nonRoot) {
12318
- throw new Error("nonRoot path is not set");
12374
+ throw new Error(`nonRoot path is not set for ${relativeFilePath}`);
12319
12375
  }
12320
- const relativePath = join95(paths.nonRoot.relativeDirPath, relativeFilePath);
12321
- const fileContent = await readFileContent(join95(baseDir, relativePath));
12376
+ const relativePath = join96(paths.nonRoot.relativeDirPath, relativeFilePath);
12377
+ const fileContent = await readFileContent(join96(baseDir, relativePath));
12322
12378
  return new _FactorydroidRule({
12323
12379
  baseDir,
12324
12380
  relativeDirPath: paths.nonRoot.relativeDirPath,
@@ -12377,7 +12433,7 @@ var FactorydroidRule = class _FactorydroidRule extends ToolRule {
12377
12433
  };
12378
12434
 
12379
12435
  // src/features/rules/geminicli-rule.ts
12380
- import { join as join96 } from "path";
12436
+ import { join as join97 } from "path";
12381
12437
  var GeminiCliRule = class _GeminiCliRule extends ToolRule {
12382
12438
  static getSettablePaths({
12383
12439
  global,
@@ -12412,7 +12468,7 @@ var GeminiCliRule = class _GeminiCliRule extends ToolRule {
12412
12468
  if (isRoot) {
12413
12469
  const relativePath2 = paths.root.relativeFilePath;
12414
12470
  const fileContent2 = await readFileContent(
12415
- join96(baseDir, paths.root.relativeDirPath, relativePath2)
12471
+ join97(baseDir, paths.root.relativeDirPath, relativePath2)
12416
12472
  );
12417
12473
  return new _GeminiCliRule({
12418
12474
  baseDir,
@@ -12424,10 +12480,10 @@ var GeminiCliRule = class _GeminiCliRule extends ToolRule {
12424
12480
  });
12425
12481
  }
12426
12482
  if (!paths.nonRoot) {
12427
- throw new Error("nonRoot path is not set");
12483
+ throw new Error(`nonRoot path is not set for ${relativeFilePath}`);
12428
12484
  }
12429
- const relativePath = join96(paths.nonRoot.relativeDirPath, relativeFilePath);
12430
- const fileContent = await readFileContent(join96(baseDir, relativePath));
12485
+ const relativePath = join97(paths.nonRoot.relativeDirPath, relativeFilePath);
12486
+ const fileContent = await readFileContent(join97(baseDir, relativePath));
12431
12487
  return new _GeminiCliRule({
12432
12488
  baseDir,
12433
12489
  relativeDirPath: paths.nonRoot.relativeDirPath,
@@ -12486,7 +12542,7 @@ var GeminiCliRule = class _GeminiCliRule extends ToolRule {
12486
12542
  };
12487
12543
 
12488
12544
  // src/features/rules/junie-rule.ts
12489
- import { join as join97 } from "path";
12545
+ import { join as join98 } from "path";
12490
12546
  var JunieRule = class _JunieRule extends ToolRule {
12491
12547
  static getSettablePaths(_options = {}) {
12492
12548
  return {
@@ -12505,8 +12561,8 @@ var JunieRule = class _JunieRule extends ToolRule {
12505
12561
  validate = true
12506
12562
  }) {
12507
12563
  const isRoot = relativeFilePath === "guidelines.md";
12508
- const relativePath = isRoot ? "guidelines.md" : join97(".junie", "memories", relativeFilePath);
12509
- const fileContent = await readFileContent(join97(baseDir, relativePath));
12564
+ const relativePath = isRoot ? "guidelines.md" : join98(".junie", "memories", relativeFilePath);
12565
+ const fileContent = await readFileContent(join98(baseDir, relativePath));
12510
12566
  return new _JunieRule({
12511
12567
  baseDir,
12512
12568
  relativeDirPath: isRoot ? this.getSettablePaths().root.relativeDirPath : this.getSettablePaths().nonRoot.relativeDirPath,
@@ -12561,7 +12617,7 @@ var JunieRule = class _JunieRule extends ToolRule {
12561
12617
  };
12562
12618
 
12563
12619
  // src/features/rules/kilo-rule.ts
12564
- import { join as join98 } from "path";
12620
+ import { join as join99 } from "path";
12565
12621
  var KiloRule = class _KiloRule extends ToolRule {
12566
12622
  static getSettablePaths(_options = {}) {
12567
12623
  return {
@@ -12576,7 +12632,7 @@ var KiloRule = class _KiloRule extends ToolRule {
12576
12632
  validate = true
12577
12633
  }) {
12578
12634
  const fileContent = await readFileContent(
12579
- join98(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
12635
+ join99(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
12580
12636
  );
12581
12637
  return new _KiloRule({
12582
12638
  baseDir,
@@ -12628,7 +12684,7 @@ var KiloRule = class _KiloRule extends ToolRule {
12628
12684
  };
12629
12685
 
12630
12686
  // src/features/rules/kiro-rule.ts
12631
- import { join as join99 } from "path";
12687
+ import { join as join100 } from "path";
12632
12688
  var KiroRule = class _KiroRule extends ToolRule {
12633
12689
  static getSettablePaths(_options = {}) {
12634
12690
  return {
@@ -12643,7 +12699,7 @@ var KiroRule = class _KiroRule extends ToolRule {
12643
12699
  validate = true
12644
12700
  }) {
12645
12701
  const fileContent = await readFileContent(
12646
- join99(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
12702
+ join100(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
12647
12703
  );
12648
12704
  return new _KiroRule({
12649
12705
  baseDir,
@@ -12697,7 +12753,7 @@ var KiroRule = class _KiroRule extends ToolRule {
12697
12753
  };
12698
12754
 
12699
12755
  // src/features/rules/opencode-rule.ts
12700
- import { join as join100 } from "path";
12756
+ import { join as join101 } from "path";
12701
12757
  var OpenCodeRule = class _OpenCodeRule extends ToolRule {
12702
12758
  static getSettablePaths(_options = {}) {
12703
12759
  return {
@@ -12716,8 +12772,8 @@ var OpenCodeRule = class _OpenCodeRule extends ToolRule {
12716
12772
  validate = true
12717
12773
  }) {
12718
12774
  const isRoot = relativeFilePath === "AGENTS.md";
12719
- const relativePath = isRoot ? "AGENTS.md" : join100(".opencode", "memories", relativeFilePath);
12720
- const fileContent = await readFileContent(join100(baseDir, relativePath));
12775
+ const relativePath = isRoot ? "AGENTS.md" : join101(".opencode", "memories", relativeFilePath);
12776
+ const fileContent = await readFileContent(join101(baseDir, relativePath));
12721
12777
  return new _OpenCodeRule({
12722
12778
  baseDir,
12723
12779
  relativeDirPath: isRoot ? this.getSettablePaths().root.relativeDirPath : this.getSettablePaths().nonRoot.relativeDirPath,
@@ -12772,7 +12828,7 @@ var OpenCodeRule = class _OpenCodeRule extends ToolRule {
12772
12828
  };
12773
12829
 
12774
12830
  // src/features/rules/qwencode-rule.ts
12775
- import { join as join101 } from "path";
12831
+ import { join as join102 } from "path";
12776
12832
  var QwencodeRule = class _QwencodeRule extends ToolRule {
12777
12833
  static getSettablePaths(_options = {}) {
12778
12834
  return {
@@ -12791,8 +12847,8 @@ var QwencodeRule = class _QwencodeRule extends ToolRule {
12791
12847
  validate = true
12792
12848
  }) {
12793
12849
  const isRoot = relativeFilePath === "QWEN.md";
12794
- const relativePath = isRoot ? "QWEN.md" : join101(".qwen", "memories", relativeFilePath);
12795
- const fileContent = await readFileContent(join101(baseDir, relativePath));
12850
+ const relativePath = isRoot ? "QWEN.md" : join102(".qwen", "memories", relativeFilePath);
12851
+ const fileContent = await readFileContent(join102(baseDir, relativePath));
12796
12852
  return new _QwencodeRule({
12797
12853
  baseDir,
12798
12854
  relativeDirPath: isRoot ? this.getSettablePaths().root.relativeDirPath : this.getSettablePaths().nonRoot.relativeDirPath,
@@ -12844,7 +12900,7 @@ var QwencodeRule = class _QwencodeRule extends ToolRule {
12844
12900
  };
12845
12901
 
12846
12902
  // src/features/rules/replit-rule.ts
12847
- import { join as join102 } from "path";
12903
+ import { join as join103 } from "path";
12848
12904
  var ReplitRule = class _ReplitRule extends ToolRule {
12849
12905
  static getSettablePaths(_options = {}) {
12850
12906
  return {
@@ -12862,11 +12918,11 @@ var ReplitRule = class _ReplitRule extends ToolRule {
12862
12918
  const paths = this.getSettablePaths();
12863
12919
  const isRoot = relativeFilePath === paths.root.relativeFilePath;
12864
12920
  if (!isRoot) {
12865
- throw new Error("ReplitRule only supports root rules");
12921
+ throw new Error(`ReplitRule only supports root rules: ${relativeFilePath}`);
12866
12922
  }
12867
12923
  const relativePath = paths.root.relativeFilePath;
12868
12924
  const fileContent = await readFileContent(
12869
- join102(baseDir, paths.root.relativeDirPath, relativePath)
12925
+ join103(baseDir, paths.root.relativeDirPath, relativePath)
12870
12926
  );
12871
12927
  return new _ReplitRule({
12872
12928
  baseDir,
@@ -12885,7 +12941,7 @@ var ReplitRule = class _ReplitRule extends ToolRule {
12885
12941
  const paths = this.getSettablePaths();
12886
12942
  const isRoot = rulesyncRule.getFrontmatter().root ?? false;
12887
12943
  if (!isRoot) {
12888
- throw new Error("ReplitRule only supports root rules");
12944
+ throw new Error(`ReplitRule only supports root rules: ${rulesyncRule.getRelativeFilePath()}`);
12889
12945
  }
12890
12946
  return new _ReplitRule(
12891
12947
  this.buildToolRuleParamsDefault({
@@ -12932,7 +12988,7 @@ var ReplitRule = class _ReplitRule extends ToolRule {
12932
12988
  };
12933
12989
 
12934
12990
  // src/features/rules/roo-rule.ts
12935
- import { join as join103 } from "path";
12991
+ import { join as join104 } from "path";
12936
12992
  var RooRule = class _RooRule extends ToolRule {
12937
12993
  static getSettablePaths(_options = {}) {
12938
12994
  return {
@@ -12947,7 +13003,7 @@ var RooRule = class _RooRule extends ToolRule {
12947
13003
  validate = true
12948
13004
  }) {
12949
13005
  const fileContent = await readFileContent(
12950
- join103(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
13006
+ join104(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
12951
13007
  );
12952
13008
  return new _RooRule({
12953
13009
  baseDir,
@@ -13016,7 +13072,7 @@ var RooRule = class _RooRule extends ToolRule {
13016
13072
  };
13017
13073
 
13018
13074
  // src/features/rules/warp-rule.ts
13019
- import { join as join104 } from "path";
13075
+ import { join as join105 } from "path";
13020
13076
  var WarpRule = class _WarpRule extends ToolRule {
13021
13077
  constructor({ fileContent, root, ...rest }) {
13022
13078
  super({
@@ -13042,8 +13098,8 @@ var WarpRule = class _WarpRule extends ToolRule {
13042
13098
  validate = true
13043
13099
  }) {
13044
13100
  const isRoot = relativeFilePath === this.getSettablePaths().root.relativeFilePath;
13045
- const relativePath = isRoot ? this.getSettablePaths().root.relativeFilePath : join104(this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath);
13046
- const fileContent = await readFileContent(join104(baseDir, relativePath));
13101
+ const relativePath = isRoot ? this.getSettablePaths().root.relativeFilePath : join105(this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath);
13102
+ const fileContent = await readFileContent(join105(baseDir, relativePath));
13047
13103
  return new _WarpRule({
13048
13104
  baseDir,
13049
13105
  relativeDirPath: isRoot ? this.getSettablePaths().root.relativeDirPath : ".warp",
@@ -13098,7 +13154,7 @@ var WarpRule = class _WarpRule extends ToolRule {
13098
13154
  };
13099
13155
 
13100
13156
  // src/features/rules/windsurf-rule.ts
13101
- import { join as join105 } from "path";
13157
+ import { join as join106 } from "path";
13102
13158
  var WindsurfRule = class _WindsurfRule extends ToolRule {
13103
13159
  static getSettablePaths(_options = {}) {
13104
13160
  return {
@@ -13113,7 +13169,7 @@ var WindsurfRule = class _WindsurfRule extends ToolRule {
13113
13169
  validate = true
13114
13170
  }) {
13115
13171
  const fileContent = await readFileContent(
13116
- join105(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
13172
+ join106(baseDir, this.getSettablePaths().nonRoot.relativeDirPath, relativeFilePath)
13117
13173
  );
13118
13174
  return new _WindsurfRule({
13119
13175
  baseDir,
@@ -13499,7 +13555,7 @@ var RulesProcessor = class extends FeatureProcessor {
13499
13555
  }).relativeDirPath;
13500
13556
  return this.skills.filter((skill) => skillClass.isTargetedByRulesyncSkill(skill)).map((skill) => {
13501
13557
  const frontmatter = skill.getFrontmatter();
13502
- const relativePath = join106(toolRelativeDirPath, skill.getDirName(), SKILL_FILE_NAME);
13558
+ const relativePath = join107(toolRelativeDirPath, skill.getDirName(), SKILL_FILE_NAME);
13503
13559
  return {
13504
13560
  name: frontmatter.name,
13505
13561
  description: frontmatter.description,
@@ -13610,8 +13666,8 @@ var RulesProcessor = class extends FeatureProcessor {
13610
13666
  * Load and parse rulesync rule files from .rulesync/rules/ directory
13611
13667
  */
13612
13668
  async loadRulesyncFiles() {
13613
- const rulesyncBaseDir = join106(this.baseDir, RULESYNC_RULES_RELATIVE_DIR_PATH);
13614
- const files = await findFilesByGlobs(join106(rulesyncBaseDir, "**", "*.md"));
13669
+ const rulesyncBaseDir = join107(this.baseDir, RULESYNC_RULES_RELATIVE_DIR_PATH);
13670
+ const files = await findFilesByGlobs(join107(rulesyncBaseDir, "**", "*.md"));
13615
13671
  logger.debug(`Found ${files.length} rulesync files`);
13616
13672
  const rulesyncRules = await Promise.all(
13617
13673
  files.map((file) => {
@@ -13624,7 +13680,9 @@ var RulesProcessor = class extends FeatureProcessor {
13624
13680
  );
13625
13681
  const rootRules = rulesyncRules.filter((rule) => rule.getFrontmatter().root);
13626
13682
  if (rootRules.length > 1) {
13627
- throw new Error("Multiple root rulesync rules found");
13683
+ throw new Error(
13684
+ `Multiple root rulesync rules found: ${rootRules.map((r) => join107(r.getRelativeDirPath(), r.getRelativeFilePath())).join(", ")}`
13685
+ );
13628
13686
  }
13629
13687
  if (rootRules.length === 0 && rulesyncRules.length > 0) {
13630
13688
  logger.warn(
@@ -13633,21 +13691,25 @@ var RulesProcessor = class extends FeatureProcessor {
13633
13691
  }
13634
13692
  const localRootRules = rulesyncRules.filter((rule) => rule.getFrontmatter().localRoot);
13635
13693
  if (localRootRules.length > 1) {
13636
- throw new Error("Multiple localRoot rules found. Only one rule can have localRoot: true");
13694
+ throw new Error(
13695
+ `Multiple localRoot rules found: ${localRootRules.map((r) => join107(r.getRelativeDirPath(), r.getRelativeFilePath())).join(", ")}. Only one rule can have localRoot: true`
13696
+ );
13637
13697
  }
13638
13698
  if (localRootRules.length > 0 && rootRules.length === 0) {
13639
- throw new Error("localRoot: true requires a root: true rule to exist");
13699
+ throw new Error(
13700
+ `localRoot: true requires a root: true rule to exist (found in ${localRootRules.map((r) => join107(r.getRelativeDirPath(), r.getRelativeFilePath())).join(", ")})`
13701
+ );
13640
13702
  }
13641
13703
  if (this.global) {
13642
13704
  const nonRootRules = rulesyncRules.filter((rule) => !rule.getFrontmatter().root);
13643
13705
  if (nonRootRules.length > 0) {
13644
13706
  logger.warn(
13645
- `${nonRootRules.length} non-root rulesync rules found, but it's in global mode, so ignoring them`
13707
+ `${nonRootRules.length} non-root rulesync rules found, but it's in global mode, so ignoring them: ${nonRootRules.map((r) => join107(r.getRelativeDirPath(), r.getRelativeFilePath())).join(", ")}`
13646
13708
  );
13647
13709
  }
13648
13710
  if (localRootRules.length > 0) {
13649
13711
  logger.warn(
13650
- `${localRootRules.length} localRoot rules found, but localRoot is not supported in global mode, ignoring them`
13712
+ `${localRootRules.length} localRoot rules found, but localRoot is not supported in global mode, ignoring them: ${localRootRules.map((r) => join107(r.getRelativeDirPath(), r.getRelativeFilePath())).join(", ")}`
13651
13713
  );
13652
13714
  }
13653
13715
  return rootRules;
@@ -13669,7 +13731,7 @@ var RulesProcessor = class extends FeatureProcessor {
13669
13731
  return [];
13670
13732
  }
13671
13733
  const rootFilePaths = await findFilesByGlobs(
13672
- join106(
13734
+ join107(
13673
13735
  this.baseDir,
13674
13736
  settablePaths.root.relativeDirPath ?? ".",
13675
13737
  settablePaths.root.relativeFilePath
@@ -13680,7 +13742,7 @@ var RulesProcessor = class extends FeatureProcessor {
13680
13742
  (filePath) => factory.class.forDeletion({
13681
13743
  baseDir: this.baseDir,
13682
13744
  relativeDirPath: settablePaths.root?.relativeDirPath ?? ".",
13683
- relativeFilePath: basename23(filePath),
13745
+ relativeFilePath: basename24(filePath),
13684
13746
  global: this.global
13685
13747
  })
13686
13748
  ).filter((rule) => rule.isDeletable());
@@ -13689,7 +13751,7 @@ var RulesProcessor = class extends FeatureProcessor {
13689
13751
  rootFilePaths.map(
13690
13752
  (filePath) => factory.class.fromFile({
13691
13753
  baseDir: this.baseDir,
13692
- relativeFilePath: basename23(filePath),
13754
+ relativeFilePath: basename24(filePath),
13693
13755
  global: this.global
13694
13756
  })
13695
13757
  )
@@ -13707,13 +13769,13 @@ var RulesProcessor = class extends FeatureProcessor {
13707
13769
  return [];
13708
13770
  }
13709
13771
  const localRootFilePaths = await findFilesByGlobs(
13710
- join106(this.baseDir, settablePaths.root.relativeDirPath ?? ".", "CLAUDE.local.md")
13772
+ join107(this.baseDir, settablePaths.root.relativeDirPath ?? ".", "CLAUDE.local.md")
13711
13773
  );
13712
13774
  return localRootFilePaths.map(
13713
13775
  (filePath) => factory.class.forDeletion({
13714
13776
  baseDir: this.baseDir,
13715
13777
  relativeDirPath: settablePaths.root?.relativeDirPath ?? ".",
13716
- relativeFilePath: basename23(filePath),
13778
+ relativeFilePath: basename24(filePath),
13717
13779
  global: this.global
13718
13780
  })
13719
13781
  ).filter((rule) => rule.isDeletable());
@@ -13723,9 +13785,9 @@ var RulesProcessor = class extends FeatureProcessor {
13723
13785
  if (!settablePaths.nonRoot) {
13724
13786
  return [];
13725
13787
  }
13726
- const nonRootBaseDir = join106(this.baseDir, settablePaths.nonRoot.relativeDirPath);
13788
+ const nonRootBaseDir = join107(this.baseDir, settablePaths.nonRoot.relativeDirPath);
13727
13789
  const nonRootFilePaths = await findFilesByGlobs(
13728
- join106(nonRootBaseDir, "**", `*.${factory.meta.extension}`)
13790
+ join107(nonRootBaseDir, "**", `*.${factory.meta.extension}`)
13729
13791
  );
13730
13792
  if (forDeletion) {
13731
13793
  return nonRootFilePaths.map((filePath) => {
@@ -13757,7 +13819,7 @@ var RulesProcessor = class extends FeatureProcessor {
13757
13819
  logger.debug(`Found ${nonRootToolRules.length} non-root tool rule files`);
13758
13820
  return [...rootToolRules, ...localRootToolRules, ...nonRootToolRules];
13759
13821
  } catch (error) {
13760
- logger.error(`Failed to load tool files: ${formatError(error)}`);
13822
+ logger.error(`Failed to load tool files for ${this.toolTarget}: ${formatError(error)}`);
13761
13823
  return [];
13762
13824
  }
13763
13825
  }
@@ -13854,14 +13916,14 @@ s/<command> [arguments]
13854
13916
  This syntax employs a double slash (\`s/\`) to prevent conflicts with built-in slash commands.
13855
13917
  The \`s\` in \`s/\` stands for *simulate*. Because custom slash commands are not built-in, this syntax provides a pseudo way to invoke them.
13856
13918
 
13857
- When users call a custom slash command, you have to look for the markdown file, \`${join106(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, "{command}.md")}\`, then execute the contents of that file as the block of operations.` : "";
13919
+ When users call a custom slash command, you have to look for the markdown file, \`${join107(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, "{command}.md")}\`, then execute the contents of that file as the block of operations.` : "";
13858
13920
  const subagentsSection = subagents ? `## Simulated Subagents
13859
13921
 
13860
13922
  Simulated subagents are specialized AI assistants that can be invoked to handle specific types of tasks. In this case, it can be appear something like custom slash commands simply. Simulated subagents can be called by custom slash commands.
13861
13923
 
13862
- When users call a simulated subagent, it will look for the corresponding markdown file, \`${join106(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, "{subagent}.md")}\`, and execute its contents as the block of operations.
13924
+ When users call a simulated subagent, it will look for the corresponding markdown file, \`${join107(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, "{subagent}.md")}\`, and execute its contents as the block of operations.
13863
13925
 
13864
- For example, if the user instructs \`Call planner subagent to plan the refactoring\`, you have to look for the markdown file, \`${join106(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, "planner.md")}\`, and execute its contents as the block of operations.` : "";
13926
+ For example, if the user instructs \`Call planner subagent to plan the refactoring\`, you have to look for the markdown file, \`${join107(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, "planner.md")}\`, and execute its contents as the block of operations.` : "";
13865
13927
  const skillsSection = skills ? this.generateSkillsSection(skills) : "";
13866
13928
  const result = [
13867
13929
  overview,
@@ -13888,61 +13950,56 @@ ${toonContent}`;
13888
13950
  }
13889
13951
  };
13890
13952
 
13891
- // src/types/git-provider.ts
13892
- import { z as z49 } from "zod/mini";
13893
- var ALL_GIT_PROVIDERS = ["github", "gitlab"];
13894
- var GitProviderSchema = z49.enum(ALL_GIT_PROVIDERS);
13895
-
13896
13953
  // src/lib/github-client.ts
13897
13954
  import { RequestError } from "@octokit/request-error";
13898
13955
  import { Octokit } from "@octokit/rest";
13899
13956
 
13900
13957
  // src/types/fetch.ts
13901
- import { z as z51 } from "zod/mini";
13958
+ import { z as z50 } from "zod/mini";
13902
13959
 
13903
13960
  // src/types/fetch-targets.ts
13904
- import { z as z50 } from "zod/mini";
13961
+ import { z as z49 } from "zod/mini";
13905
13962
  var ALL_FETCH_TARGETS = ["rulesync", ...ALL_TOOL_TARGETS];
13906
- var FetchTargetSchema = z50.enum(ALL_FETCH_TARGETS);
13963
+ var FetchTargetSchema = z49.enum(ALL_FETCH_TARGETS);
13907
13964
 
13908
13965
  // src/types/fetch.ts
13909
- var ConflictStrategySchema = z51.enum(["skip", "overwrite"]);
13910
- var GitHubFileTypeSchema = z51.enum(["file", "dir", "symlink", "submodule"]);
13911
- var GitHubFileEntrySchema = z51.looseObject({
13912
- name: z51.string(),
13913
- path: z51.string(),
13914
- sha: z51.string(),
13915
- size: z51.number(),
13966
+ var ConflictStrategySchema = z50.enum(["skip", "overwrite"]);
13967
+ var GitHubFileTypeSchema = z50.enum(["file", "dir", "symlink", "submodule"]);
13968
+ var GitHubFileEntrySchema = z50.looseObject({
13969
+ name: z50.string(),
13970
+ path: z50.string(),
13971
+ sha: z50.string(),
13972
+ size: z50.number(),
13916
13973
  type: GitHubFileTypeSchema,
13917
- download_url: z51.nullable(z51.string())
13974
+ download_url: z50.nullable(z50.string())
13918
13975
  });
13919
- var FetchOptionsSchema = z51.looseObject({
13920
- target: z51.optional(FetchTargetSchema),
13921
- features: z51.optional(z51.array(z51.enum(ALL_FEATURES_WITH_WILDCARD))),
13922
- ref: z51.optional(z51.string()),
13923
- path: z51.optional(z51.string()),
13924
- output: z51.optional(z51.string()),
13925
- conflict: z51.optional(ConflictStrategySchema),
13926
- token: z51.optional(z51.string()),
13927
- verbose: z51.optional(z51.boolean()),
13928
- silent: z51.optional(z51.boolean())
13976
+ var FetchOptionsSchema = z50.looseObject({
13977
+ target: z50.optional(FetchTargetSchema),
13978
+ features: z50.optional(z50.array(z50.enum(ALL_FEATURES_WITH_WILDCARD))),
13979
+ ref: z50.optional(z50.string()),
13980
+ path: z50.optional(z50.string()),
13981
+ output: z50.optional(z50.string()),
13982
+ conflict: z50.optional(ConflictStrategySchema),
13983
+ token: z50.optional(z50.string()),
13984
+ verbose: z50.optional(z50.boolean()),
13985
+ silent: z50.optional(z50.boolean())
13929
13986
  });
13930
- var FetchFileStatusSchema = z51.enum(["created", "overwritten", "skipped"]);
13931
- var GitHubRepoInfoSchema = z51.looseObject({
13932
- default_branch: z51.string(),
13933
- private: z51.boolean()
13987
+ var FetchFileStatusSchema = z50.enum(["created", "overwritten", "skipped"]);
13988
+ var GitHubRepoInfoSchema = z50.looseObject({
13989
+ default_branch: z50.string(),
13990
+ private: z50.boolean()
13934
13991
  });
13935
- var GitHubReleaseAssetSchema = z51.looseObject({
13936
- name: z51.string(),
13937
- browser_download_url: z51.string(),
13938
- size: z51.number()
13992
+ var GitHubReleaseAssetSchema = z50.looseObject({
13993
+ name: z50.string(),
13994
+ browser_download_url: z50.string(),
13995
+ size: z50.number()
13939
13996
  });
13940
- var GitHubReleaseSchema = z51.looseObject({
13941
- tag_name: z51.string(),
13942
- name: z51.nullable(z51.string()),
13943
- prerelease: z51.boolean(),
13944
- draft: z51.boolean(),
13945
- assets: z51.array(GitHubReleaseAssetSchema)
13997
+ var GitHubReleaseSchema = z50.looseObject({
13998
+ tag_name: z50.string(),
13999
+ name: z50.nullable(z50.string()),
14000
+ prerelease: z50.boolean(),
14001
+ draft: z50.boolean(),
14002
+ assets: z50.array(GitHubReleaseAssetSchema)
13946
14003
  });
13947
14004
 
13948
14005
  // src/lib/github-client.ts
@@ -14110,6 +14167,21 @@ var GitHubClient = class {
14110
14167
  throw error;
14111
14168
  }
14112
14169
  }
14170
+ /**
14171
+ * Resolve a ref (branch, tag, or SHA) to a full commit SHA.
14172
+ */
14173
+ async resolveRefToSha(owner, repo, ref) {
14174
+ try {
14175
+ const { data } = await this.octokit.repos.getCommit({
14176
+ owner,
14177
+ repo,
14178
+ ref
14179
+ });
14180
+ return data.sha;
14181
+ } catch (error) {
14182
+ throw this.handleError(error);
14183
+ }
14184
+ }
14113
14185
  /**
14114
14186
  * Get the latest release from a repository
14115
14187
  */
@@ -14180,96 +14252,60 @@ var GitHubClient = class {
14180
14252
  }
14181
14253
  };
14182
14254
 
14183
- // src/lib/fetch.ts
14184
- var FEATURE_PATHS = {
14185
- rules: ["rules"],
14186
- commands: ["commands"],
14187
- subagents: ["subagents"],
14188
- skills: ["skills"],
14189
- ignore: [RULESYNC_AIIGNORE_FILE_NAME],
14190
- mcp: [RULESYNC_MCP_FILE_NAME],
14191
- hooks: [RULESYNC_HOOKS_FILE_NAME]
14192
- };
14193
- function isToolTarget(target) {
14194
- return target !== "rulesync";
14195
- }
14196
- function validateFileSize(relativePath, size) {
14197
- if (size > MAX_FILE_SIZE) {
14198
- throw new GitHubClientError(
14199
- `File "${relativePath}" exceeds maximum size limit (${(size / 1024 / 1024).toFixed(2)}MB > ${MAX_FILE_SIZE / 1024 / 1024}MB)`
14200
- );
14255
+ // src/lib/github-utils.ts
14256
+ var MAX_RECURSION_DEPTH = 15;
14257
+ async function withSemaphore(semaphore, fn) {
14258
+ await semaphore.acquire();
14259
+ try {
14260
+ return await fn();
14261
+ } finally {
14262
+ semaphore.release();
14201
14263
  }
14202
14264
  }
14203
- async function processFeatureConversion(params) {
14204
- const { processor, outputDir } = params;
14205
- const paths = [];
14206
- const toolFiles = await processor.loadToolFiles();
14207
- if (toolFiles.length === 0) {
14208
- return { paths: [] };
14265
+ async function listDirectoryRecursive(params) {
14266
+ const { client, owner, repo, path: path4, ref, depth = 0, semaphore } = params;
14267
+ if (depth > MAX_RECURSION_DEPTH) {
14268
+ throw new Error(
14269
+ `Maximum recursion depth (${MAX_RECURSION_DEPTH}) exceeded while listing directory: ${path4}`
14270
+ );
14209
14271
  }
14210
- const rulesyncFiles = await processor.convertToolFilesToRulesyncFiles(toolFiles);
14211
- for (const file of rulesyncFiles) {
14212
- const relativePath = join107(file.getRelativeDirPath(), file.getRelativeFilePath());
14213
- const outputPath = join107(outputDir, relativePath);
14214
- await writeFileContent(outputPath, file.getFileContent());
14215
- paths.push(relativePath);
14272
+ const entries = await withSemaphore(
14273
+ semaphore,
14274
+ () => client.listDirectory(owner, repo, path4, ref)
14275
+ );
14276
+ const files = [];
14277
+ const directories = [];
14278
+ for (const entry of entries) {
14279
+ if (entry.type === "file") {
14280
+ files.push(entry);
14281
+ } else if (entry.type === "dir") {
14282
+ directories.push(entry);
14283
+ }
14216
14284
  }
14217
- return { paths };
14218
- }
14219
- async function convertFetchedFilesToRulesync(params) {
14220
- const { tempDir, outputDir, target, features } = params;
14221
- const convertedPaths = [];
14222
- const featureConfigs = [
14223
- {
14224
- feature: "rules",
14225
- getTargets: () => RulesProcessor.getToolTargets({ global: false }),
14226
- createProcessor: () => new RulesProcessor({ baseDir: tempDir, toolTarget: target, global: false })
14227
- },
14228
- {
14229
- feature: "commands",
14230
- getTargets: () => CommandsProcessor.getToolTargets({ global: false, includeSimulated: false }),
14231
- createProcessor: () => new CommandsProcessor({ baseDir: tempDir, toolTarget: target, global: false })
14232
- },
14233
- {
14234
- feature: "subagents",
14235
- getTargets: () => SubagentsProcessor.getToolTargets({ global: false, includeSimulated: false }),
14236
- createProcessor: () => new SubagentsProcessor({ baseDir: tempDir, toolTarget: target, global: false })
14237
- },
14238
- {
14239
- feature: "ignore",
14240
- getTargets: () => IgnoreProcessor.getToolTargets(),
14241
- createProcessor: () => new IgnoreProcessor({ baseDir: tempDir, toolTarget: target })
14242
- },
14243
- {
14244
- feature: "mcp",
14245
- getTargets: () => McpProcessor.getToolTargets({ global: false }),
14246
- createProcessor: () => new McpProcessor({ baseDir: tempDir, toolTarget: target, global: false })
14247
- },
14248
- {
14249
- feature: "hooks",
14250
- getTargets: () => HooksProcessor.getToolTargets({ global: false }),
14251
- createProcessor: () => new HooksProcessor({ baseDir: tempDir, toolTarget: target, global: false })
14252
- }
14253
- ];
14254
- for (const config of featureConfigs) {
14255
- if (!features.includes(config.feature)) {
14256
- continue;
14257
- }
14258
- const supportedTargets = config.getTargets();
14259
- if (!supportedTargets.includes(target)) {
14260
- continue;
14261
- }
14262
- const processor = config.createProcessor();
14263
- const result = await processFeatureConversion({ processor, outputDir });
14264
- convertedPaths.push(...result.paths);
14265
- }
14266
- if (features.includes("skills")) {
14267
- logger.debug(
14268
- "Skills conversion is not yet supported in fetch command. Use import command instead."
14269
- );
14270
- }
14271
- return { converted: convertedPaths.length, convertedPaths };
14285
+ const subResults = await Promise.all(
14286
+ directories.map(
14287
+ (dir) => listDirectoryRecursive({
14288
+ client,
14289
+ owner,
14290
+ repo,
14291
+ path: dir.path,
14292
+ ref,
14293
+ depth: depth + 1,
14294
+ semaphore
14295
+ })
14296
+ )
14297
+ );
14298
+ return [...files, ...subResults.flat()];
14272
14299
  }
14300
+
14301
+ // src/types/git-provider.ts
14302
+ import { z as z51 } from "zod/mini";
14303
+ var ALL_GIT_PROVIDERS = ["github", "gitlab"];
14304
+ var GitProviderSchema = z51.enum(ALL_GIT_PROVIDERS);
14305
+
14306
+ // src/lib/source-parser.ts
14307
+ var GITHUB_HOSTS = /* @__PURE__ */ new Set(["github.com", "www.github.com"]);
14308
+ var GITLAB_HOSTS = /* @__PURE__ */ new Set(["gitlab.com", "www.gitlab.com"]);
14273
14309
  function parseSource(source) {
14274
14310
  if (source.startsWith("http://") || source.startsWith("https://")) {
14275
14311
  return parseUrl(source);
@@ -14286,18 +14322,6 @@ function parseSource(source) {
14286
14322
  }
14287
14323
  return { provider: "github", ...parseShorthand(source) };
14288
14324
  }
14289
- var GITHUB_HOSTS = /* @__PURE__ */ new Set(["github.com", "www.github.com"]);
14290
- var GITLAB_HOSTS = /* @__PURE__ */ new Set(["gitlab.com", "www.gitlab.com"]);
14291
- var MAX_RECURSION_DEPTH = 15;
14292
- var FETCH_CONCURRENCY_LIMIT = 10;
14293
- async function withSemaphore(semaphore, fn) {
14294
- await semaphore.acquire();
14295
- try {
14296
- return await fn();
14297
- } finally {
14298
- semaphore.release();
14299
- }
14300
- }
14301
14325
  function parseUrl(url) {
14302
14326
  const urlObj = new URL(url);
14303
14327
  const host = urlObj.hostname.toLowerCase();
@@ -14372,6 +14396,97 @@ function parseShorthand(source) {
14372
14396
  path: path4
14373
14397
  };
14374
14398
  }
14399
+
14400
+ // src/lib/fetch.ts
14401
+ var FEATURE_PATHS = {
14402
+ rules: ["rules"],
14403
+ commands: ["commands"],
14404
+ subagents: ["subagents"],
14405
+ skills: ["skills"],
14406
+ ignore: [RULESYNC_AIIGNORE_FILE_NAME],
14407
+ mcp: [RULESYNC_MCP_FILE_NAME],
14408
+ hooks: [RULESYNC_HOOKS_FILE_NAME]
14409
+ };
14410
+ function isToolTarget(target) {
14411
+ return target !== "rulesync";
14412
+ }
14413
+ function validateFileSize(relativePath, size) {
14414
+ if (size > MAX_FILE_SIZE) {
14415
+ throw new GitHubClientError(
14416
+ `File "${relativePath}" exceeds maximum size limit (${(size / 1024 / 1024).toFixed(2)}MB > ${MAX_FILE_SIZE / 1024 / 1024}MB)`
14417
+ );
14418
+ }
14419
+ }
14420
+ async function processFeatureConversion(params) {
14421
+ const { processor, outputDir } = params;
14422
+ const paths = [];
14423
+ const toolFiles = await processor.loadToolFiles();
14424
+ if (toolFiles.length === 0) {
14425
+ return { paths: [] };
14426
+ }
14427
+ const rulesyncFiles = await processor.convertToolFilesToRulesyncFiles(toolFiles);
14428
+ for (const file of rulesyncFiles) {
14429
+ const relativePath = join108(file.getRelativeDirPath(), file.getRelativeFilePath());
14430
+ const outputPath = join108(outputDir, relativePath);
14431
+ await writeFileContent(outputPath, file.getFileContent());
14432
+ paths.push(relativePath);
14433
+ }
14434
+ return { paths };
14435
+ }
14436
+ async function convertFetchedFilesToRulesync(params) {
14437
+ const { tempDir, outputDir, target, features } = params;
14438
+ const convertedPaths = [];
14439
+ const featureConfigs = [
14440
+ {
14441
+ feature: "rules",
14442
+ getTargets: () => RulesProcessor.getToolTargets({ global: false }),
14443
+ createProcessor: () => new RulesProcessor({ baseDir: tempDir, toolTarget: target, global: false })
14444
+ },
14445
+ {
14446
+ feature: "commands",
14447
+ getTargets: () => CommandsProcessor.getToolTargets({ global: false, includeSimulated: false }),
14448
+ createProcessor: () => new CommandsProcessor({ baseDir: tempDir, toolTarget: target, global: false })
14449
+ },
14450
+ {
14451
+ feature: "subagents",
14452
+ getTargets: () => SubagentsProcessor.getToolTargets({ global: false, includeSimulated: false }),
14453
+ createProcessor: () => new SubagentsProcessor({ baseDir: tempDir, toolTarget: target, global: false })
14454
+ },
14455
+ {
14456
+ feature: "ignore",
14457
+ getTargets: () => IgnoreProcessor.getToolTargets(),
14458
+ createProcessor: () => new IgnoreProcessor({ baseDir: tempDir, toolTarget: target })
14459
+ },
14460
+ {
14461
+ feature: "mcp",
14462
+ getTargets: () => McpProcessor.getToolTargets({ global: false }),
14463
+ createProcessor: () => new McpProcessor({ baseDir: tempDir, toolTarget: target, global: false })
14464
+ },
14465
+ {
14466
+ feature: "hooks",
14467
+ getTargets: () => HooksProcessor.getToolTargets({ global: false }),
14468
+ createProcessor: () => new HooksProcessor({ baseDir: tempDir, toolTarget: target, global: false })
14469
+ }
14470
+ ];
14471
+ for (const config of featureConfigs) {
14472
+ if (!features.includes(config.feature)) {
14473
+ continue;
14474
+ }
14475
+ const supportedTargets = config.getTargets();
14476
+ if (!supportedTargets.includes(target)) {
14477
+ continue;
14478
+ }
14479
+ const processor = config.createProcessor();
14480
+ const result = await processFeatureConversion({ processor, outputDir });
14481
+ convertedPaths.push(...result.paths);
14482
+ }
14483
+ if (features.includes("skills")) {
14484
+ logger.debug(
14485
+ "Skills conversion is not yet supported in fetch command. Use import command instead."
14486
+ );
14487
+ }
14488
+ return { converted: convertedPaths.length, convertedPaths };
14489
+ }
14375
14490
  function resolveFeatures(features) {
14376
14491
  if (!features || features.length === 0 || features.includes("*")) {
14377
14492
  return [...ALL_FEATURES];
@@ -14458,7 +14573,7 @@ async function fetchFiles(params) {
14458
14573
  skipped: 0
14459
14574
  };
14460
14575
  }
14461
- const outputBasePath = join107(baseDir, outputDir);
14576
+ const outputBasePath = join108(baseDir, outputDir);
14462
14577
  for (const { relativePath, size } of filesToFetch) {
14463
14578
  checkPathTraversal({
14464
14579
  relativePath,
@@ -14468,7 +14583,7 @@ async function fetchFiles(params) {
14468
14583
  }
14469
14584
  const results = await Promise.all(
14470
14585
  filesToFetch.map(async ({ remotePath, relativePath }) => {
14471
- const localPath = join107(outputBasePath, relativePath);
14586
+ const localPath = join108(outputBasePath, relativePath);
14472
14587
  const exists = await fileExists(localPath);
14473
14588
  if (exists && conflictStrategy === "skip") {
14474
14589
  logger.debug(`Skipping existing file: ${relativePath}`);
@@ -14510,7 +14625,7 @@ async function collectFeatureFiles(params) {
14510
14625
  );
14511
14626
  const results = await Promise.all(
14512
14627
  tasks.map(async ({ featurePath }) => {
14513
- const fullPath = basePath === "." || basePath === "" ? featurePath : join107(basePath, featurePath);
14628
+ const fullPath = basePath === "." || basePath === "" ? featurePath : join108(basePath, featurePath);
14514
14629
  const collected = [];
14515
14630
  try {
14516
14631
  if (featurePath.includes(".")) {
@@ -14563,41 +14678,6 @@ async function collectFeatureFiles(params) {
14563
14678
  );
14564
14679
  return results.flat();
14565
14680
  }
14566
- async function listDirectoryRecursive(params) {
14567
- const { client, owner, repo, path: path4, ref, depth = 0, semaphore } = params;
14568
- if (depth > MAX_RECURSION_DEPTH) {
14569
- throw new Error(
14570
- `Maximum recursion depth (${MAX_RECURSION_DEPTH}) exceeded while listing directory: ${path4}`
14571
- );
14572
- }
14573
- const entries = await withSemaphore(
14574
- semaphore,
14575
- () => client.listDirectory(owner, repo, path4, ref)
14576
- );
14577
- const files = [];
14578
- const directories = [];
14579
- for (const entry of entries) {
14580
- if (entry.type === "file") {
14581
- files.push(entry);
14582
- } else if (entry.type === "dir") {
14583
- directories.push(entry);
14584
- }
14585
- }
14586
- const subResults = await Promise.all(
14587
- directories.map(
14588
- (dir) => listDirectoryRecursive({
14589
- client,
14590
- owner,
14591
- repo,
14592
- path: dir.path,
14593
- ref,
14594
- depth: depth + 1,
14595
- semaphore
14596
- })
14597
- )
14598
- );
14599
- return [...files, ...subResults.flat()];
14600
- }
14601
14681
  async function fetchAndConvertToolFiles(params) {
14602
14682
  const {
14603
14683
  client,
@@ -14645,7 +14725,7 @@ async function fetchAndConvertToolFiles(params) {
14645
14725
  relativePath: toolRelativePath,
14646
14726
  intendedRootDir: tempDir
14647
14727
  });
14648
- const localPath = join107(tempDir, toolRelativePath);
14728
+ const localPath = join108(tempDir, toolRelativePath);
14649
14729
  const content = await withSemaphore(
14650
14730
  semaphore,
14651
14731
  () => client.getFileContent(parsed.owner, parsed.repo, remotePath, ref)
@@ -14654,7 +14734,7 @@ async function fetchAndConvertToolFiles(params) {
14654
14734
  logger.debug(`Fetched to temp: ${toolRelativePath}`);
14655
14735
  })
14656
14736
  );
14657
- const outputBasePath = join107(baseDir, outputDir);
14737
+ const outputBasePath = join108(baseDir, outputDir);
14658
14738
  const { converted, convertedPaths } = await convertFetchedFilesToRulesync({
14659
14739
  tempDir,
14660
14740
  outputDir: outputBasePath,
@@ -14665,7 +14745,7 @@ async function fetchAndConvertToolFiles(params) {
14665
14745
  relativePath,
14666
14746
  status: "created"
14667
14747
  }));
14668
- logger.info(`Converted ${converted} files from ${target} format to rulesync format`);
14748
+ logger.debug(`Converted ${converted} files from ${target} format to rulesync format`);
14669
14749
  return {
14670
14750
  source: `${parsed.owner}/${parsed.repo}`,
14671
14751
  ref,
@@ -14727,7 +14807,7 @@ function mapToToolPath(relativePath, toolPaths) {
14727
14807
  if (relativePath.startsWith("rules/")) {
14728
14808
  const restPath = relativePath.substring("rules/".length);
14729
14809
  if (toolPaths.rules?.nonRoot) {
14730
- return join107(toolPaths.rules.nonRoot, restPath);
14810
+ return join108(toolPaths.rules.nonRoot, restPath);
14731
14811
  }
14732
14812
  }
14733
14813
  if (toolPaths.rules?.root && relativePath === toolPaths.rules.root) {
@@ -14736,19 +14816,19 @@ function mapToToolPath(relativePath, toolPaths) {
14736
14816
  if (relativePath.startsWith("commands/")) {
14737
14817
  const restPath = relativePath.substring("commands/".length);
14738
14818
  if (toolPaths.commands) {
14739
- return join107(toolPaths.commands, restPath);
14819
+ return join108(toolPaths.commands, restPath);
14740
14820
  }
14741
14821
  }
14742
14822
  if (relativePath.startsWith("subagents/")) {
14743
14823
  const restPath = relativePath.substring("subagents/".length);
14744
14824
  if (toolPaths.subagents) {
14745
- return join107(toolPaths.subagents, restPath);
14825
+ return join108(toolPaths.subagents, restPath);
14746
14826
  }
14747
14827
  }
14748
14828
  if (relativePath.startsWith("skills/")) {
14749
14829
  const restPath = relativePath.substring("skills/".length);
14750
14830
  if (toolPaths.skills) {
14751
- return join107(toolPaths.skills, restPath);
14831
+ return join108(toolPaths.skills, restPath);
14752
14832
  }
14753
14833
  }
14754
14834
  return relativePath;
@@ -14778,7 +14858,7 @@ async function fetchCommand(options) {
14778
14858
  verbose: fetchOptions.verbose ?? false,
14779
14859
  silent: fetchOptions.silent ?? false
14780
14860
  });
14781
- logger.info(`Fetching files from ${source}...`);
14861
+ logger.debug(`Fetching files from ${source}...`);
14782
14862
  try {
14783
14863
  const summary = await fetchFiles({
14784
14864
  source,
@@ -14801,10 +14881,14 @@ async function fetchCommand(options) {
14801
14881
 
14802
14882
  // src/config/config-resolver.ts
14803
14883
  import { parse as parseJsonc } from "jsonc-parser";
14804
- import { dirname as dirname2, join as join108, resolve as resolve4 } from "path";
14884
+ import { dirname as dirname2, join as join109, resolve as resolve4 } from "path";
14805
14885
 
14806
14886
  // src/config/config.ts
14807
- import { optional as optional3, z as z52 } from "zod/mini";
14887
+ import { minLength, optional as optional3, z as z52 } from "zod/mini";
14888
+ var SourceEntrySchema = z52.object({
14889
+ source: z52.string().check(minLength(1, "source must be a non-empty string")),
14890
+ skills: optional3(z52.array(z52.string()))
14891
+ });
14808
14892
  var ConfigParamsSchema = z52.object({
14809
14893
  baseDirs: z52.array(z52.string()),
14810
14894
  targets: RulesyncTargetsSchema,
@@ -14818,7 +14902,9 @@ var ConfigParamsSchema = z52.object({
14818
14902
  simulateSubagents: optional3(z52.boolean()),
14819
14903
  simulateSkills: optional3(z52.boolean()),
14820
14904
  dryRun: optional3(z52.boolean()),
14821
- check: optional3(z52.boolean())
14905
+ check: optional3(z52.boolean()),
14906
+ // Declarative skill sources
14907
+ sources: optional3(z52.array(SourceEntrySchema))
14822
14908
  });
14823
14909
  var PartialConfigParamsSchema = z52.partial(ConfigParamsSchema);
14824
14910
  var ConfigFileSchema = z52.object({
@@ -14844,6 +14930,7 @@ var Config = class {
14844
14930
  simulateSkills;
14845
14931
  dryRun;
14846
14932
  check;
14933
+ sources;
14847
14934
  constructor({
14848
14935
  baseDirs,
14849
14936
  targets,
@@ -14856,7 +14943,8 @@ var Config = class {
14856
14943
  simulateSubagents,
14857
14944
  simulateSkills,
14858
14945
  dryRun,
14859
- check
14946
+ check,
14947
+ sources
14860
14948
  }) {
14861
14949
  this.validateConflictingTargets(targets);
14862
14950
  if (dryRun && check) {
@@ -14874,6 +14962,7 @@ var Config = class {
14874
14962
  this.simulateSkills = simulateSkills ?? false;
14875
14963
  this.dryRun = dryRun ?? false;
14876
14964
  this.check = check ?? false;
14965
+ this.sources = sources ?? [];
14877
14966
  }
14878
14967
  validateConflictingTargets(targets) {
14879
14968
  for (const [target1, target2] of CONFLICTING_TARGET_PAIRS) {
@@ -14964,6 +15053,9 @@ var Config = class {
14964
15053
  getCheck() {
14965
15054
  return this.check;
14966
15055
  }
15056
+ getSources() {
15057
+ return this.sources;
15058
+ }
14967
15059
  /**
14968
15060
  * Returns true if either dry-run or check mode is enabled.
14969
15061
  * In both modes, no files should be written.
@@ -14987,7 +15079,8 @@ var getDefaults = () => ({
14987
15079
  simulateSubagents: false,
14988
15080
  simulateSkills: false,
14989
15081
  dryRun: false,
14990
- check: false
15082
+ check: false,
15083
+ sources: []
14991
15084
  });
14992
15085
  var loadConfigFromFile = async (filePath) => {
14993
15086
  if (!await fileExists(filePath)) {
@@ -15017,7 +15110,8 @@ var mergeConfigs = (baseConfig, localConfig) => {
15017
15110
  simulateSubagents: localConfig.simulateSubagents ?? baseConfig.simulateSubagents,
15018
15111
  simulateSkills: localConfig.simulateSkills ?? baseConfig.simulateSkills,
15019
15112
  dryRun: localConfig.dryRun ?? baseConfig.dryRun,
15020
- check: localConfig.check ?? baseConfig.check
15113
+ check: localConfig.check ?? baseConfig.check,
15114
+ sources: localConfig.sources ?? baseConfig.sources
15021
15115
  };
15022
15116
  };
15023
15117
  var ConfigResolver = class {
@@ -15039,7 +15133,7 @@ var ConfigResolver = class {
15039
15133
  const validatedConfigPath = resolvePath(configPath, process.cwd());
15040
15134
  const baseConfig = await loadConfigFromFile(validatedConfigPath);
15041
15135
  const configDir = dirname2(validatedConfigPath);
15042
- const localConfigPath = join108(configDir, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH);
15136
+ const localConfigPath = join109(configDir, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH);
15043
15137
  const localConfig = await loadConfigFromFile(localConfigPath);
15044
15138
  const configByFile = mergeConfigs(baseConfig, localConfig);
15045
15139
  const resolvedGlobal = global ?? configByFile.global ?? getDefaults().global;
@@ -15061,7 +15155,8 @@ var ConfigResolver = class {
15061
15155
  simulateSubagents: resolvedSimulateSubagents,
15062
15156
  simulateSkills: resolvedSimulateSkills,
15063
15157
  dryRun: dryRun ?? configByFile.dryRun ?? getDefaults().dryRun,
15064
- check: check ?? configByFile.check ?? getDefaults().check
15158
+ check: check ?? configByFile.check ?? getDefaults().check,
15159
+ sources: configByFile.sources ?? getDefaults().sources
15065
15160
  };
15066
15161
  return new Config(configParams);
15067
15162
  }
@@ -15082,34 +15177,38 @@ function getBaseDirsInLightOfGlobal({
15082
15177
 
15083
15178
  // src/lib/generate.ts
15084
15179
  import { intersection } from "es-toolkit";
15085
- import { join as join109 } from "path";
15180
+ import { join as join110 } from "path";
15086
15181
  async function processFeatureGeneration(params) {
15087
15182
  const { config, processor, toolFiles } = params;
15088
15183
  let totalCount = 0;
15184
+ const allPaths = [];
15089
15185
  let hasDiff = false;
15090
- const writtenCount = await processor.writeAiFiles(toolFiles);
15091
- totalCount += writtenCount;
15092
- if (writtenCount > 0) hasDiff = true;
15186
+ const writeResult = await processor.writeAiFiles(toolFiles);
15187
+ totalCount += writeResult.count;
15188
+ allPaths.push(...writeResult.paths);
15189
+ if (writeResult.count > 0) hasDiff = true;
15093
15190
  if (config.getDelete()) {
15094
15191
  const existingToolFiles = await processor.loadToolFiles({ forDeletion: true });
15095
15192
  const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, toolFiles);
15096
15193
  if (orphanCount > 0) hasDiff = true;
15097
15194
  }
15098
- return { count: totalCount, hasDiff };
15195
+ return { count: totalCount, paths: allPaths, hasDiff };
15099
15196
  }
15100
15197
  async function processDirFeatureGeneration(params) {
15101
15198
  const { config, processor, toolDirs } = params;
15102
15199
  let totalCount = 0;
15200
+ const allPaths = [];
15103
15201
  let hasDiff = false;
15104
- const writtenCount = await processor.writeAiDirs(toolDirs);
15105
- totalCount += writtenCount;
15106
- if (writtenCount > 0) hasDiff = true;
15202
+ const writeResult = await processor.writeAiDirs(toolDirs);
15203
+ totalCount += writeResult.count;
15204
+ allPaths.push(...writeResult.paths);
15205
+ if (writeResult.count > 0) hasDiff = true;
15107
15206
  if (config.getDelete()) {
15108
15207
  const existingToolDirs = await processor.loadToolDirsToDelete();
15109
15208
  const orphanCount = await processor.removeOrphanAiDirs(existingToolDirs, toolDirs);
15110
15209
  if (orphanCount > 0) hasDiff = true;
15111
15210
  }
15112
- return { count: totalCount, hasDiff };
15211
+ return { count: totalCount, paths: allPaths, hasDiff };
15113
15212
  }
15114
15213
  async function processEmptyFeatureGeneration(params) {
15115
15214
  const { config, processor } = params;
@@ -15120,10 +15219,10 @@ async function processEmptyFeatureGeneration(params) {
15120
15219
  const orphanCount = await processor.removeOrphanAiFiles(existingToolFiles, []);
15121
15220
  if (orphanCount > 0) hasDiff = true;
15122
15221
  }
15123
- return { count: totalCount, hasDiff };
15222
+ return { count: totalCount, paths: [], hasDiff };
15124
15223
  }
15125
15224
  async function checkRulesyncDirExists(params) {
15126
- return fileExists(join109(params.baseDir, RULESYNC_RELATIVE_DIR_PATH));
15225
+ return fileExists(join110(params.baseDir, RULESYNC_RELATIVE_DIR_PATH));
15127
15226
  }
15128
15227
  async function generate(params) {
15129
15228
  const { config } = params;
@@ -15137,12 +15236,19 @@ async function generate(params) {
15137
15236
  const hasDiff = ignoreResult.hasDiff || mcpResult.hasDiff || commandsResult.hasDiff || subagentsResult.hasDiff || skillsResult.hasDiff || hooksResult.hasDiff || rulesResult.hasDiff;
15138
15237
  return {
15139
15238
  rulesCount: rulesResult.count,
15239
+ rulesPaths: rulesResult.paths,
15140
15240
  ignoreCount: ignoreResult.count,
15241
+ ignorePaths: ignoreResult.paths,
15141
15242
  mcpCount: mcpResult.count,
15243
+ mcpPaths: mcpResult.paths,
15142
15244
  commandsCount: commandsResult.count,
15245
+ commandsPaths: commandsResult.paths,
15143
15246
  subagentsCount: subagentsResult.count,
15247
+ subagentsPaths: subagentsResult.paths,
15144
15248
  skillsCount: skillsResult.count,
15249
+ skillsPaths: skillsResult.paths,
15145
15250
  hooksCount: hooksResult.count,
15251
+ hooksPaths: hooksResult.paths,
15146
15252
  skills: skillsResult.skills,
15147
15253
  hasDiff
15148
15254
  };
@@ -15150,6 +15256,7 @@ async function generate(params) {
15150
15256
  async function generateRulesCore(params) {
15151
15257
  const { config, skills } = params;
15152
15258
  let totalCount = 0;
15259
+ const allPaths = [];
15153
15260
  let hasDiff = false;
15154
15261
  const toolTargets = intersection(
15155
15262
  config.getTargets(),
@@ -15178,17 +15285,19 @@ async function generateRulesCore(params) {
15178
15285
  toolFiles
15179
15286
  });
15180
15287
  totalCount += result.count;
15288
+ allPaths.push(...result.paths);
15181
15289
  if (result.hasDiff) hasDiff = true;
15182
15290
  }
15183
15291
  }
15184
- return { count: totalCount, hasDiff };
15292
+ return { count: totalCount, paths: allPaths, hasDiff };
15185
15293
  }
15186
15294
  async function generateIgnoreCore(params) {
15187
15295
  const { config } = params;
15188
15296
  if (config.getGlobal()) {
15189
- return { count: 0, hasDiff: false };
15297
+ return { count: 0, paths: [], hasDiff: false };
15190
15298
  }
15191
15299
  let totalCount = 0;
15300
+ const allPaths = [];
15192
15301
  let hasDiff = false;
15193
15302
  for (const toolTarget of intersection(config.getTargets(), IgnoreProcessor.getToolTargets())) {
15194
15303
  if (!config.getFeatures(toolTarget).includes("ignore")) {
@@ -15217,6 +15326,7 @@ async function generateIgnoreCore(params) {
15217
15326
  });
15218
15327
  }
15219
15328
  totalCount += result.count;
15329
+ allPaths.push(...result.paths);
15220
15330
  if (result.hasDiff) hasDiff = true;
15221
15331
  } catch (error) {
15222
15332
  logger.warn(
@@ -15226,11 +15336,12 @@ async function generateIgnoreCore(params) {
15226
15336
  }
15227
15337
  }
15228
15338
  }
15229
- return { count: totalCount, hasDiff };
15339
+ return { count: totalCount, paths: allPaths, hasDiff };
15230
15340
  }
15231
15341
  async function generateMcpCore(params) {
15232
15342
  const { config } = params;
15233
15343
  let totalCount = 0;
15344
+ const allPaths = [];
15234
15345
  let hasDiff = false;
15235
15346
  const toolTargets = intersection(
15236
15347
  config.getTargets(),
@@ -15255,14 +15366,16 @@ async function generateMcpCore(params) {
15255
15366
  toolFiles
15256
15367
  });
15257
15368
  totalCount += result.count;
15369
+ allPaths.push(...result.paths);
15258
15370
  if (result.hasDiff) hasDiff = true;
15259
15371
  }
15260
15372
  }
15261
- return { count: totalCount, hasDiff };
15373
+ return { count: totalCount, paths: allPaths, hasDiff };
15262
15374
  }
15263
15375
  async function generateCommandsCore(params) {
15264
15376
  const { config } = params;
15265
15377
  let totalCount = 0;
15378
+ const allPaths = [];
15266
15379
  let hasDiff = false;
15267
15380
  const toolTargets = intersection(
15268
15381
  config.getTargets(),
@@ -15290,14 +15403,16 @@ async function generateCommandsCore(params) {
15290
15403
  toolFiles
15291
15404
  });
15292
15405
  totalCount += result.count;
15406
+ allPaths.push(...result.paths);
15293
15407
  if (result.hasDiff) hasDiff = true;
15294
15408
  }
15295
15409
  }
15296
- return { count: totalCount, hasDiff };
15410
+ return { count: totalCount, paths: allPaths, hasDiff };
15297
15411
  }
15298
15412
  async function generateSubagentsCore(params) {
15299
15413
  const { config } = params;
15300
15414
  let totalCount = 0;
15415
+ const allPaths = [];
15301
15416
  let hasDiff = false;
15302
15417
  const toolTargets = intersection(
15303
15418
  config.getTargets(),
@@ -15325,14 +15440,16 @@ async function generateSubagentsCore(params) {
15325
15440
  toolFiles
15326
15441
  });
15327
15442
  totalCount += result.count;
15443
+ allPaths.push(...result.paths);
15328
15444
  if (result.hasDiff) hasDiff = true;
15329
15445
  }
15330
15446
  }
15331
- return { count: totalCount, hasDiff };
15447
+ return { count: totalCount, paths: allPaths, hasDiff };
15332
15448
  }
15333
15449
  async function generateSkillsCore(params) {
15334
15450
  const { config } = params;
15335
15451
  let totalCount = 0;
15452
+ const allPaths = [];
15336
15453
  let hasDiff = false;
15337
15454
  const allSkills = [];
15338
15455
  const toolTargets = intersection(
@@ -15366,14 +15483,16 @@ async function generateSkillsCore(params) {
15366
15483
  toolDirs
15367
15484
  });
15368
15485
  totalCount += result.count;
15486
+ allPaths.push(...result.paths);
15369
15487
  if (result.hasDiff) hasDiff = true;
15370
15488
  }
15371
15489
  }
15372
- return { count: totalCount, skills: allSkills, hasDiff };
15490
+ return { count: totalCount, paths: allPaths, skills: allSkills, hasDiff };
15373
15491
  }
15374
15492
  async function generateHooksCore(params) {
15375
15493
  const { config } = params;
15376
15494
  let totalCount = 0;
15495
+ const allPaths = [];
15377
15496
  let hasDiff = false;
15378
15497
  const toolTargets = intersection(
15379
15498
  config.getTargets(),
@@ -15406,10 +15525,11 @@ async function generateHooksCore(params) {
15406
15525
  });
15407
15526
  }
15408
15527
  totalCount += result.count;
15528
+ allPaths.push(...result.paths);
15409
15529
  if (result.hasDiff) hasDiff = true;
15410
15530
  }
15411
15531
  }
15412
- return { count: totalCount, hasDiff };
15532
+ return { count: totalCount, paths: allPaths, hasDiff };
15413
15533
  }
15414
15534
 
15415
15535
  // src/utils/result.ts
@@ -15419,13 +15539,16 @@ function calculateTotalCount(result) {
15419
15539
 
15420
15540
  // src/cli/commands/generate.ts
15421
15541
  function logFeatureResult(params) {
15422
- const { count, featureName, isPreview, modePrefix } = params;
15542
+ const { count, paths, featureName, isPreview, modePrefix } = params;
15423
15543
  if (count > 0) {
15424
15544
  if (isPreview) {
15425
15545
  logger.info(`${modePrefix} Would write ${count} ${featureName}`);
15426
15546
  } else {
15427
15547
  logger.success(`Written ${count} ${featureName}`);
15428
15548
  }
15549
+ for (const p of paths) {
15550
+ logger.info(` ${p}`);
15551
+ }
15429
15552
  }
15430
15553
  }
15431
15554
  async function generateCommand(options) {
@@ -15437,73 +15560,80 @@ async function generateCommand(options) {
15437
15560
  const check = config.getCheck();
15438
15561
  const isPreview = config.isPreviewMode();
15439
15562
  const modePrefix = isPreview ? "[DRY RUN]" : "";
15440
- logger.info("Generating files...");
15563
+ logger.debug("Generating files...");
15441
15564
  if (!await checkRulesyncDirExists({ baseDir: process.cwd() })) {
15442
15565
  logger.error("\u274C .rulesync directory not found. Run 'rulesync init' first.");
15443
15566
  process.exit(1);
15444
15567
  }
15445
- logger.info(`Base directories: ${config.getBaseDirs().join(", ")}`);
15568
+ logger.debug(`Base directories: ${config.getBaseDirs().join(", ")}`);
15446
15569
  const features = config.getFeatures();
15447
15570
  if (features.includes("ignore")) {
15448
- logger.info("Generating ignore files...");
15571
+ logger.debug("Generating ignore files...");
15449
15572
  }
15450
15573
  if (features.includes("mcp")) {
15451
- logger.info("Generating MCP files...");
15574
+ logger.debug("Generating MCP files...");
15452
15575
  }
15453
15576
  if (features.includes("commands")) {
15454
- logger.info("Generating command files...");
15577
+ logger.debug("Generating command files...");
15455
15578
  }
15456
15579
  if (features.includes("subagents")) {
15457
- logger.info("Generating subagent files...");
15580
+ logger.debug("Generating subagent files...");
15458
15581
  }
15459
15582
  if (features.includes("skills")) {
15460
- logger.info("Generating skill files...");
15583
+ logger.debug("Generating skill files...");
15461
15584
  }
15462
15585
  if (features.includes("hooks")) {
15463
- logger.info("Generating hooks...");
15586
+ logger.debug("Generating hooks...");
15464
15587
  }
15465
15588
  if (features.includes("rules")) {
15466
- logger.info("Generating rule files...");
15589
+ logger.debug("Generating rule files...");
15467
15590
  }
15468
15591
  const result = await generate({ config });
15469
15592
  logFeatureResult({
15470
15593
  count: result.ignoreCount,
15594
+ paths: result.ignorePaths,
15471
15595
  featureName: "ignore file(s)",
15472
15596
  isPreview,
15473
15597
  modePrefix
15474
15598
  });
15475
15599
  logFeatureResult({
15476
15600
  count: result.mcpCount,
15601
+ paths: result.mcpPaths,
15477
15602
  featureName: "MCP configuration(s)",
15478
15603
  isPreview,
15479
15604
  modePrefix
15480
15605
  });
15481
15606
  logFeatureResult({
15482
15607
  count: result.commandsCount,
15608
+ paths: result.commandsPaths,
15483
15609
  featureName: "command(s)",
15484
15610
  isPreview,
15485
15611
  modePrefix
15486
15612
  });
15487
15613
  logFeatureResult({
15488
15614
  count: result.subagentsCount,
15615
+ paths: result.subagentsPaths,
15489
15616
  featureName: "subagent(s)",
15490
15617
  isPreview,
15491
15618
  modePrefix
15492
15619
  });
15493
15620
  logFeatureResult({
15494
15621
  count: result.skillsCount,
15622
+ paths: result.skillsPaths,
15495
15623
  featureName: "skill(s)",
15496
15624
  isPreview,
15497
15625
  modePrefix
15498
15626
  });
15499
15627
  logFeatureResult({
15500
15628
  count: result.hooksCount,
15629
+ paths: result.hooksPaths,
15501
15630
  featureName: "hooks file(s)",
15502
15631
  isPreview,
15503
15632
  modePrefix
15504
15633
  });
15505
15634
  logFeatureResult({
15506
15635
  count: result.rulesCount,
15636
+ paths: result.rulesPaths,
15507
15637
  featureName: "rule(s)",
15508
15638
  isPreview,
15509
15639
  modePrefix
@@ -15538,10 +15668,12 @@ async function generateCommand(options) {
15538
15668
  }
15539
15669
 
15540
15670
  // src/cli/commands/gitignore.ts
15541
- import { join as join110 } from "path";
15671
+ import { join as join111 } from "path";
15542
15672
  var RULESYNC_HEADER = "# Generated by Rulesync";
15543
15673
  var LEGACY_RULESYNC_HEADER = "# Generated by rulesync - AI tool configuration files";
15544
15674
  var RULESYNC_IGNORE_ENTRIES = [
15675
+ // Rulesync curated (fetched) skills
15676
+ ".rulesync/skills/.curated/",
15545
15677
  // AGENTS.md
15546
15678
  "**/AGENTS.md",
15547
15679
  "**/.agents/",
@@ -15686,7 +15818,7 @@ var removeExistingRulesyncEntries = (content) => {
15686
15818
  return result;
15687
15819
  };
15688
15820
  var gitignoreCommand = async () => {
15689
- const gitignorePath = join110(process.cwd(), ".gitignore");
15821
+ const gitignorePath = join111(process.cwd(), ".gitignore");
15690
15822
  let gitignoreContent = "";
15691
15823
  if (await fileExists(gitignorePath)) {
15692
15824
  gitignoreContent = await readFileContent(gitignorePath);
@@ -15758,7 +15890,7 @@ async function importRulesCore(params) {
15758
15890
  return 0;
15759
15891
  }
15760
15892
  const rulesyncFiles = await rulesProcessor.convertToolFilesToRulesyncFiles(toolFiles);
15761
- const writtenCount = await rulesProcessor.writeAiFiles(rulesyncFiles);
15893
+ const { count: writtenCount } = await rulesProcessor.writeAiFiles(rulesyncFiles);
15762
15894
  if (config.getVerbose() && writtenCount > 0) {
15763
15895
  logger.success(`Created ${writtenCount} rule files`);
15764
15896
  }
@@ -15785,7 +15917,7 @@ async function importIgnoreCore(params) {
15785
15917
  return 0;
15786
15918
  }
15787
15919
  const rulesyncFiles = await ignoreProcessor.convertToolFilesToRulesyncFiles(toolFiles);
15788
- const writtenCount = await ignoreProcessor.writeAiFiles(rulesyncFiles);
15920
+ const { count: writtenCount } = await ignoreProcessor.writeAiFiles(rulesyncFiles);
15789
15921
  if (config.getVerbose()) {
15790
15922
  logger.success(`Created ignore files from ${toolFiles.length} tool ignore configurations`);
15791
15923
  }
@@ -15814,7 +15946,7 @@ async function importMcpCore(params) {
15814
15946
  return 0;
15815
15947
  }
15816
15948
  const rulesyncFiles = await mcpProcessor.convertToolFilesToRulesyncFiles(toolFiles);
15817
- const writtenCount = await mcpProcessor.writeAiFiles(rulesyncFiles);
15949
+ const { count: writtenCount } = await mcpProcessor.writeAiFiles(rulesyncFiles);
15818
15950
  if (config.getVerbose() && writtenCount > 0) {
15819
15951
  logger.success(`Created ${writtenCount} MCP files`);
15820
15952
  }
@@ -15840,7 +15972,7 @@ async function importCommandsCore(params) {
15840
15972
  return 0;
15841
15973
  }
15842
15974
  const rulesyncFiles = await commandsProcessor.convertToolFilesToRulesyncFiles(toolFiles);
15843
- const writtenCount = await commandsProcessor.writeAiFiles(rulesyncFiles);
15975
+ const { count: writtenCount } = await commandsProcessor.writeAiFiles(rulesyncFiles);
15844
15976
  if (config.getVerbose() && writtenCount > 0) {
15845
15977
  logger.success(`Created ${writtenCount} command files`);
15846
15978
  }
@@ -15866,7 +15998,7 @@ async function importSubagentsCore(params) {
15866
15998
  return 0;
15867
15999
  }
15868
16000
  const rulesyncFiles = await subagentsProcessor.convertToolFilesToRulesyncFiles(toolFiles);
15869
- const writtenCount = await subagentsProcessor.writeAiFiles(rulesyncFiles);
16001
+ const { count: writtenCount } = await subagentsProcessor.writeAiFiles(rulesyncFiles);
15870
16002
  if (config.getVerbose() && writtenCount > 0) {
15871
16003
  logger.success(`Created ${writtenCount} subagent files`);
15872
16004
  }
@@ -15892,7 +16024,7 @@ async function importSkillsCore(params) {
15892
16024
  return 0;
15893
16025
  }
15894
16026
  const rulesyncDirs = await skillsProcessor.convertToolDirsToRulesyncDirs(toolDirs);
15895
- const writtenCount = await skillsProcessor.writeAiDirs(rulesyncDirs);
16027
+ const { count: writtenCount } = await skillsProcessor.writeAiDirs(rulesyncDirs);
15896
16028
  if (config.getVerbose() && writtenCount > 0) {
15897
16029
  logger.success(`Created ${writtenCount} skill directories`);
15898
16030
  }
@@ -15923,7 +16055,7 @@ async function importHooksCore(params) {
15923
16055
  return 0;
15924
16056
  }
15925
16057
  const rulesyncFiles = await hooksProcessor.convertToolFilesToRulesyncFiles(toolFiles);
15926
- const writtenCount = await hooksProcessor.writeAiFiles(rulesyncFiles);
16058
+ const { count: writtenCount } = await hooksProcessor.writeAiFiles(rulesyncFiles);
15927
16059
  if (config.getVerbose() && writtenCount > 0) {
15928
16060
  logger.success(`Created ${writtenCount} hooks file(s)`);
15929
16061
  }
@@ -15946,7 +16078,7 @@ async function importCommand(options) {
15946
16078
  silent: config.getSilent()
15947
16079
  });
15948
16080
  const tool = config.getTargets()[0];
15949
- logger.info(`Importing files from ${tool}...`);
16081
+ logger.debug(`Importing files from ${tool}...`);
15950
16082
  const result = await importFromTool({ config, tool });
15951
16083
  const totalImported = calculateTotalCount(result);
15952
16084
  if (totalImported === 0) {
@@ -15966,7 +16098,7 @@ async function importCommand(options) {
15966
16098
  }
15967
16099
 
15968
16100
  // src/lib/init.ts
15969
- import { join as join111 } from "path";
16101
+ import { join as join112 } from "path";
15970
16102
  async function init() {
15971
16103
  const sampleFiles = await createSampleFiles();
15972
16104
  const configFile = await createConfigFile();
@@ -16156,27 +16288,27 @@ Keep the summary concise and ready to reuse in future tasks.`
16156
16288
  await ensureDir(subagentPaths.relativeDirPath);
16157
16289
  await ensureDir(skillPaths.relativeDirPath);
16158
16290
  await ensureDir(ignorePaths.recommended.relativeDirPath);
16159
- const ruleFilepath = join111(rulePaths.recommended.relativeDirPath, sampleRuleFile.filename);
16291
+ const ruleFilepath = join112(rulePaths.recommended.relativeDirPath, sampleRuleFile.filename);
16160
16292
  results.push(await writeIfNotExists(ruleFilepath, sampleRuleFile.content));
16161
- const mcpFilepath = join111(
16293
+ const mcpFilepath = join112(
16162
16294
  mcpPaths.recommended.relativeDirPath,
16163
16295
  mcpPaths.recommended.relativeFilePath
16164
16296
  );
16165
16297
  results.push(await writeIfNotExists(mcpFilepath, sampleMcpFile.content));
16166
- const commandFilepath = join111(commandPaths.relativeDirPath, sampleCommandFile.filename);
16298
+ const commandFilepath = join112(commandPaths.relativeDirPath, sampleCommandFile.filename);
16167
16299
  results.push(await writeIfNotExists(commandFilepath, sampleCommandFile.content));
16168
- const subagentFilepath = join111(subagentPaths.relativeDirPath, sampleSubagentFile.filename);
16300
+ const subagentFilepath = join112(subagentPaths.relativeDirPath, sampleSubagentFile.filename);
16169
16301
  results.push(await writeIfNotExists(subagentFilepath, sampleSubagentFile.content));
16170
- const skillDirPath = join111(skillPaths.relativeDirPath, sampleSkillFile.dirName);
16302
+ const skillDirPath = join112(skillPaths.relativeDirPath, sampleSkillFile.dirName);
16171
16303
  await ensureDir(skillDirPath);
16172
- const skillFilepath = join111(skillDirPath, SKILL_FILE_NAME);
16304
+ const skillFilepath = join112(skillDirPath, SKILL_FILE_NAME);
16173
16305
  results.push(await writeIfNotExists(skillFilepath, sampleSkillFile.content));
16174
- const ignoreFilepath = join111(
16306
+ const ignoreFilepath = join112(
16175
16307
  ignorePaths.recommended.relativeDirPath,
16176
16308
  ignorePaths.recommended.relativeFilePath
16177
16309
  );
16178
16310
  results.push(await writeIfNotExists(ignoreFilepath, sampleIgnoreFile.content));
16179
- const hooksFilepath = join111(hooksPaths.relativeDirPath, hooksPaths.relativeFilePath);
16311
+ const hooksFilepath = join112(hooksPaths.relativeDirPath, hooksPaths.relativeFilePath);
16180
16312
  results.push(await writeIfNotExists(hooksFilepath, sampleHooksFile.content));
16181
16313
  return results;
16182
16314
  }
@@ -16190,7 +16322,7 @@ async function writeIfNotExists(path4, content) {
16190
16322
 
16191
16323
  // src/cli/commands/init.ts
16192
16324
  async function initCommand() {
16193
- logger.info("Initializing rulesync...");
16325
+ logger.debug("Initializing rulesync...");
16194
16326
  await ensureDir(RULESYNC_RELATIVE_DIR_PATH);
16195
16327
  const result = await init();
16196
16328
  for (const file of result.sampleFiles) {
@@ -16213,19 +16345,447 @@ async function initCommand() {
16213
16345
  logger.info("2. Run 'rulesync generate' to create configuration files");
16214
16346
  }
16215
16347
 
16348
+ // src/lib/sources.ts
16349
+ import { Semaphore as Semaphore2 } from "es-toolkit/promise";
16350
+ import { join as join114, resolve as resolve5, sep } from "path";
16351
+
16352
+ // src/lib/sources-lock.ts
16353
+ import { createHash } from "crypto";
16354
+ import { join as join113 } from "path";
16355
+ import { optional as optional4, z as z53 } from "zod/mini";
16356
+ var LOCKFILE_VERSION = 1;
16357
+ var LockedSkillSchema = z53.object({
16358
+ integrity: z53.string()
16359
+ });
16360
+ var LockedSourceSchema = z53.object({
16361
+ requestedRef: optional4(z53.string()),
16362
+ resolvedRef: z53.string(),
16363
+ resolvedAt: optional4(z53.string()),
16364
+ skills: z53.record(z53.string(), LockedSkillSchema)
16365
+ });
16366
+ var SourcesLockSchema = z53.object({
16367
+ lockfileVersion: z53.number(),
16368
+ sources: z53.record(z53.string(), LockedSourceSchema)
16369
+ });
16370
+ var LegacyLockedSourceSchema = z53.object({
16371
+ resolvedRef: z53.string(),
16372
+ skills: z53.array(z53.string())
16373
+ });
16374
+ var LegacySourcesLockSchema = z53.object({
16375
+ sources: z53.record(z53.string(), LegacyLockedSourceSchema)
16376
+ });
16377
+ function migrateLegacyLock(legacy) {
16378
+ const sources = {};
16379
+ for (const [key, entry] of Object.entries(legacy.sources)) {
16380
+ const skills = {};
16381
+ for (const name of entry.skills) {
16382
+ skills[name] = { integrity: "" };
16383
+ }
16384
+ sources[key] = {
16385
+ resolvedRef: entry.resolvedRef,
16386
+ skills
16387
+ };
16388
+ }
16389
+ logger.info(
16390
+ "Migrated legacy sources lockfile to version 1. Run 'rulesync install --update' to populate integrity hashes."
16391
+ );
16392
+ return { lockfileVersion: LOCKFILE_VERSION, sources };
16393
+ }
16394
+ function createEmptyLock() {
16395
+ return { lockfileVersion: LOCKFILE_VERSION, sources: {} };
16396
+ }
16397
+ async function readLockFile(params) {
16398
+ const lockPath = join113(params.baseDir, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);
16399
+ if (!await fileExists(lockPath)) {
16400
+ logger.debug("No sources lockfile found, starting fresh.");
16401
+ return createEmptyLock();
16402
+ }
16403
+ try {
16404
+ const content = await readFileContent(lockPath);
16405
+ const data = JSON.parse(content);
16406
+ const result = SourcesLockSchema.safeParse(data);
16407
+ if (result.success) {
16408
+ return result.data;
16409
+ }
16410
+ const legacyResult = LegacySourcesLockSchema.safeParse(data);
16411
+ if (legacyResult.success) {
16412
+ return migrateLegacyLock(legacyResult.data);
16413
+ }
16414
+ logger.warn(
16415
+ `Invalid sources lockfile format (${RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH}). Starting fresh.`
16416
+ );
16417
+ return createEmptyLock();
16418
+ } catch {
16419
+ logger.warn(
16420
+ `Failed to read sources lockfile (${RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH}). Starting fresh.`
16421
+ );
16422
+ return createEmptyLock();
16423
+ }
16424
+ }
16425
+ async function writeLockFile(params) {
16426
+ const lockPath = join113(params.baseDir, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);
16427
+ const content = JSON.stringify(params.lock, null, 2) + "\n";
16428
+ await writeFileContent(lockPath, content);
16429
+ logger.debug(`Wrote sources lockfile to ${lockPath}`);
16430
+ }
16431
+ function computeSkillIntegrity(files) {
16432
+ const hash = createHash("sha256");
16433
+ const sorted = files.toSorted((a, b) => a.path.localeCompare(b.path));
16434
+ for (const file of sorted) {
16435
+ hash.update(file.path);
16436
+ hash.update("\0");
16437
+ hash.update(file.content);
16438
+ hash.update("\0");
16439
+ }
16440
+ return `sha256-${hash.digest("hex")}`;
16441
+ }
16442
+ function normalizeSourceKey(source) {
16443
+ let key = source;
16444
+ for (const prefix of [
16445
+ "https://www.github.com/",
16446
+ "https://github.com/",
16447
+ "http://www.github.com/",
16448
+ "http://github.com/"
16449
+ ]) {
16450
+ if (key.toLowerCase().startsWith(prefix)) {
16451
+ key = key.substring(prefix.length);
16452
+ break;
16453
+ }
16454
+ }
16455
+ if (key.startsWith("github:")) {
16456
+ key = key.substring("github:".length);
16457
+ }
16458
+ key = key.replace(/\/+$/, "");
16459
+ key = key.replace(/\.git$/, "");
16460
+ key = key.toLowerCase();
16461
+ return key;
16462
+ }
16463
+ function getLockedSource(lock, sourceKey) {
16464
+ const normalized = normalizeSourceKey(sourceKey);
16465
+ for (const [key, value] of Object.entries(lock.sources)) {
16466
+ if (normalizeSourceKey(key) === normalized) {
16467
+ return value;
16468
+ }
16469
+ }
16470
+ return void 0;
16471
+ }
16472
+ function setLockedSource(lock, sourceKey, entry) {
16473
+ const normalized = normalizeSourceKey(sourceKey);
16474
+ const filteredSources = {};
16475
+ for (const [key, value] of Object.entries(lock.sources)) {
16476
+ if (normalizeSourceKey(key) !== normalized) {
16477
+ filteredSources[key] = value;
16478
+ }
16479
+ }
16480
+ return {
16481
+ lockfileVersion: lock.lockfileVersion,
16482
+ sources: {
16483
+ ...filteredSources,
16484
+ [normalized]: entry
16485
+ }
16486
+ };
16487
+ }
16488
+ function getLockedSkillNames(entry) {
16489
+ return Object.keys(entry.skills);
16490
+ }
16491
+
16492
+ // src/lib/sources.ts
16493
+ async function resolveAndFetchSources(params) {
16494
+ const { sources, baseDir, options = {} } = params;
16495
+ if (sources.length === 0) {
16496
+ return { fetchedSkillCount: 0, sourcesProcessed: 0 };
16497
+ }
16498
+ if (options.skipSources) {
16499
+ logger.info("Skipping source fetching.");
16500
+ return { fetchedSkillCount: 0, sourcesProcessed: 0 };
16501
+ }
16502
+ let lock = options.updateSources ? createEmptyLock() : await readLockFile({ baseDir });
16503
+ if (options.frozen) {
16504
+ const missingKeys = [];
16505
+ const missingSkills = [];
16506
+ const curatedDir = join114(baseDir, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);
16507
+ for (const source of sources) {
16508
+ const locked = getLockedSource(lock, source.source);
16509
+ if (!locked) {
16510
+ missingKeys.push(source.source);
16511
+ continue;
16512
+ }
16513
+ const skillNames = getLockedSkillNames(locked);
16514
+ for (const skillName of skillNames) {
16515
+ if (!await directoryExists(join114(curatedDir, skillName))) {
16516
+ missingSkills.push(`${source.source}:${skillName}`);
16517
+ }
16518
+ }
16519
+ }
16520
+ if (missingKeys.length > 0) {
16521
+ throw new Error(
16522
+ `Frozen install failed: lockfile is missing entries for: ${missingKeys.join(", ")}. Run 'rulesync install' to update the lockfile.`
16523
+ );
16524
+ }
16525
+ if (missingSkills.length > 0) {
16526
+ throw new Error(
16527
+ `Frozen install failed: locked skills missing from disk: ${missingSkills.join(", ")}. Run 'rulesync install' to fetch missing skills.`
16528
+ );
16529
+ }
16530
+ }
16531
+ const originalLockJson = JSON.stringify(lock);
16532
+ const token = GitHubClient.resolveToken(options.token);
16533
+ const client = new GitHubClient({ token });
16534
+ const localSkillNames = await getLocalSkillDirNames(baseDir);
16535
+ let totalSkillCount = 0;
16536
+ const allFetchedSkillNames = /* @__PURE__ */ new Set();
16537
+ for (const sourceEntry of sources) {
16538
+ try {
16539
+ const { skillCount, fetchedSkillNames, updatedLock } = await fetchSource({
16540
+ sourceEntry,
16541
+ client,
16542
+ baseDir,
16543
+ lock,
16544
+ localSkillNames,
16545
+ alreadyFetchedSkillNames: allFetchedSkillNames,
16546
+ updateSources: options.updateSources ?? false
16547
+ });
16548
+ lock = updatedLock;
16549
+ totalSkillCount += skillCount;
16550
+ for (const name of fetchedSkillNames) {
16551
+ allFetchedSkillNames.add(name);
16552
+ }
16553
+ } catch (error) {
16554
+ if (error instanceof GitHubClientError) {
16555
+ logGitHubAuthHints(error);
16556
+ } else {
16557
+ logger.error(`Failed to fetch source "${sourceEntry.source}": ${formatError(error)}`);
16558
+ }
16559
+ }
16560
+ }
16561
+ const sourceKeys = new Set(sources.map((s) => normalizeSourceKey(s.source)));
16562
+ const prunedSources = {};
16563
+ for (const [key, value] of Object.entries(lock.sources)) {
16564
+ if (sourceKeys.has(normalizeSourceKey(key))) {
16565
+ prunedSources[key] = value;
16566
+ } else {
16567
+ logger.debug(`Pruned stale lockfile entry: ${key}`);
16568
+ }
16569
+ }
16570
+ lock = { lockfileVersion: lock.lockfileVersion, sources: prunedSources };
16571
+ if (!options.frozen && JSON.stringify(lock) !== originalLockJson) {
16572
+ await writeLockFile({ baseDir, lock });
16573
+ } else {
16574
+ logger.debug("Lockfile unchanged, skipping write.");
16575
+ }
16576
+ return { fetchedSkillCount: totalSkillCount, sourcesProcessed: sources.length };
16577
+ }
16578
+ async function checkLockedSkillsExist(curatedDir, skillNames) {
16579
+ if (skillNames.length === 0) return true;
16580
+ for (const name of skillNames) {
16581
+ if (!await directoryExists(join114(curatedDir, name))) {
16582
+ return false;
16583
+ }
16584
+ }
16585
+ return true;
16586
+ }
16587
+ async function fetchSource(params) {
16588
+ const { sourceEntry, client, baseDir, localSkillNames, alreadyFetchedSkillNames, updateSources } = params;
16589
+ let { lock } = params;
16590
+ const parsed = parseSource(sourceEntry.source);
16591
+ if (parsed.provider === "gitlab") {
16592
+ throw new Error("GitLab sources are not yet supported.");
16593
+ }
16594
+ const sourceKey = sourceEntry.source;
16595
+ const locked = getLockedSource(lock, sourceKey);
16596
+ const lockedSkillNames = locked ? getLockedSkillNames(locked) : [];
16597
+ let ref;
16598
+ let resolvedSha;
16599
+ let requestedRef;
16600
+ if (locked && !updateSources) {
16601
+ ref = locked.resolvedRef;
16602
+ resolvedSha = locked.resolvedRef;
16603
+ requestedRef = locked.requestedRef;
16604
+ logger.debug(`Using locked ref for ${sourceKey}: ${resolvedSha}`);
16605
+ } else {
16606
+ requestedRef = parsed.ref ?? await client.getDefaultBranch(parsed.owner, parsed.repo);
16607
+ resolvedSha = await client.resolveRefToSha(parsed.owner, parsed.repo, requestedRef);
16608
+ ref = resolvedSha;
16609
+ logger.debug(`Resolved ${sourceKey} ref "${requestedRef}" to SHA: ${resolvedSha}`);
16610
+ }
16611
+ const curatedDir = join114(baseDir, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);
16612
+ if (locked && resolvedSha === locked.resolvedRef && !updateSources) {
16613
+ const allExist = await checkLockedSkillsExist(curatedDir, lockedSkillNames);
16614
+ if (allExist) {
16615
+ logger.debug(`SHA unchanged for ${sourceKey}, skipping re-fetch.`);
16616
+ return {
16617
+ skillCount: 0,
16618
+ fetchedSkillNames: lockedSkillNames,
16619
+ updatedLock: lock
16620
+ };
16621
+ }
16622
+ }
16623
+ const skillFilter = sourceEntry.skills ?? ["*"];
16624
+ const isWildcard = skillFilter.length === 1 && skillFilter[0] === "*";
16625
+ const skillsBasePath = parsed.path ?? "skills";
16626
+ let remoteSkillDirs;
16627
+ try {
16628
+ const entries = await client.listDirectory(parsed.owner, parsed.repo, skillsBasePath, ref);
16629
+ remoteSkillDirs = entries.filter((e) => e.type === "dir").map((e) => ({ name: e.name, path: e.path }));
16630
+ } catch (error) {
16631
+ if (error instanceof GitHubClientError && error.statusCode === 404) {
16632
+ logger.warn(`No skills/ directory found in ${sourceKey}. Skipping.`);
16633
+ return { skillCount: 0, fetchedSkillNames: [], updatedLock: lock };
16634
+ }
16635
+ throw error;
16636
+ }
16637
+ const filteredDirs = isWildcard ? remoteSkillDirs : remoteSkillDirs.filter((d) => skillFilter.includes(d.name));
16638
+ const semaphore = new Semaphore2(FETCH_CONCURRENCY_LIMIT);
16639
+ const fetchedSkills = {};
16640
+ if (locked) {
16641
+ const resolvedCuratedDir = resolve5(curatedDir);
16642
+ for (const prevSkill of lockedSkillNames) {
16643
+ const prevDir = join114(curatedDir, prevSkill);
16644
+ if (!resolve5(prevDir).startsWith(resolvedCuratedDir + sep)) {
16645
+ logger.warn(
16646
+ `Skipping removal of "${prevSkill}": resolved path is outside the curated directory.`
16647
+ );
16648
+ continue;
16649
+ }
16650
+ if (await directoryExists(prevDir)) {
16651
+ await removeDirectory(prevDir);
16652
+ }
16653
+ }
16654
+ }
16655
+ for (const skillDir of filteredDirs) {
16656
+ if (skillDir.name.includes("..") || skillDir.name.includes("/") || skillDir.name.includes("\\")) {
16657
+ logger.warn(
16658
+ `Skipping skill with invalid name "${skillDir.name}" from ${sourceKey}: contains path traversal characters.`
16659
+ );
16660
+ continue;
16661
+ }
16662
+ if (localSkillNames.has(skillDir.name)) {
16663
+ logger.debug(
16664
+ `Skipping remote skill "${skillDir.name}" from ${sourceKey}: local skill takes precedence.`
16665
+ );
16666
+ continue;
16667
+ }
16668
+ if (alreadyFetchedSkillNames.has(skillDir.name)) {
16669
+ logger.warn(
16670
+ `Skipping duplicate skill "${skillDir.name}" from ${sourceKey}: already fetched from another source.`
16671
+ );
16672
+ continue;
16673
+ }
16674
+ const allFiles = await listDirectoryRecursive({
16675
+ client,
16676
+ owner: parsed.owner,
16677
+ repo: parsed.repo,
16678
+ path: skillDir.path,
16679
+ ref,
16680
+ semaphore
16681
+ });
16682
+ const files = allFiles.filter((file) => {
16683
+ if (file.size > MAX_FILE_SIZE) {
16684
+ logger.warn(
16685
+ `Skipping file "${file.path}" (${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`
16686
+ );
16687
+ return false;
16688
+ }
16689
+ return true;
16690
+ });
16691
+ const skillFiles = [];
16692
+ for (const file of files) {
16693
+ const relativeToSkill = file.path.substring(skillDir.path.length + 1);
16694
+ const localFilePath = join114(curatedDir, skillDir.name, relativeToSkill);
16695
+ checkPathTraversal({
16696
+ relativePath: relativeToSkill,
16697
+ intendedRootDir: join114(curatedDir, skillDir.name)
16698
+ });
16699
+ const content = await withSemaphore(
16700
+ semaphore,
16701
+ () => client.getFileContent(parsed.owner, parsed.repo, file.path, ref)
16702
+ );
16703
+ await writeFileContent(localFilePath, content);
16704
+ skillFiles.push({ path: relativeToSkill, content });
16705
+ }
16706
+ const integrity = computeSkillIntegrity(skillFiles);
16707
+ const lockedSkillEntry = locked?.skills[skillDir.name];
16708
+ if (lockedSkillEntry && lockedSkillEntry.integrity && lockedSkillEntry.integrity !== integrity && resolvedSha === locked?.resolvedRef) {
16709
+ logger.warn(
16710
+ `Integrity mismatch for skill "${skillDir.name}" from ${sourceKey}: expected "${lockedSkillEntry.integrity}", got "${integrity}". Content may have been tampered with.`
16711
+ );
16712
+ }
16713
+ fetchedSkills[skillDir.name] = { integrity };
16714
+ logger.debug(`Fetched skill "${skillDir.name}" from ${sourceKey}`);
16715
+ }
16716
+ const fetchedNames = Object.keys(fetchedSkills);
16717
+ const mergedSkills = { ...fetchedSkills };
16718
+ if (locked) {
16719
+ for (const [skillName, skillEntry] of Object.entries(locked.skills)) {
16720
+ if (!(skillName in mergedSkills)) {
16721
+ mergedSkills[skillName] = skillEntry;
16722
+ }
16723
+ }
16724
+ }
16725
+ lock = setLockedSource(lock, sourceKey, {
16726
+ requestedRef,
16727
+ resolvedRef: resolvedSha,
16728
+ resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
16729
+ skills: mergedSkills
16730
+ });
16731
+ logger.info(
16732
+ `Fetched ${fetchedNames.length} skill(s) from ${sourceKey}: ${fetchedNames.join(", ") || "(none)"}`
16733
+ );
16734
+ return {
16735
+ skillCount: fetchedNames.length,
16736
+ fetchedSkillNames: fetchedNames,
16737
+ updatedLock: lock
16738
+ };
16739
+ }
16740
+
16741
+ // src/cli/commands/install.ts
16742
+ async function installCommand(options) {
16743
+ logger.configure({
16744
+ verbose: options.verbose ?? false,
16745
+ silent: options.silent ?? false
16746
+ });
16747
+ const config = await ConfigResolver.resolve({
16748
+ configPath: options.configPath,
16749
+ verbose: options.verbose,
16750
+ silent: options.silent
16751
+ });
16752
+ const sources = config.getSources();
16753
+ if (sources.length === 0) {
16754
+ logger.warn("No sources defined in configuration. Nothing to install.");
16755
+ return;
16756
+ }
16757
+ logger.debug(`Installing skills from ${sources.length} source(s)...`);
16758
+ const result = await resolveAndFetchSources({
16759
+ sources,
16760
+ baseDir: process.cwd(),
16761
+ options: {
16762
+ updateSources: options.update,
16763
+ frozen: options.frozen,
16764
+ token: options.token
16765
+ }
16766
+ });
16767
+ if (result.fetchedSkillCount > 0) {
16768
+ logger.success(
16769
+ `Installed ${result.fetchedSkillCount} skill(s) from ${result.sourcesProcessed} source(s).`
16770
+ );
16771
+ } else {
16772
+ logger.success(`All skills up to date (${result.sourcesProcessed} source(s) checked).`);
16773
+ }
16774
+ }
16775
+
16216
16776
  // src/cli/commands/mcp.ts
16217
16777
  import { FastMCP } from "fastmcp";
16218
16778
 
16219
16779
  // src/mcp/tools.ts
16220
- import { z as z61 } from "zod/mini";
16780
+ import { z as z62 } from "zod/mini";
16221
16781
 
16222
16782
  // src/mcp/commands.ts
16223
- import { basename as basename24, join as join112 } from "path";
16224
- import { z as z53 } from "zod/mini";
16783
+ import { basename as basename25, join as join115 } from "path";
16784
+ import { z as z54 } from "zod/mini";
16225
16785
  var maxCommandSizeBytes = 1024 * 1024;
16226
16786
  var maxCommandsCount = 1e3;
16227
16787
  async function listCommands() {
16228
- const commandsDir = join112(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
16788
+ const commandsDir = join115(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
16229
16789
  try {
16230
16790
  const files = await listDirectoryFiles(commandsDir);
16231
16791
  const mdFiles = files.filter((file) => file.endsWith(".md"));
@@ -16237,7 +16797,7 @@ async function listCommands() {
16237
16797
  });
16238
16798
  const frontmatter = command.getFrontmatter();
16239
16799
  return {
16240
- relativePathFromCwd: join112(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, file),
16800
+ relativePathFromCwd: join115(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, file),
16241
16801
  frontmatter
16242
16802
  };
16243
16803
  } catch (error) {
@@ -16248,7 +16808,9 @@ async function listCommands() {
16248
16808
  );
16249
16809
  return commands.filter((command) => command !== null);
16250
16810
  } catch (error) {
16251
- logger.error(`Failed to read commands directory: ${formatError(error)}`);
16811
+ logger.error(
16812
+ `Failed to read commands directory (${RULESYNC_COMMANDS_RELATIVE_DIR_PATH}): ${formatError(error)}`
16813
+ );
16252
16814
  return [];
16253
16815
  }
16254
16816
  }
@@ -16257,13 +16819,13 @@ async function getCommand({ relativePathFromCwd }) {
16257
16819
  relativePath: relativePathFromCwd,
16258
16820
  intendedRootDir: process.cwd()
16259
16821
  });
16260
- const filename = basename24(relativePathFromCwd);
16822
+ const filename = basename25(relativePathFromCwd);
16261
16823
  try {
16262
16824
  const command = await RulesyncCommand.fromFile({
16263
16825
  relativeFilePath: filename
16264
16826
  });
16265
16827
  return {
16266
- relativePathFromCwd: join112(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
16828
+ relativePathFromCwd: join115(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
16267
16829
  frontmatter: command.getFrontmatter(),
16268
16830
  body: command.getBody()
16269
16831
  };
@@ -16282,20 +16844,22 @@ async function putCommand({
16282
16844
  relativePath: relativePathFromCwd,
16283
16845
  intendedRootDir: process.cwd()
16284
16846
  });
16285
- const filename = basename24(relativePathFromCwd);
16847
+ const filename = basename25(relativePathFromCwd);
16286
16848
  const estimatedSize = JSON.stringify(frontmatter).length + body.length;
16287
16849
  if (estimatedSize > maxCommandSizeBytes) {
16288
16850
  throw new Error(
16289
- `Command size ${estimatedSize} bytes exceeds maximum ${maxCommandSizeBytes} bytes (1MB)`
16851
+ `Command size ${estimatedSize} bytes exceeds maximum ${maxCommandSizeBytes} bytes (1MB) for ${relativePathFromCwd}`
16290
16852
  );
16291
16853
  }
16292
16854
  try {
16293
16855
  const existingCommands = await listCommands();
16294
16856
  const isUpdate = existingCommands.some(
16295
- (command2) => command2.relativePathFromCwd === join112(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
16857
+ (command2) => command2.relativePathFromCwd === join115(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
16296
16858
  );
16297
16859
  if (!isUpdate && existingCommands.length >= maxCommandsCount) {
16298
- throw new Error(`Maximum number of commands (${maxCommandsCount}) reached`);
16860
+ throw new Error(
16861
+ `Maximum number of commands (${maxCommandsCount}) reached in ${RULESYNC_COMMANDS_RELATIVE_DIR_PATH}`
16862
+ );
16299
16863
  }
16300
16864
  const fileContent = stringifyFrontmatter(body, frontmatter);
16301
16865
  const command = new RulesyncCommand({
@@ -16307,11 +16871,11 @@ async function putCommand({
16307
16871
  fileContent,
16308
16872
  validate: true
16309
16873
  });
16310
- const commandsDir = join112(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
16874
+ const commandsDir = join115(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
16311
16875
  await ensureDir(commandsDir);
16312
16876
  await writeFileContent(command.getFilePath(), command.getFileContent());
16313
16877
  return {
16314
- relativePathFromCwd: join112(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
16878
+ relativePathFromCwd: join115(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
16315
16879
  frontmatter: command.getFrontmatter(),
16316
16880
  body: command.getBody()
16317
16881
  };
@@ -16326,12 +16890,12 @@ async function deleteCommand({ relativePathFromCwd }) {
16326
16890
  relativePath: relativePathFromCwd,
16327
16891
  intendedRootDir: process.cwd()
16328
16892
  });
16329
- const filename = basename24(relativePathFromCwd);
16330
- const fullPath = join112(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename);
16893
+ const filename = basename25(relativePathFromCwd);
16894
+ const fullPath = join115(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename);
16331
16895
  try {
16332
16896
  await removeFile(fullPath);
16333
16897
  return {
16334
- relativePathFromCwd: join112(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
16898
+ relativePathFromCwd: join115(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
16335
16899
  };
16336
16900
  } catch (error) {
16337
16901
  throw new Error(`Failed to delete command file ${relativePathFromCwd}: ${formatError(error)}`, {
@@ -16340,23 +16904,23 @@ async function deleteCommand({ relativePathFromCwd }) {
16340
16904
  }
16341
16905
  }
16342
16906
  var commandToolSchemas = {
16343
- listCommands: z53.object({}),
16344
- getCommand: z53.object({
16345
- relativePathFromCwd: z53.string()
16907
+ listCommands: z54.object({}),
16908
+ getCommand: z54.object({
16909
+ relativePathFromCwd: z54.string()
16346
16910
  }),
16347
- putCommand: z53.object({
16348
- relativePathFromCwd: z53.string(),
16911
+ putCommand: z54.object({
16912
+ relativePathFromCwd: z54.string(),
16349
16913
  frontmatter: RulesyncCommandFrontmatterSchema,
16350
- body: z53.string()
16914
+ body: z54.string()
16351
16915
  }),
16352
- deleteCommand: z53.object({
16353
- relativePathFromCwd: z53.string()
16916
+ deleteCommand: z54.object({
16917
+ relativePathFromCwd: z54.string()
16354
16918
  })
16355
16919
  };
16356
16920
  var commandTools = {
16357
16921
  listCommands: {
16358
16922
  name: "listCommands",
16359
- description: `List all commands from ${join112(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
16923
+ description: `List all commands from ${join115(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
16360
16924
  parameters: commandToolSchemas.listCommands,
16361
16925
  execute: async () => {
16362
16926
  const commands = await listCommands();
@@ -16398,15 +16962,15 @@ var commandTools = {
16398
16962
  };
16399
16963
 
16400
16964
  // src/mcp/generate.ts
16401
- import { z as z54 } from "zod/mini";
16402
- var generateOptionsSchema = z54.object({
16403
- targets: z54.optional(z54.array(z54.string())),
16404
- features: z54.optional(z54.array(z54.string())),
16405
- delete: z54.optional(z54.boolean()),
16406
- global: z54.optional(z54.boolean()),
16407
- simulateCommands: z54.optional(z54.boolean()),
16408
- simulateSubagents: z54.optional(z54.boolean()),
16409
- simulateSkills: z54.optional(z54.boolean())
16965
+ import { z as z55 } from "zod/mini";
16966
+ var generateOptionsSchema = z55.object({
16967
+ targets: z55.optional(z55.array(z55.string())),
16968
+ features: z55.optional(z55.array(z55.string())),
16969
+ delete: z55.optional(z55.boolean()),
16970
+ global: z55.optional(z55.boolean()),
16971
+ simulateCommands: z55.optional(z55.boolean()),
16972
+ simulateSubagents: z55.optional(z55.boolean()),
16973
+ simulateSkills: z55.optional(z55.boolean())
16410
16974
  });
16411
16975
  async function executeGenerate(options = {}) {
16412
16976
  try {
@@ -16483,11 +17047,11 @@ var generateTools = {
16483
17047
  };
16484
17048
 
16485
17049
  // src/mcp/ignore.ts
16486
- import { join as join113 } from "path";
16487
- import { z as z55 } from "zod/mini";
17050
+ import { join as join116 } from "path";
17051
+ import { z as z56 } from "zod/mini";
16488
17052
  var maxIgnoreFileSizeBytes = 100 * 1024;
16489
17053
  async function getIgnoreFile() {
16490
- const ignoreFilePath = join113(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
17054
+ const ignoreFilePath = join116(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
16491
17055
  try {
16492
17056
  const content = await readFileContent(ignoreFilePath);
16493
17057
  return {
@@ -16495,17 +17059,20 @@ async function getIgnoreFile() {
16495
17059
  content
16496
17060
  };
16497
17061
  } catch (error) {
16498
- throw new Error(`Failed to read .rulesync/.aiignore file: ${formatError(error)}`, {
16499
- cause: error
16500
- });
17062
+ throw new Error(
17063
+ `Failed to read ignore file (${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}): ${formatError(error)}`,
17064
+ {
17065
+ cause: error
17066
+ }
17067
+ );
16501
17068
  }
16502
17069
  }
16503
17070
  async function putIgnoreFile({ content }) {
16504
- const ignoreFilePath = join113(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
17071
+ const ignoreFilePath = join116(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
16505
17072
  const contentSizeBytes = Buffer.byteLength(content, "utf8");
16506
17073
  if (contentSizeBytes > maxIgnoreFileSizeBytes) {
16507
17074
  throw new Error(
16508
- `Ignore file size ${contentSizeBytes} bytes exceeds maximum ${maxIgnoreFileSizeBytes} bytes (100KB)`
17075
+ `Ignore file size ${contentSizeBytes} bytes exceeds maximum ${maxIgnoreFileSizeBytes} bytes (100KB) for ${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}`
16509
17076
  );
16510
17077
  }
16511
17078
  try {
@@ -16516,14 +17083,17 @@ async function putIgnoreFile({ content }) {
16516
17083
  content
16517
17084
  };
16518
17085
  } catch (error) {
16519
- throw new Error(`Failed to write .rulesync/.aiignore file: ${formatError(error)}`, {
16520
- cause: error
16521
- });
17086
+ throw new Error(
17087
+ `Failed to write ignore file (${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}): ${formatError(error)}`,
17088
+ {
17089
+ cause: error
17090
+ }
17091
+ );
16522
17092
  }
16523
17093
  }
16524
17094
  async function deleteIgnoreFile() {
16525
- const aiignorePath = join113(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
16526
- const legacyIgnorePath = join113(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);
17095
+ const aiignorePath = join116(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
17096
+ const legacyIgnorePath = join116(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);
16527
17097
  try {
16528
17098
  await Promise.all([removeFile(aiignorePath), removeFile(legacyIgnorePath)]);
16529
17099
  return {
@@ -16533,7 +17103,7 @@ async function deleteIgnoreFile() {
16533
17103
  };
16534
17104
  } catch (error) {
16535
17105
  throw new Error(
16536
- `Failed to delete .rulesyncignore and .rulesync/.aiignore files: ${formatError(error)}`,
17106
+ `Failed to delete ignore files (${RULESYNC_AIIGNORE_RELATIVE_FILE_PATH}, ${RULESYNC_IGNORE_RELATIVE_FILE_PATH}): ${formatError(error)}`,
16537
17107
  {
16538
17108
  cause: error
16539
17109
  }
@@ -16541,11 +17111,11 @@ async function deleteIgnoreFile() {
16541
17111
  }
16542
17112
  }
16543
17113
  var ignoreToolSchemas = {
16544
- getIgnoreFile: z55.object({}),
16545
- putIgnoreFile: z55.object({
16546
- content: z55.string()
17114
+ getIgnoreFile: z56.object({}),
17115
+ putIgnoreFile: z56.object({
17116
+ content: z56.string()
16547
17117
  }),
16548
- deleteIgnoreFile: z55.object({})
17118
+ deleteIgnoreFile: z56.object({})
16549
17119
  };
16550
17120
  var ignoreTools = {
16551
17121
  getIgnoreFile: {
@@ -16578,11 +17148,11 @@ var ignoreTools = {
16578
17148
  };
16579
17149
 
16580
17150
  // src/mcp/import.ts
16581
- import { z as z56 } from "zod/mini";
16582
- var importOptionsSchema = z56.object({
16583
- target: z56.string(),
16584
- features: z56.optional(z56.array(z56.string())),
16585
- global: z56.optional(z56.boolean())
17151
+ import { z as z57 } from "zod/mini";
17152
+ var importOptionsSchema = z57.object({
17153
+ target: z57.string(),
17154
+ features: z57.optional(z57.array(z57.string())),
17155
+ global: z57.optional(z57.boolean())
16586
17156
  });
16587
17157
  async function executeImport(options) {
16588
17158
  try {
@@ -16651,15 +17221,15 @@ var importTools = {
16651
17221
  };
16652
17222
 
16653
17223
  // src/mcp/mcp.ts
16654
- import { join as join114 } from "path";
16655
- import { z as z57 } from "zod/mini";
17224
+ import { join as join117 } from "path";
17225
+ import { z as z58 } from "zod/mini";
16656
17226
  var maxMcpSizeBytes = 1024 * 1024;
16657
17227
  async function getMcpFile() {
16658
17228
  try {
16659
17229
  const rulesyncMcp = await RulesyncMcp.fromFile({
16660
17230
  validate: true
16661
17231
  });
16662
- const relativePathFromCwd = join114(
17232
+ const relativePathFromCwd = join117(
16663
17233
  rulesyncMcp.getRelativeDirPath(),
16664
17234
  rulesyncMcp.getRelativeFilePath()
16665
17235
  );
@@ -16668,30 +17238,36 @@ async function getMcpFile() {
16668
17238
  content: rulesyncMcp.getFileContent()
16669
17239
  };
16670
17240
  } catch (error) {
16671
- throw new Error(`Failed to read MCP file: ${formatError(error)}`, {
16672
- cause: error
16673
- });
17241
+ throw new Error(
17242
+ `Failed to read MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,
17243
+ {
17244
+ cause: error
17245
+ }
17246
+ );
16674
17247
  }
16675
17248
  }
16676
17249
  async function putMcpFile({ content }) {
16677
17250
  if (content.length > maxMcpSizeBytes) {
16678
17251
  throw new Error(
16679
- `MCP file size ${content.length} bytes exceeds maximum ${maxMcpSizeBytes} bytes (1MB)`
17252
+ `MCP file size ${content.length} bytes exceeds maximum ${maxMcpSizeBytes} bytes (1MB) for ${RULESYNC_MCP_RELATIVE_FILE_PATH}`
16680
17253
  );
16681
17254
  }
16682
17255
  try {
16683
17256
  JSON.parse(content);
16684
17257
  } catch (error) {
16685
- throw new Error(`Invalid JSON format in MCP file: ${formatError(error)}`, {
16686
- cause: error
16687
- });
17258
+ throw new Error(
17259
+ `Invalid JSON format in MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,
17260
+ {
17261
+ cause: error
17262
+ }
17263
+ );
16688
17264
  }
16689
17265
  try {
16690
17266
  const baseDir = process.cwd();
16691
17267
  const paths = RulesyncMcp.getSettablePaths();
16692
17268
  const relativeDirPath = paths.recommended.relativeDirPath;
16693
17269
  const relativeFilePath = paths.recommended.relativeFilePath;
16694
- const fullPath = join114(baseDir, relativeDirPath, relativeFilePath);
17270
+ const fullPath = join117(baseDir, relativeDirPath, relativeFilePath);
16695
17271
  const rulesyncMcp = new RulesyncMcp({
16696
17272
  baseDir,
16697
17273
  relativeDirPath,
@@ -16699,32 +17275,35 @@ async function putMcpFile({ content }) {
16699
17275
  fileContent: content,
16700
17276
  validate: true
16701
17277
  });
16702
- await ensureDir(join114(baseDir, relativeDirPath));
17278
+ await ensureDir(join117(baseDir, relativeDirPath));
16703
17279
  await writeFileContent(fullPath, content);
16704
- const relativePathFromCwd = join114(relativeDirPath, relativeFilePath);
17280
+ const relativePathFromCwd = join117(relativeDirPath, relativeFilePath);
16705
17281
  return {
16706
17282
  relativePathFromCwd,
16707
17283
  content: rulesyncMcp.getFileContent()
16708
17284
  };
16709
17285
  } catch (error) {
16710
- throw new Error(`Failed to write MCP file: ${formatError(error)}`, {
16711
- cause: error
16712
- });
17286
+ throw new Error(
17287
+ `Failed to write MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,
17288
+ {
17289
+ cause: error
17290
+ }
17291
+ );
16713
17292
  }
16714
17293
  }
16715
17294
  async function deleteMcpFile() {
16716
17295
  try {
16717
17296
  const baseDir = process.cwd();
16718
17297
  const paths = RulesyncMcp.getSettablePaths();
16719
- const recommendedPath = join114(
17298
+ const recommendedPath = join117(
16720
17299
  baseDir,
16721
17300
  paths.recommended.relativeDirPath,
16722
17301
  paths.recommended.relativeFilePath
16723
17302
  );
16724
- const legacyPath = join114(baseDir, paths.legacy.relativeDirPath, paths.legacy.relativeFilePath);
17303
+ const legacyPath = join117(baseDir, paths.legacy.relativeDirPath, paths.legacy.relativeFilePath);
16725
17304
  await removeFile(recommendedPath);
16726
17305
  await removeFile(legacyPath);
16727
- const relativePathFromCwd = join114(
17306
+ const relativePathFromCwd = join117(
16728
17307
  paths.recommended.relativeDirPath,
16729
17308
  paths.recommended.relativeFilePath
16730
17309
  );
@@ -16732,17 +17311,20 @@ async function deleteMcpFile() {
16732
17311
  relativePathFromCwd
16733
17312
  };
16734
17313
  } catch (error) {
16735
- throw new Error(`Failed to delete MCP file: ${formatError(error)}`, {
16736
- cause: error
16737
- });
17314
+ throw new Error(
17315
+ `Failed to delete MCP file (${RULESYNC_MCP_RELATIVE_FILE_PATH}): ${formatError(error)}`,
17316
+ {
17317
+ cause: error
17318
+ }
17319
+ );
16738
17320
  }
16739
17321
  }
16740
17322
  var mcpToolSchemas = {
16741
- getMcpFile: z57.object({}),
16742
- putMcpFile: z57.object({
16743
- content: z57.string()
17323
+ getMcpFile: z58.object({}),
17324
+ putMcpFile: z58.object({
17325
+ content: z58.string()
16744
17326
  }),
16745
- deleteMcpFile: z57.object({})
17327
+ deleteMcpFile: z58.object({})
16746
17328
  };
16747
17329
  var mcpTools = {
16748
17330
  getMcpFile: {
@@ -16775,12 +17357,12 @@ var mcpTools = {
16775
17357
  };
16776
17358
 
16777
17359
  // src/mcp/rules.ts
16778
- import { basename as basename25, join as join115 } from "path";
16779
- import { z as z58 } from "zod/mini";
17360
+ import { basename as basename26, join as join118 } from "path";
17361
+ import { z as z59 } from "zod/mini";
16780
17362
  var maxRuleSizeBytes = 1024 * 1024;
16781
17363
  var maxRulesCount = 1e3;
16782
17364
  async function listRules() {
16783
- const rulesDir = join115(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
17365
+ const rulesDir = join118(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
16784
17366
  try {
16785
17367
  const files = await listDirectoryFiles(rulesDir);
16786
17368
  const mdFiles = files.filter((file) => file.endsWith(".md"));
@@ -16793,7 +17375,7 @@ async function listRules() {
16793
17375
  });
16794
17376
  const frontmatter = rule.getFrontmatter();
16795
17377
  return {
16796
- relativePathFromCwd: join115(RULESYNC_RULES_RELATIVE_DIR_PATH, file),
17378
+ relativePathFromCwd: join118(RULESYNC_RULES_RELATIVE_DIR_PATH, file),
16797
17379
  frontmatter
16798
17380
  };
16799
17381
  } catch (error) {
@@ -16804,7 +17386,9 @@ async function listRules() {
16804
17386
  );
16805
17387
  return rules.filter((rule) => rule !== null);
16806
17388
  } catch (error) {
16807
- logger.error(`Failed to read rules directory: ${formatError(error)}`);
17389
+ logger.error(
17390
+ `Failed to read rules directory (${RULESYNC_RULES_RELATIVE_DIR_PATH}): ${formatError(error)}`
17391
+ );
16808
17392
  return [];
16809
17393
  }
16810
17394
  }
@@ -16813,14 +17397,14 @@ async function getRule({ relativePathFromCwd }) {
16813
17397
  relativePath: relativePathFromCwd,
16814
17398
  intendedRootDir: process.cwd()
16815
17399
  });
16816
- const filename = basename25(relativePathFromCwd);
17400
+ const filename = basename26(relativePathFromCwd);
16817
17401
  try {
16818
17402
  const rule = await RulesyncRule.fromFile({
16819
17403
  relativeFilePath: filename,
16820
17404
  validate: true
16821
17405
  });
16822
17406
  return {
16823
- relativePathFromCwd: join115(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
17407
+ relativePathFromCwd: join118(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
16824
17408
  frontmatter: rule.getFrontmatter(),
16825
17409
  body: rule.getBody()
16826
17410
  };
@@ -16839,20 +17423,22 @@ async function putRule({
16839
17423
  relativePath: relativePathFromCwd,
16840
17424
  intendedRootDir: process.cwd()
16841
17425
  });
16842
- const filename = basename25(relativePathFromCwd);
17426
+ const filename = basename26(relativePathFromCwd);
16843
17427
  const estimatedSize = JSON.stringify(frontmatter).length + body.length;
16844
17428
  if (estimatedSize > maxRuleSizeBytes) {
16845
17429
  throw new Error(
16846
- `Rule size ${estimatedSize} bytes exceeds maximum ${maxRuleSizeBytes} bytes (1MB)`
17430
+ `Rule size ${estimatedSize} bytes exceeds maximum ${maxRuleSizeBytes} bytes (1MB) for ${relativePathFromCwd}`
16847
17431
  );
16848
17432
  }
16849
17433
  try {
16850
17434
  const existingRules = await listRules();
16851
17435
  const isUpdate = existingRules.some(
16852
- (rule2) => rule2.relativePathFromCwd === join115(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
17436
+ (rule2) => rule2.relativePathFromCwd === join118(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
16853
17437
  );
16854
17438
  if (!isUpdate && existingRules.length >= maxRulesCount) {
16855
- throw new Error(`Maximum number of rules (${maxRulesCount}) reached`);
17439
+ throw new Error(
17440
+ `Maximum number of rules (${maxRulesCount}) reached in ${RULESYNC_RULES_RELATIVE_DIR_PATH}`
17441
+ );
16856
17442
  }
16857
17443
  const rule = new RulesyncRule({
16858
17444
  baseDir: process.cwd(),
@@ -16862,11 +17448,11 @@ async function putRule({
16862
17448
  body,
16863
17449
  validate: true
16864
17450
  });
16865
- const rulesDir = join115(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
17451
+ const rulesDir = join118(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
16866
17452
  await ensureDir(rulesDir);
16867
17453
  await writeFileContent(rule.getFilePath(), rule.getFileContent());
16868
17454
  return {
16869
- relativePathFromCwd: join115(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
17455
+ relativePathFromCwd: join118(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
16870
17456
  frontmatter: rule.getFrontmatter(),
16871
17457
  body: rule.getBody()
16872
17458
  };
@@ -16881,12 +17467,12 @@ async function deleteRule({ relativePathFromCwd }) {
16881
17467
  relativePath: relativePathFromCwd,
16882
17468
  intendedRootDir: process.cwd()
16883
17469
  });
16884
- const filename = basename25(relativePathFromCwd);
16885
- const fullPath = join115(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH, filename);
17470
+ const filename = basename26(relativePathFromCwd);
17471
+ const fullPath = join118(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH, filename);
16886
17472
  try {
16887
17473
  await removeFile(fullPath);
16888
17474
  return {
16889
- relativePathFromCwd: join115(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
17475
+ relativePathFromCwd: join118(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
16890
17476
  };
16891
17477
  } catch (error) {
16892
17478
  throw new Error(`Failed to delete rule file ${relativePathFromCwd}: ${formatError(error)}`, {
@@ -16895,23 +17481,23 @@ async function deleteRule({ relativePathFromCwd }) {
16895
17481
  }
16896
17482
  }
16897
17483
  var ruleToolSchemas = {
16898
- listRules: z58.object({}),
16899
- getRule: z58.object({
16900
- relativePathFromCwd: z58.string()
17484
+ listRules: z59.object({}),
17485
+ getRule: z59.object({
17486
+ relativePathFromCwd: z59.string()
16901
17487
  }),
16902
- putRule: z58.object({
16903
- relativePathFromCwd: z58.string(),
17488
+ putRule: z59.object({
17489
+ relativePathFromCwd: z59.string(),
16904
17490
  frontmatter: RulesyncRuleFrontmatterSchema,
16905
- body: z58.string()
17491
+ body: z59.string()
16906
17492
  }),
16907
- deleteRule: z58.object({
16908
- relativePathFromCwd: z58.string()
17493
+ deleteRule: z59.object({
17494
+ relativePathFromCwd: z59.string()
16909
17495
  })
16910
17496
  };
16911
17497
  var ruleTools = {
16912
17498
  listRules: {
16913
17499
  name: "listRules",
16914
- description: `List all rules from ${join115(RULESYNC_RULES_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
17500
+ description: `List all rules from ${join118(RULESYNC_RULES_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
16915
17501
  parameters: ruleToolSchemas.listRules,
16916
17502
  execute: async () => {
16917
17503
  const rules = await listRules();
@@ -16953,8 +17539,8 @@ var ruleTools = {
16953
17539
  };
16954
17540
 
16955
17541
  // src/mcp/skills.ts
16956
- import { basename as basename26, dirname as dirname3, join as join116 } from "path";
16957
- import { z as z59 } from "zod/mini";
17542
+ import { basename as basename27, dirname as dirname3, join as join119 } from "path";
17543
+ import { z as z60 } from "zod/mini";
16958
17544
  var maxSkillSizeBytes = 1024 * 1024;
16959
17545
  var maxSkillsCount = 1e3;
16960
17546
  function aiDirFileToMcpSkillFile(file) {
@@ -16970,19 +17556,19 @@ function mcpSkillFileToAiDirFile(file) {
16970
17556
  };
16971
17557
  }
16972
17558
  function extractDirName(relativeDirPathFromCwd) {
16973
- const dirName = basename26(relativeDirPathFromCwd);
17559
+ const dirName = basename27(relativeDirPathFromCwd);
16974
17560
  if (!dirName) {
16975
17561
  throw new Error(`Invalid path: ${relativeDirPathFromCwd}`);
16976
17562
  }
16977
17563
  return dirName;
16978
17564
  }
16979
17565
  async function listSkills() {
16980
- const skillsDir = join116(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH);
17566
+ const skillsDir = join119(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH);
16981
17567
  try {
16982
- const skillDirPaths = await findFilesByGlobs(join116(skillsDir, "*"), { type: "dir" });
17568
+ const skillDirPaths = await findFilesByGlobs(join119(skillsDir, "*"), { type: "dir" });
16983
17569
  const skills = await Promise.all(
16984
17570
  skillDirPaths.map(async (dirPath) => {
16985
- const dirName = basename26(dirPath);
17571
+ const dirName = basename27(dirPath);
16986
17572
  if (!dirName) return null;
16987
17573
  try {
16988
17574
  const skill = await RulesyncSkill.fromDir({
@@ -16990,7 +17576,7 @@ async function listSkills() {
16990
17576
  });
16991
17577
  const frontmatter = skill.getFrontmatter();
16992
17578
  return {
16993
- relativeDirPathFromCwd: join116(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
17579
+ relativeDirPathFromCwd: join119(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
16994
17580
  frontmatter
16995
17581
  };
16996
17582
  } catch (error) {
@@ -17001,7 +17587,9 @@ async function listSkills() {
17001
17587
  );
17002
17588
  return skills.filter((skill) => skill !== null);
17003
17589
  } catch (error) {
17004
- logger.error(`Failed to read skills directory: ${formatError(error)}`);
17590
+ logger.error(
17591
+ `Failed to read skills directory (${RULESYNC_SKILLS_RELATIVE_DIR_PATH}): ${formatError(error)}`
17592
+ );
17005
17593
  return [];
17006
17594
  }
17007
17595
  }
@@ -17016,7 +17604,7 @@ async function getSkill({ relativeDirPathFromCwd }) {
17016
17604
  dirName
17017
17605
  });
17018
17606
  return {
17019
- relativeDirPathFromCwd: join116(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
17607
+ relativeDirPathFromCwd: join119(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
17020
17608
  frontmatter: skill.getFrontmatter(),
17021
17609
  body: skill.getBody(),
17022
17610
  otherFiles: skill.getOtherFiles().map(aiDirFileToMcpSkillFile)
@@ -17044,16 +17632,18 @@ async function putSkill({
17044
17632
  const estimatedSize = JSON.stringify(frontmatter).length + body.length + otherFiles.reduce((acc, file) => acc + file.name.length + file.body.length, 0);
17045
17633
  if (estimatedSize > maxSkillSizeBytes) {
17046
17634
  throw new Error(
17047
- `Skill size ${estimatedSize} bytes exceeds maximum ${maxSkillSizeBytes} bytes (1MB)`
17635
+ `Skill size ${estimatedSize} bytes exceeds maximum ${maxSkillSizeBytes} bytes (1MB) for ${relativeDirPathFromCwd}`
17048
17636
  );
17049
17637
  }
17050
17638
  try {
17051
17639
  const existingSkills = await listSkills();
17052
17640
  const isUpdate = existingSkills.some(
17053
- (skill2) => skill2.relativeDirPathFromCwd === join116(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName)
17641
+ (skill2) => skill2.relativeDirPathFromCwd === join119(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName)
17054
17642
  );
17055
17643
  if (!isUpdate && existingSkills.length >= maxSkillsCount) {
17056
- throw new Error(`Maximum number of skills (${maxSkillsCount}) reached`);
17644
+ throw new Error(
17645
+ `Maximum number of skills (${maxSkillsCount}) reached in ${RULESYNC_SKILLS_RELATIVE_DIR_PATH}`
17646
+ );
17057
17647
  }
17058
17648
  const aiDirFiles = otherFiles.map(mcpSkillFileToAiDirFile);
17059
17649
  const skill = new RulesyncSkill({
@@ -17065,9 +17655,9 @@ async function putSkill({
17065
17655
  otherFiles: aiDirFiles,
17066
17656
  validate: true
17067
17657
  });
17068
- const skillDirPath = join116(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);
17658
+ const skillDirPath = join119(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);
17069
17659
  await ensureDir(skillDirPath);
17070
- const skillFilePath = join116(skillDirPath, SKILL_FILE_NAME);
17660
+ const skillFilePath = join119(skillDirPath, SKILL_FILE_NAME);
17071
17661
  const skillFileContent = stringifyFrontmatter(body, frontmatter);
17072
17662
  await writeFileContent(skillFilePath, skillFileContent);
17073
17663
  for (const file of otherFiles) {
@@ -17075,15 +17665,15 @@ async function putSkill({
17075
17665
  relativePath: file.name,
17076
17666
  intendedRootDir: skillDirPath
17077
17667
  });
17078
- const filePath = join116(skillDirPath, file.name);
17079
- const fileDir = join116(skillDirPath, dirname3(file.name));
17668
+ const filePath = join119(skillDirPath, file.name);
17669
+ const fileDir = join119(skillDirPath, dirname3(file.name));
17080
17670
  if (fileDir !== skillDirPath) {
17081
17671
  await ensureDir(fileDir);
17082
17672
  }
17083
17673
  await writeFileContent(filePath, file.body);
17084
17674
  }
17085
17675
  return {
17086
- relativeDirPathFromCwd: join116(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
17676
+ relativeDirPathFromCwd: join119(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
17087
17677
  frontmatter: skill.getFrontmatter(),
17088
17678
  body: skill.getBody(),
17089
17679
  otherFiles: skill.getOtherFiles().map(aiDirFileToMcpSkillFile)
@@ -17105,13 +17695,13 @@ async function deleteSkill({
17105
17695
  intendedRootDir: process.cwd()
17106
17696
  });
17107
17697
  const dirName = extractDirName(relativeDirPathFromCwd);
17108
- const skillDirPath = join116(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);
17698
+ const skillDirPath = join119(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);
17109
17699
  try {
17110
17700
  if (await directoryExists(skillDirPath)) {
17111
17701
  await removeDirectory(skillDirPath);
17112
17702
  }
17113
17703
  return {
17114
- relativeDirPathFromCwd: join116(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName)
17704
+ relativeDirPathFromCwd: join119(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName)
17115
17705
  };
17116
17706
  } catch (error) {
17117
17707
  throw new Error(
@@ -17122,29 +17712,29 @@ async function deleteSkill({
17122
17712
  );
17123
17713
  }
17124
17714
  }
17125
- var McpSkillFileSchema = z59.object({
17126
- name: z59.string(),
17127
- body: z59.string()
17715
+ var McpSkillFileSchema = z60.object({
17716
+ name: z60.string(),
17717
+ body: z60.string()
17128
17718
  });
17129
17719
  var skillToolSchemas = {
17130
- listSkills: z59.object({}),
17131
- getSkill: z59.object({
17132
- relativeDirPathFromCwd: z59.string()
17720
+ listSkills: z60.object({}),
17721
+ getSkill: z60.object({
17722
+ relativeDirPathFromCwd: z60.string()
17133
17723
  }),
17134
- putSkill: z59.object({
17135
- relativeDirPathFromCwd: z59.string(),
17724
+ putSkill: z60.object({
17725
+ relativeDirPathFromCwd: z60.string(),
17136
17726
  frontmatter: RulesyncSkillFrontmatterSchema,
17137
- body: z59.string(),
17138
- otherFiles: z59.optional(z59.array(McpSkillFileSchema))
17727
+ body: z60.string(),
17728
+ otherFiles: z60.optional(z60.array(McpSkillFileSchema))
17139
17729
  }),
17140
- deleteSkill: z59.object({
17141
- relativeDirPathFromCwd: z59.string()
17730
+ deleteSkill: z60.object({
17731
+ relativeDirPathFromCwd: z60.string()
17142
17732
  })
17143
17733
  };
17144
17734
  var skillTools = {
17145
17735
  listSkills: {
17146
17736
  name: "listSkills",
17147
- description: `List all skills from ${join116(RULESYNC_SKILLS_RELATIVE_DIR_PATH, "*", SKILL_FILE_NAME)} with their frontmatter.`,
17737
+ description: `List all skills from ${join119(RULESYNC_SKILLS_RELATIVE_DIR_PATH, "*", SKILL_FILE_NAME)} with their frontmatter.`,
17148
17738
  parameters: skillToolSchemas.listSkills,
17149
17739
  execute: async () => {
17150
17740
  const skills = await listSkills();
@@ -17187,12 +17777,12 @@ var skillTools = {
17187
17777
  };
17188
17778
 
17189
17779
  // src/mcp/subagents.ts
17190
- import { basename as basename27, join as join117 } from "path";
17191
- import { z as z60 } from "zod/mini";
17780
+ import { basename as basename28, join as join120 } from "path";
17781
+ import { z as z61 } from "zod/mini";
17192
17782
  var maxSubagentSizeBytes = 1024 * 1024;
17193
17783
  var maxSubagentsCount = 1e3;
17194
17784
  async function listSubagents() {
17195
- const subagentsDir = join117(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
17785
+ const subagentsDir = join120(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
17196
17786
  try {
17197
17787
  const files = await listDirectoryFiles(subagentsDir);
17198
17788
  const mdFiles = files.filter((file) => file.endsWith(".md"));
@@ -17205,7 +17795,7 @@ async function listSubagents() {
17205
17795
  });
17206
17796
  const frontmatter = subagent.getFrontmatter();
17207
17797
  return {
17208
- relativePathFromCwd: join117(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, file),
17798
+ relativePathFromCwd: join120(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, file),
17209
17799
  frontmatter
17210
17800
  };
17211
17801
  } catch (error) {
@@ -17218,7 +17808,9 @@ async function listSubagents() {
17218
17808
  (subagent) => subagent !== null
17219
17809
  );
17220
17810
  } catch (error) {
17221
- logger.error(`Failed to read subagents directory: ${formatError(error)}`);
17811
+ logger.error(
17812
+ `Failed to read subagents directory (${RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH}): ${formatError(error)}`
17813
+ );
17222
17814
  return [];
17223
17815
  }
17224
17816
  }
@@ -17227,14 +17819,14 @@ async function getSubagent({ relativePathFromCwd }) {
17227
17819
  relativePath: relativePathFromCwd,
17228
17820
  intendedRootDir: process.cwd()
17229
17821
  });
17230
- const filename = basename27(relativePathFromCwd);
17822
+ const filename = basename28(relativePathFromCwd);
17231
17823
  try {
17232
17824
  const subagent = await RulesyncSubagent.fromFile({
17233
17825
  relativeFilePath: filename,
17234
17826
  validate: true
17235
17827
  });
17236
17828
  return {
17237
- relativePathFromCwd: join117(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
17829
+ relativePathFromCwd: join120(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
17238
17830
  frontmatter: subagent.getFrontmatter(),
17239
17831
  body: subagent.getBody()
17240
17832
  };
@@ -17253,20 +17845,22 @@ async function putSubagent({
17253
17845
  relativePath: relativePathFromCwd,
17254
17846
  intendedRootDir: process.cwd()
17255
17847
  });
17256
- const filename = basename27(relativePathFromCwd);
17848
+ const filename = basename28(relativePathFromCwd);
17257
17849
  const estimatedSize = JSON.stringify(frontmatter).length + body.length;
17258
17850
  if (estimatedSize > maxSubagentSizeBytes) {
17259
17851
  throw new Error(
17260
- `Subagent size ${estimatedSize} bytes exceeds maximum ${maxSubagentSizeBytes} bytes (1MB)`
17852
+ `Subagent size ${estimatedSize} bytes exceeds maximum ${maxSubagentSizeBytes} bytes (1MB) for ${relativePathFromCwd}`
17261
17853
  );
17262
17854
  }
17263
17855
  try {
17264
17856
  const existingSubagents = await listSubagents();
17265
17857
  const isUpdate = existingSubagents.some(
17266
- (subagent2) => subagent2.relativePathFromCwd === join117(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
17858
+ (subagent2) => subagent2.relativePathFromCwd === join120(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
17267
17859
  );
17268
17860
  if (!isUpdate && existingSubagents.length >= maxSubagentsCount) {
17269
- throw new Error(`Maximum number of subagents (${maxSubagentsCount}) reached`);
17861
+ throw new Error(
17862
+ `Maximum number of subagents (${maxSubagentsCount}) reached in ${RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH}`
17863
+ );
17270
17864
  }
17271
17865
  const subagent = new RulesyncSubagent({
17272
17866
  baseDir: process.cwd(),
@@ -17276,11 +17870,11 @@ async function putSubagent({
17276
17870
  body,
17277
17871
  validate: true
17278
17872
  });
17279
- const subagentsDir = join117(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
17873
+ const subagentsDir = join120(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
17280
17874
  await ensureDir(subagentsDir);
17281
17875
  await writeFileContent(subagent.getFilePath(), subagent.getFileContent());
17282
17876
  return {
17283
- relativePathFromCwd: join117(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
17877
+ relativePathFromCwd: join120(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
17284
17878
  frontmatter: subagent.getFrontmatter(),
17285
17879
  body: subagent.getBody()
17286
17880
  };
@@ -17295,12 +17889,12 @@ async function deleteSubagent({ relativePathFromCwd }) {
17295
17889
  relativePath: relativePathFromCwd,
17296
17890
  intendedRootDir: process.cwd()
17297
17891
  });
17298
- const filename = basename27(relativePathFromCwd);
17299
- const fullPath = join117(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename);
17892
+ const filename = basename28(relativePathFromCwd);
17893
+ const fullPath = join120(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename);
17300
17894
  try {
17301
17895
  await removeFile(fullPath);
17302
17896
  return {
17303
- relativePathFromCwd: join117(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
17897
+ relativePathFromCwd: join120(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
17304
17898
  };
17305
17899
  } catch (error) {
17306
17900
  throw new Error(
@@ -17312,23 +17906,23 @@ async function deleteSubagent({ relativePathFromCwd }) {
17312
17906
  }
17313
17907
  }
17314
17908
  var subagentToolSchemas = {
17315
- listSubagents: z60.object({}),
17316
- getSubagent: z60.object({
17317
- relativePathFromCwd: z60.string()
17909
+ listSubagents: z61.object({}),
17910
+ getSubagent: z61.object({
17911
+ relativePathFromCwd: z61.string()
17318
17912
  }),
17319
- putSubagent: z60.object({
17320
- relativePathFromCwd: z60.string(),
17913
+ putSubagent: z61.object({
17914
+ relativePathFromCwd: z61.string(),
17321
17915
  frontmatter: RulesyncSubagentFrontmatterSchema,
17322
- body: z60.string()
17916
+ body: z61.string()
17323
17917
  }),
17324
- deleteSubagent: z60.object({
17325
- relativePathFromCwd: z60.string()
17918
+ deleteSubagent: z61.object({
17919
+ relativePathFromCwd: z61.string()
17326
17920
  })
17327
17921
  };
17328
17922
  var subagentTools = {
17329
17923
  listSubagents: {
17330
17924
  name: "listSubagents",
17331
- description: `List all subagents from ${join117(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
17925
+ description: `List all subagents from ${join120(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
17332
17926
  parameters: subagentToolSchemas.listSubagents,
17333
17927
  execute: async () => {
17334
17928
  const subagents = await listSubagents();
@@ -17370,7 +17964,7 @@ var subagentTools = {
17370
17964
  };
17371
17965
 
17372
17966
  // src/mcp/tools.ts
17373
- var rulesyncFeatureSchema = z61.enum([
17967
+ var rulesyncFeatureSchema = z62.enum([
17374
17968
  "rule",
17375
17969
  "command",
17376
17970
  "subagent",
@@ -17380,21 +17974,21 @@ var rulesyncFeatureSchema = z61.enum([
17380
17974
  "generate",
17381
17975
  "import"
17382
17976
  ]);
17383
- var rulesyncOperationSchema = z61.enum(["list", "get", "put", "delete", "run"]);
17384
- var skillFileSchema = z61.object({
17385
- name: z61.string(),
17386
- body: z61.string()
17977
+ var rulesyncOperationSchema = z62.enum(["list", "get", "put", "delete", "run"]);
17978
+ var skillFileSchema = z62.object({
17979
+ name: z62.string(),
17980
+ body: z62.string()
17387
17981
  });
17388
- var rulesyncToolSchema = z61.object({
17982
+ var rulesyncToolSchema = z62.object({
17389
17983
  feature: rulesyncFeatureSchema,
17390
17984
  operation: rulesyncOperationSchema,
17391
- targetPathFromCwd: z61.optional(z61.string()),
17392
- frontmatter: z61.optional(z61.unknown()),
17393
- body: z61.optional(z61.string()),
17394
- otherFiles: z61.optional(z61.array(skillFileSchema)),
17395
- content: z61.optional(z61.string()),
17396
- generateOptions: z61.optional(generateOptionsSchema),
17397
- importOptions: z61.optional(importOptionsSchema)
17985
+ targetPathFromCwd: z62.optional(z62.string()),
17986
+ frontmatter: z62.optional(z62.unknown()),
17987
+ body: z62.optional(z62.string()),
17988
+ otherFiles: z62.optional(z62.array(skillFileSchema)),
17989
+ content: z62.optional(z62.string()),
17990
+ generateOptions: z62.optional(generateOptionsSchema),
17991
+ importOptions: z62.optional(importOptionsSchema)
17398
17992
  });
17399
17993
  var supportedOperationsByFeature = {
17400
17994
  rule: ["list", "get", "put", "delete"],
@@ -17723,7 +18317,7 @@ async function downloadFile(url, destPath) {
17723
18317
  redirect: "follow"
17724
18318
  });
17725
18319
  if (!response.ok) {
17726
- throw new Error(`Failed to download: HTTP ${response.status}`);
18320
+ throw new Error(`Failed to download ${url}: HTTP ${response.status}`);
17727
18321
  }
17728
18322
  if (response.url) {
17729
18323
  validateDownloadUrl(response.url);
@@ -17953,7 +18547,7 @@ async function updateCommand(currentVersion, options) {
17953
18547
  }
17954
18548
 
17955
18549
  // src/cli/index.ts
17956
- var getVersion = () => "7.0.0";
18550
+ var getVersion = () => "7.2.0";
17957
18551
  var main = async () => {
17958
18552
  const program = new Command();
17959
18553
  const version = getVersion();
@@ -18026,6 +18620,21 @@ var main = async () => {
18026
18620
  process.exit(1);
18027
18621
  }
18028
18622
  });
18623
+ program.command("install").description("Install skills from declarative sources in rulesync.jsonc").option("--update", "Force re-resolve all source refs, ignoring lockfile").option("--frozen", "Fail if lockfile is missing or out of sync (for CI)").option("--token <token>", "GitHub token for private repos").option("-c, --config <path>", "Path to configuration file").option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(async (options) => {
18624
+ try {
18625
+ await installCommand({
18626
+ update: options.update,
18627
+ frozen: options.frozen,
18628
+ token: options.token,
18629
+ configPath: options.config,
18630
+ verbose: options.verbose,
18631
+ silent: options.silent
18632
+ });
18633
+ } catch (error) {
18634
+ logger.error(formatError(error));
18635
+ process.exit(1);
18636
+ }
18637
+ });
18029
18638
  program.command("generate").description("Generate configuration files for AI tools").option(
18030
18639
  "-t, --targets <tools>",
18031
18640
  "Comma-separated list of tools to generate for (e.g., 'copilot,cursor,cline' or '*' for all)",