rulesync 8.6.0 → 8.7.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.
@@ -3079,7 +3079,7 @@ var CommandsProcessor = class extends FeatureProcessor {
3079
3079
  };
3080
3080
 
3081
3081
  // src/features/hooks/hooks-processor.ts
3082
- var import_mini19 = require("zod/mini");
3082
+ var import_mini18 = require("zod/mini");
3083
3083
 
3084
3084
  // src/types/hooks.ts
3085
3085
  var import_mini15 = require("zod/mini");
@@ -3383,22 +3383,34 @@ function canonicalToToolHooks({
3383
3383
  `matcher "${matcherKey}" on "${eventName}" hook will be ignored \u2014 this event does not support matchers`
3384
3384
  );
3385
3385
  }
3386
- const hooks = defs.map((def) => {
3386
+ const hooks = [];
3387
+ for (const def of defs) {
3388
+ const hookType = def.type ?? "command";
3389
+ if (converterConfig.supportedHookTypes && !converterConfig.supportedHookTypes.has(hookType)) {
3390
+ continue;
3391
+ }
3387
3392
  const commandText = def.command;
3388
3393
  const trimmedCommand = typeof commandText === "string" ? commandText.trimStart() : void 0;
3389
- const shouldPrefix = typeof trimmedCommand === "string" && !trimmedCommand.startsWith("$") && (!converterConfig.prefixDotRelativeCommandsOnly || trimmedCommand.startsWith("."));
3394
+ const shouldPrefix = converterConfig.projectDirVar !== "" && typeof trimmedCommand === "string" && !trimmedCommand.startsWith("$") && (!converterConfig.prefixDotRelativeCommandsOnly || trimmedCommand.startsWith("."));
3390
3395
  const command = shouldPrefix && typeof trimmedCommand === "string" ? `${converterConfig.projectDirVar}/${trimmedCommand.replace(/^\.\//, "")}` : def.command;
3391
- return {
3392
- type: def.type ?? "command",
3396
+ hooks.push({
3397
+ type: hookType,
3393
3398
  ...command !== void 0 && command !== null && { command },
3394
3399
  ...def.timeout !== void 0 && def.timeout !== null && { timeout: def.timeout },
3395
- ...def.prompt !== void 0 && def.prompt !== null && { prompt: def.prompt }
3396
- };
3397
- });
3400
+ ...def.prompt !== void 0 && def.prompt !== null && { prompt: def.prompt },
3401
+ ...converterConfig.passthroughFields?.includes("name") && def.name !== void 0 && def.name !== null && { name: def.name },
3402
+ ...converterConfig.passthroughFields?.includes("description") && def.description !== void 0 && def.description !== null && { description: def.description }
3403
+ });
3404
+ }
3405
+ if (hooks.length === 0) {
3406
+ continue;
3407
+ }
3398
3408
  const includeMatcher = matcherKey && !isNoMatcherEvent;
3399
3409
  entries.push(includeMatcher ? { matcher: matcherKey, hooks } : { hooks });
3400
3410
  }
3401
- result[toolEventName] = entries;
3411
+ if (entries.length > 0) {
3412
+ result[toolEventName] = entries;
3413
+ }
3402
3414
  }
3403
3415
  return result;
3404
3416
  }
@@ -3419,7 +3431,7 @@ function toolHooksToCanonical({
3419
3431
  const hookDefs = rawEntry.hooks ?? [];
3420
3432
  for (const h of hookDefs) {
3421
3433
  const cmd = typeof h.command === "string" ? h.command : void 0;
3422
- const command = typeof cmd === "string" && cmd.includes(`${converterConfig.projectDirVar}/`) ? cmd.replace(
3434
+ const command = converterConfig.projectDirVar !== "" && typeof cmd === "string" && cmd.includes(`${converterConfig.projectDirVar}/`) ? cmd.replace(
3423
3435
  new RegExp(
3424
3436
  `^${converterConfig.projectDirVar.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\/?`
3425
3437
  ),
@@ -3433,6 +3445,8 @@ function toolHooksToCanonical({
3433
3445
  ...command !== void 0 && command !== null && { command },
3434
3446
  ...timeout !== void 0 && timeout !== null && { timeout },
3435
3447
  ...prompt !== void 0 && prompt !== null && { prompt },
3448
+ ...converterConfig.passthroughFields?.includes("name") && typeof h.name === "string" && { name: h.name },
3449
+ ...converterConfig.passthroughFields?.includes("description") && typeof h.description === "string" && { description: h.description },
3436
3450
  ...rawEntry.matcher !== void 0 && rawEntry.matcher !== null && rawEntry.matcher !== "" && { matcher: rawEntry.matcher }
3437
3451
  });
3438
3452
  }
@@ -3531,6 +3545,9 @@ var ToolHooks = class extends ToolFile {
3531
3545
  static forDeletion(_params) {
3532
3546
  throw new Error("Please implement this method in the subclass.");
3533
3547
  }
3548
+ static async getAuxiliaryFiles(_params) {
3549
+ return [];
3550
+ }
3534
3551
  };
3535
3552
 
3536
3553
  // src/features/hooks/claudecode-hooks.ts
@@ -3652,89 +3669,28 @@ var ClaudecodeHooks = class _ClaudecodeHooks extends ToolHooks {
3652
3669
  // src/features/hooks/codexcli-hooks.ts
3653
3670
  var import_node_path24 = require("path");
3654
3671
  var smolToml2 = __toESM(require("smol-toml"), 1);
3655
- var import_mini16 = require("zod/mini");
3656
- function canonicalToCodexcliHooks(config) {
3657
- const codexSupported = new Set(CODEXCLI_HOOK_EVENTS);
3658
- const sharedHooks = {};
3659
- for (const [event, defs] of Object.entries(config.hooks)) {
3660
- if (codexSupported.has(event)) {
3661
- sharedHooks[event] = defs;
3662
- }
3663
- }
3664
- const effectiveHooks = {
3665
- ...sharedHooks,
3666
- ...config.codexcli?.hooks
3667
- };
3668
- const codex = {};
3669
- for (const [eventName, definitions] of Object.entries(effectiveHooks)) {
3670
- const codexEventName = CANONICAL_TO_CODEXCLI_EVENT_NAMES[eventName] ?? eventName;
3671
- const byMatcher = /* @__PURE__ */ new Map();
3672
- for (const def of definitions) {
3673
- const key = def.matcher ?? "";
3674
- const list = byMatcher.get(key);
3675
- if (list) list.push(def);
3676
- else byMatcher.set(key, [def]);
3677
- }
3678
- const entries = [];
3679
- for (const [matcherKey, defs] of byMatcher) {
3680
- const commandDefs = defs.filter((def) => !def.type || def.type === "command");
3681
- if (commandDefs.length === 0) continue;
3682
- const hooks = commandDefs.map((def) => ({
3683
- type: "command",
3684
- ...def.command !== void 0 && def.command !== null && { command: def.command },
3685
- ...def.timeout !== void 0 && def.timeout !== null && { timeout: def.timeout }
3686
- }));
3687
- entries.push(matcherKey ? { matcher: matcherKey, hooks } : { hooks });
3688
- }
3689
- if (entries.length > 0) {
3690
- codex[codexEventName] = entries;
3691
- }
3692
- }
3693
- return codex;
3694
- }
3695
- var CodexHookEntrySchema = import_mini16.z.looseObject({
3696
- type: import_mini16.z.optional(import_mini16.z.string()),
3697
- command: import_mini16.z.optional(import_mini16.z.string()),
3698
- timeout: import_mini16.z.optional(import_mini16.z.number())
3699
- });
3700
- var CodexMatcherEntrySchema = import_mini16.z.looseObject({
3701
- matcher: import_mini16.z.optional(import_mini16.z.string()),
3702
- hooks: import_mini16.z.optional(import_mini16.z.array(CodexHookEntrySchema))
3703
- });
3704
- function codexcliHooksToCanonical(codexHooks) {
3705
- if (codexHooks === null || codexHooks === void 0 || typeof codexHooks !== "object") {
3706
- return {};
3707
- }
3708
- const canonical = {};
3709
- for (const [codexEventName, matcherEntries] of Object.entries(codexHooks)) {
3710
- const eventName = CODEXCLI_TO_CANONICAL_EVENT_NAMES[codexEventName] ?? codexEventName;
3711
- if (!Array.isArray(matcherEntries)) continue;
3712
- const defs = [];
3713
- for (const rawEntry of matcherEntries) {
3714
- const parseResult = CodexMatcherEntrySchema.safeParse(rawEntry);
3715
- if (!parseResult.success) continue;
3716
- const entry = parseResult.data;
3717
- const hooks = entry.hooks ?? [];
3718
- for (const h of hooks) {
3719
- const hookType = h.type === "command" || h.type === "prompt" ? h.type : "command";
3720
- defs.push({
3721
- type: hookType,
3722
- ...h.command !== void 0 && h.command !== null && { command: h.command },
3723
- ...h.timeout !== void 0 && h.timeout !== null && { timeout: h.timeout },
3724
- ...entry.matcher !== void 0 && entry.matcher !== null && entry.matcher !== "" && { matcher: entry.matcher }
3725
- });
3726
- }
3727
- }
3728
- if (defs.length > 0) {
3729
- canonical[eventName] = defs;
3730
- }
3731
- }
3732
- return canonical;
3733
- }
3672
+ var CODEXCLI_CONVERTER_CONFIG = {
3673
+ supportedEvents: CODEXCLI_HOOK_EVENTS,
3674
+ canonicalToToolEventNames: CANONICAL_TO_CODEXCLI_EVENT_NAMES,
3675
+ toolToCanonicalEventNames: CODEXCLI_TO_CANONICAL_EVENT_NAMES,
3676
+ projectDirVar: "",
3677
+ supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
3678
+ passthroughFields: ["name", "description"]
3679
+ };
3734
3680
  async function buildCodexConfigTomlContent({ baseDir }) {
3735
3681
  const configPath = (0, import_node_path24.join)(baseDir, ".codex", "config.toml");
3736
3682
  const existingContent = await readFileContentOrNull(configPath) ?? smolToml2.stringify({});
3737
- const configToml = smolToml2.parse(existingContent);
3683
+ let configToml;
3684
+ try {
3685
+ configToml = smolToml2.parse(existingContent);
3686
+ } catch (error) {
3687
+ throw new Error(
3688
+ `Failed to parse existing Codex CLI config at ${configPath}: ${formatError(error)}`,
3689
+ {
3690
+ cause: error
3691
+ }
3692
+ );
3693
+ }
3738
3694
  if (typeof configToml.features !== "object" || configToml.features === null) {
3739
3695
  configToml.features = {};
3740
3696
  }
@@ -3789,7 +3745,11 @@ var CodexcliHooks = class _CodexcliHooks extends ToolHooks {
3789
3745
  }) {
3790
3746
  const paths = _CodexcliHooks.getSettablePaths({ global });
3791
3747
  const config = rulesyncHooks.getJson();
3792
- const codexHooks = canonicalToCodexcliHooks(config);
3748
+ const codexHooks = canonicalToToolHooks({
3749
+ config,
3750
+ toolOverrideHooks: config.codexcli?.hooks,
3751
+ converterConfig: CODEXCLI_CONVERTER_CONFIG
3752
+ });
3793
3753
  const fileContent = JSON.stringify({ hooks: codexHooks }, null, 2);
3794
3754
  return new _CodexcliHooks({
3795
3755
  baseDir,
@@ -3811,7 +3771,10 @@ var CodexcliHooks = class _CodexcliHooks extends ToolHooks {
3811
3771
  }
3812
3772
  );
3813
3773
  }
3814
- const hooks = codexcliHooksToCanonical(parsed.hooks);
3774
+ const hooks = toolHooksToCanonical({
3775
+ hooks: parsed.hooks,
3776
+ converterConfig: CODEXCLI_CONVERTER_CONFIG
3777
+ });
3815
3778
  return this.toRulesyncHooksDefault({
3816
3779
  fileContent: JSON.stringify({ version: 1, hooks }, null, 2)
3817
3780
  });
@@ -3832,16 +3795,21 @@ var CodexcliHooks = class _CodexcliHooks extends ToolHooks {
3832
3795
  validate: false
3833
3796
  });
3834
3797
  }
3798
+ static async getAuxiliaryFiles({
3799
+ baseDir = process.cwd()
3800
+ } = {}) {
3801
+ return [await CodexcliConfigToml.fromBaseDir({ baseDir })];
3802
+ }
3835
3803
  };
3836
3804
 
3837
3805
  // src/features/hooks/copilot-hooks.ts
3838
3806
  var import_node_path25 = require("path");
3839
- var import_mini17 = require("zod/mini");
3840
- var CopilotHookEntrySchema = import_mini17.z.looseObject({
3841
- type: import_mini17.z.string(),
3842
- bash: import_mini17.z.optional(import_mini17.z.string()),
3843
- powershell: import_mini17.z.optional(import_mini17.z.string()),
3844
- timeoutSec: import_mini17.z.optional(import_mini17.z.number())
3807
+ var import_mini16 = require("zod/mini");
3808
+ var CopilotHookEntrySchema = import_mini16.z.looseObject({
3809
+ type: import_mini16.z.string(),
3810
+ bash: import_mini16.z.optional(import_mini16.z.string()),
3811
+ powershell: import_mini16.z.optional(import_mini16.z.string()),
3812
+ timeoutSec: import_mini16.z.optional(import_mini16.z.number())
3845
3813
  });
3846
3814
  function canonicalToCopilotHooks(config) {
3847
3815
  const canonicalSchemaKeys = Object.keys(HookDefinitionSchema.shape);
@@ -4372,7 +4340,7 @@ var FactorydroidHooks = class _FactorydroidHooks extends ToolHooks {
4372
4340
 
4373
4341
  // src/features/hooks/geminicli-hooks.ts
4374
4342
  var import_node_path29 = require("path");
4375
- var import_mini18 = require("zod/mini");
4343
+ var import_mini17 = require("zod/mini");
4376
4344
  function canonicalToGeminicliHooks(config) {
4377
4345
  const geminiSupported = new Set(GEMINICLI_HOOK_EVENTS);
4378
4346
  const sharedHooks = {};
@@ -4416,16 +4384,16 @@ function canonicalToGeminicliHooks(config) {
4416
4384
  }
4417
4385
  return gemini;
4418
4386
  }
4419
- var GeminiHookEntrySchema = import_mini18.z.looseObject({
4420
- type: import_mini18.z.optional(import_mini18.z.string()),
4421
- command: import_mini18.z.optional(import_mini18.z.string()),
4422
- timeout: import_mini18.z.optional(import_mini18.z.number()),
4423
- name: import_mini18.z.optional(import_mini18.z.string()),
4424
- description: import_mini18.z.optional(import_mini18.z.string())
4387
+ var GeminiHookEntrySchema = import_mini17.z.looseObject({
4388
+ type: import_mini17.z.optional(import_mini17.z.string()),
4389
+ command: import_mini17.z.optional(import_mini17.z.string()),
4390
+ timeout: import_mini17.z.optional(import_mini17.z.number()),
4391
+ name: import_mini17.z.optional(import_mini17.z.string()),
4392
+ description: import_mini17.z.optional(import_mini17.z.string())
4425
4393
  });
4426
- var GeminiMatcherEntrySchema = import_mini18.z.looseObject({
4427
- matcher: import_mini18.z.optional(import_mini18.z.string()),
4428
- hooks: import_mini18.z.optional(import_mini18.z.array(GeminiHookEntrySchema))
4394
+ var GeminiMatcherEntrySchema = import_mini17.z.looseObject({
4395
+ matcher: import_mini17.z.optional(import_mini17.z.string()),
4396
+ hooks: import_mini17.z.optional(import_mini17.z.array(GeminiHookEntrySchema))
4429
4397
  });
4430
4398
  function geminiHooksToCanonical(geminiHooks) {
4431
4399
  if (geminiHooks === null || geminiHooks === void 0 || typeof geminiHooks !== "object") {
@@ -4809,7 +4777,7 @@ var hooksProcessorToolTargetTuple = [
4809
4777
  "geminicli",
4810
4778
  "deepagents"
4811
4779
  ];
4812
- var HooksProcessorToolTargetSchema = import_mini19.z.enum(hooksProcessorToolTargetTuple);
4780
+ var HooksProcessorToolTargetSchema = import_mini18.z.enum(hooksProcessorToolTargetTuple);
4813
4781
  var toolHooksFactories = /* @__PURE__ */ new Map([
4814
4782
  [
4815
4783
  "cursor",
@@ -5068,8 +5036,12 @@ var HooksProcessor = class extends FeatureProcessor {
5068
5036
  global: this.global
5069
5037
  });
5070
5038
  const result = [toolHooks];
5071
- if (this.toolTarget === "codexcli") {
5072
- result.push(await CodexcliConfigToml.fromBaseDir({ baseDir: this.baseDir }));
5039
+ const auxiliaryFiles = await factory.class.getAuxiliaryFiles?.({
5040
+ baseDir: this.baseDir,
5041
+ global: this.global
5042
+ });
5043
+ if (auxiliaryFiles && auxiliaryFiles.length > 0) {
5044
+ result.push(...auxiliaryFiles);
5073
5045
  }
5074
5046
  return result;
5075
5047
  }
@@ -5089,7 +5061,7 @@ var HooksProcessor = class extends FeatureProcessor {
5089
5061
  };
5090
5062
 
5091
5063
  // src/features/ignore/ignore-processor.ts
5092
- var import_mini21 = require("zod/mini");
5064
+ var import_mini20 = require("zod/mini");
5093
5065
 
5094
5066
  // src/features/ignore/augmentcode-ignore.ts
5095
5067
  var import_node_path33 = require("path");
@@ -5268,12 +5240,12 @@ var AugmentcodeIgnore = class _AugmentcodeIgnore extends ToolIgnore {
5268
5240
  // src/features/ignore/claudecode-ignore.ts
5269
5241
  var import_node_path34 = require("path");
5270
5242
  var import_es_toolkit2 = require("es-toolkit");
5271
- var import_mini20 = require("zod/mini");
5243
+ var import_mini19 = require("zod/mini");
5272
5244
  var SHARED_SETTINGS_FILE = "settings.json";
5273
5245
  var LOCAL_SETTINGS_FILE = "settings.local.json";
5274
5246
  var DEFAULT_FILE_MODE = "shared";
5275
- var ClaudecodeIgnoreOptionsSchema = import_mini20.z.looseObject({
5276
- fileMode: import_mini20.z.optional(import_mini20.z.enum(["shared", "local"]))
5247
+ var ClaudecodeIgnoreOptionsSchema = import_mini19.z.looseObject({
5248
+ fileMode: import_mini19.z.optional(import_mini19.z.enum(["shared", "local"]))
5277
5249
  });
5278
5250
  var resolveFileMode = (options) => {
5279
5251
  if (!options) return DEFAULT_FILE_MODE;
@@ -6117,7 +6089,7 @@ var ignoreProcessorToolTargets = [
6117
6089
  "windsurf",
6118
6090
  "zed"
6119
6091
  ];
6120
- var IgnoreProcessorToolTargetSchema = import_mini21.z.enum(ignoreProcessorToolTargets);
6092
+ var IgnoreProcessorToolTargetSchema = import_mini20.z.enum(ignoreProcessorToolTargets);
6121
6093
  var toolIgnoreFactories = /* @__PURE__ */ new Map([
6122
6094
  ["augmentcode", { class: AugmentcodeIgnore }],
6123
6095
  ["claudecode", { class: ClaudecodeIgnore }],
@@ -6260,7 +6232,7 @@ var IgnoreProcessor = class extends FeatureProcessor {
6260
6232
  };
6261
6233
 
6262
6234
  // src/features/mcp/mcp-processor.ts
6263
- var import_mini26 = require("zod/mini");
6235
+ var import_mini25 = require("zod/mini");
6264
6236
 
6265
6237
  // src/features/mcp/claudecode-mcp.ts
6266
6238
  var import_node_path47 = require("path");
@@ -6268,47 +6240,47 @@ var import_node_path47 = require("path");
6268
6240
  // src/features/mcp/rulesync-mcp.ts
6269
6241
  var import_node_path46 = require("path");
6270
6242
  var import_object = require("es-toolkit/object");
6271
- var import_mini23 = require("zod/mini");
6243
+ var import_mini22 = require("zod/mini");
6272
6244
 
6273
6245
  // src/types/mcp.ts
6274
- var import_mini22 = require("zod/mini");
6275
- var McpServerSchema = import_mini22.z.looseObject({
6276
- type: import_mini22.z.optional(import_mini22.z.enum(["local", "stdio", "sse", "http"])),
6277
- command: import_mini22.z.optional(import_mini22.z.union([import_mini22.z.string(), import_mini22.z.array(import_mini22.z.string())])),
6278
- args: import_mini22.z.optional(import_mini22.z.array(import_mini22.z.string())),
6279
- url: import_mini22.z.optional(import_mini22.z.string()),
6280
- httpUrl: import_mini22.z.optional(import_mini22.z.string()),
6281
- env: import_mini22.z.optional(import_mini22.z.record(import_mini22.z.string(), import_mini22.z.string())),
6282
- disabled: import_mini22.z.optional(import_mini22.z.boolean()),
6283
- networkTimeout: import_mini22.z.optional(import_mini22.z.number()),
6284
- timeout: import_mini22.z.optional(import_mini22.z.number()),
6285
- trust: import_mini22.z.optional(import_mini22.z.boolean()),
6286
- cwd: import_mini22.z.optional(import_mini22.z.string()),
6287
- transport: import_mini22.z.optional(import_mini22.z.enum(["local", "stdio", "sse", "http"])),
6288
- alwaysAllow: import_mini22.z.optional(import_mini22.z.array(import_mini22.z.string())),
6289
- tools: import_mini22.z.optional(import_mini22.z.array(import_mini22.z.string())),
6290
- kiroAutoApprove: import_mini22.z.optional(import_mini22.z.array(import_mini22.z.string())),
6291
- kiroAutoBlock: import_mini22.z.optional(import_mini22.z.array(import_mini22.z.string())),
6292
- headers: import_mini22.z.optional(import_mini22.z.record(import_mini22.z.string(), import_mini22.z.string())),
6293
- enabledTools: import_mini22.z.optional(import_mini22.z.array(import_mini22.z.string())),
6294
- disabledTools: import_mini22.z.optional(import_mini22.z.array(import_mini22.z.string()))
6246
+ var import_mini21 = require("zod/mini");
6247
+ var McpServerSchema = import_mini21.z.looseObject({
6248
+ type: import_mini21.z.optional(import_mini21.z.enum(["local", "stdio", "sse", "http"])),
6249
+ command: import_mini21.z.optional(import_mini21.z.union([import_mini21.z.string(), import_mini21.z.array(import_mini21.z.string())])),
6250
+ args: import_mini21.z.optional(import_mini21.z.array(import_mini21.z.string())),
6251
+ url: import_mini21.z.optional(import_mini21.z.string()),
6252
+ httpUrl: import_mini21.z.optional(import_mini21.z.string()),
6253
+ env: import_mini21.z.optional(import_mini21.z.record(import_mini21.z.string(), import_mini21.z.string())),
6254
+ disabled: import_mini21.z.optional(import_mini21.z.boolean()),
6255
+ networkTimeout: import_mini21.z.optional(import_mini21.z.number()),
6256
+ timeout: import_mini21.z.optional(import_mini21.z.number()),
6257
+ trust: import_mini21.z.optional(import_mini21.z.boolean()),
6258
+ cwd: import_mini21.z.optional(import_mini21.z.string()),
6259
+ transport: import_mini21.z.optional(import_mini21.z.enum(["local", "stdio", "sse", "http"])),
6260
+ alwaysAllow: import_mini21.z.optional(import_mini21.z.array(import_mini21.z.string())),
6261
+ tools: import_mini21.z.optional(import_mini21.z.array(import_mini21.z.string())),
6262
+ kiroAutoApprove: import_mini21.z.optional(import_mini21.z.array(import_mini21.z.string())),
6263
+ kiroAutoBlock: import_mini21.z.optional(import_mini21.z.array(import_mini21.z.string())),
6264
+ headers: import_mini21.z.optional(import_mini21.z.record(import_mini21.z.string(), import_mini21.z.string())),
6265
+ enabledTools: import_mini21.z.optional(import_mini21.z.array(import_mini21.z.string())),
6266
+ disabledTools: import_mini21.z.optional(import_mini21.z.array(import_mini21.z.string()))
6295
6267
  });
6296
- var McpServersSchema = import_mini22.z.record(import_mini22.z.string(), McpServerSchema);
6268
+ var McpServersSchema = import_mini21.z.record(import_mini21.z.string(), McpServerSchema);
6297
6269
  function isMcpServers(value) {
6298
6270
  return value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value);
6299
6271
  }
6300
6272
 
6301
6273
  // src/features/mcp/rulesync-mcp.ts
6302
- var RulesyncMcpServerSchema = import_mini23.z.extend(McpServerSchema, {
6303
- targets: import_mini23.z.optional(RulesyncTargetsSchema),
6304
- description: import_mini23.z.optional(import_mini23.z.string()),
6305
- exposed: import_mini23.z.optional(import_mini23.z.boolean())
6274
+ var RulesyncMcpServerSchema = import_mini22.z.extend(McpServerSchema, {
6275
+ targets: import_mini22.z.optional(RulesyncTargetsSchema),
6276
+ description: import_mini22.z.optional(import_mini22.z.string()),
6277
+ exposed: import_mini22.z.optional(import_mini22.z.boolean())
6306
6278
  });
6307
- var RulesyncMcpConfigSchema = import_mini23.z.object({
6308
- mcpServers: import_mini23.z.record(import_mini23.z.string(), RulesyncMcpServerSchema)
6279
+ var RulesyncMcpConfigSchema = import_mini22.z.object({
6280
+ mcpServers: import_mini22.z.record(import_mini22.z.string(), RulesyncMcpServerSchema)
6309
6281
  });
6310
- var RulesyncMcpFileSchema = import_mini23.z.looseObject({
6311
- $schema: import_mini23.z.optional(import_mini23.z.string()),
6282
+ var RulesyncMcpFileSchema = import_mini22.z.looseObject({
6283
+ $schema: import_mini22.z.optional(import_mini22.z.string()),
6312
6284
  ...RulesyncMcpConfigSchema.shape
6313
6285
  });
6314
6286
  var RulesyncMcp = class _RulesyncMcp extends RulesyncFile {
@@ -7511,25 +7483,25 @@ var JunieMcp = class _JunieMcp extends ToolMcp {
7511
7483
  // src/features/mcp/kilo-mcp.ts
7512
7484
  var import_node_path57 = require("path");
7513
7485
  var import_jsonc_parser2 = require("jsonc-parser");
7514
- var import_mini24 = require("zod/mini");
7515
- var KiloMcpLocalServerSchema = import_mini24.z.object({
7516
- type: import_mini24.z.literal("local"),
7517
- command: import_mini24.z.array(import_mini24.z.string()),
7518
- environment: import_mini24.z.optional(import_mini24.z.record(import_mini24.z.string(), import_mini24.z.string())),
7519
- enabled: import_mini24.z._default(import_mini24.z.boolean(), true),
7520
- cwd: import_mini24.z.optional(import_mini24.z.string())
7486
+ var import_mini23 = require("zod/mini");
7487
+ var KiloMcpLocalServerSchema = import_mini23.z.object({
7488
+ type: import_mini23.z.literal("local"),
7489
+ command: import_mini23.z.array(import_mini23.z.string()),
7490
+ environment: import_mini23.z.optional(import_mini23.z.record(import_mini23.z.string(), import_mini23.z.string())),
7491
+ enabled: import_mini23.z._default(import_mini23.z.boolean(), true),
7492
+ cwd: import_mini23.z.optional(import_mini23.z.string())
7521
7493
  });
7522
- var KiloMcpRemoteServerSchema = import_mini24.z.object({
7523
- type: import_mini24.z.literal("remote"),
7524
- url: import_mini24.z.string(),
7525
- headers: import_mini24.z.optional(import_mini24.z.record(import_mini24.z.string(), import_mini24.z.string())),
7526
- enabled: import_mini24.z._default(import_mini24.z.boolean(), true)
7494
+ var KiloMcpRemoteServerSchema = import_mini23.z.object({
7495
+ type: import_mini23.z.literal("remote"),
7496
+ url: import_mini23.z.string(),
7497
+ headers: import_mini23.z.optional(import_mini23.z.record(import_mini23.z.string(), import_mini23.z.string())),
7498
+ enabled: import_mini23.z._default(import_mini23.z.boolean(), true)
7527
7499
  });
7528
- var KiloMcpServerSchema = import_mini24.z.union([KiloMcpLocalServerSchema, KiloMcpRemoteServerSchema]);
7529
- var KiloConfigSchema = import_mini24.z.looseObject({
7530
- $schema: import_mini24.z.optional(import_mini24.z.string()),
7531
- mcp: import_mini24.z.optional(import_mini24.z.record(import_mini24.z.string(), KiloMcpServerSchema)),
7532
- tools: import_mini24.z.optional(import_mini24.z.record(import_mini24.z.string(), import_mini24.z.boolean()))
7500
+ var KiloMcpServerSchema = import_mini23.z.union([KiloMcpLocalServerSchema, KiloMcpRemoteServerSchema]);
7501
+ var KiloConfigSchema = import_mini23.z.looseObject({
7502
+ $schema: import_mini23.z.optional(import_mini23.z.string()),
7503
+ mcp: import_mini23.z.optional(import_mini23.z.record(import_mini23.z.string(), KiloMcpServerSchema)),
7504
+ tools: import_mini23.z.optional(import_mini23.z.record(import_mini23.z.string(), import_mini23.z.boolean()))
7533
7505
  });
7534
7506
  function convertFromKiloFormat(kiloMcp, tools) {
7535
7507
  return Object.fromEntries(
@@ -7826,28 +7798,28 @@ var KiroMcp = class _KiroMcp extends ToolMcp {
7826
7798
  // src/features/mcp/opencode-mcp.ts
7827
7799
  var import_node_path59 = require("path");
7828
7800
  var import_jsonc_parser3 = require("jsonc-parser");
7829
- var import_mini25 = require("zod/mini");
7830
- var OpencodeMcpLocalServerSchema = import_mini25.z.object({
7831
- type: import_mini25.z.literal("local"),
7832
- command: import_mini25.z.array(import_mini25.z.string()),
7833
- environment: import_mini25.z.optional(import_mini25.z.record(import_mini25.z.string(), import_mini25.z.string())),
7834
- enabled: import_mini25.z._default(import_mini25.z.boolean(), true),
7835
- cwd: import_mini25.z.optional(import_mini25.z.string())
7801
+ var import_mini24 = require("zod/mini");
7802
+ var OpencodeMcpLocalServerSchema = import_mini24.z.object({
7803
+ type: import_mini24.z.literal("local"),
7804
+ command: import_mini24.z.array(import_mini24.z.string()),
7805
+ environment: import_mini24.z.optional(import_mini24.z.record(import_mini24.z.string(), import_mini24.z.string())),
7806
+ enabled: import_mini24.z._default(import_mini24.z.boolean(), true),
7807
+ cwd: import_mini24.z.optional(import_mini24.z.string())
7836
7808
  });
7837
- var OpencodeMcpRemoteServerSchema = import_mini25.z.object({
7838
- type: import_mini25.z.literal("remote"),
7839
- url: import_mini25.z.string(),
7840
- headers: import_mini25.z.optional(import_mini25.z.record(import_mini25.z.string(), import_mini25.z.string())),
7841
- enabled: import_mini25.z._default(import_mini25.z.boolean(), true)
7809
+ var OpencodeMcpRemoteServerSchema = import_mini24.z.object({
7810
+ type: import_mini24.z.literal("remote"),
7811
+ url: import_mini24.z.string(),
7812
+ headers: import_mini24.z.optional(import_mini24.z.record(import_mini24.z.string(), import_mini24.z.string())),
7813
+ enabled: import_mini24.z._default(import_mini24.z.boolean(), true)
7842
7814
  });
7843
- var OpencodeMcpServerSchema = import_mini25.z.union([
7815
+ var OpencodeMcpServerSchema = import_mini24.z.union([
7844
7816
  OpencodeMcpLocalServerSchema,
7845
7817
  OpencodeMcpRemoteServerSchema
7846
7818
  ]);
7847
- var OpencodeConfigSchema = import_mini25.z.looseObject({
7848
- $schema: import_mini25.z.optional(import_mini25.z.string()),
7849
- mcp: import_mini25.z.optional(import_mini25.z.record(import_mini25.z.string(), OpencodeMcpServerSchema)),
7850
- tools: import_mini25.z.optional(import_mini25.z.record(import_mini25.z.string(), import_mini25.z.boolean()))
7819
+ var OpencodeConfigSchema = import_mini24.z.looseObject({
7820
+ $schema: import_mini24.z.optional(import_mini24.z.string()),
7821
+ mcp: import_mini24.z.optional(import_mini24.z.record(import_mini24.z.string(), OpencodeMcpServerSchema)),
7822
+ tools: import_mini24.z.optional(import_mini24.z.record(import_mini24.z.string(), import_mini24.z.boolean()))
7851
7823
  });
7852
7824
  function convertFromOpencodeFormat(opencodeMcp, tools) {
7853
7825
  return Object.fromEntries(
@@ -8318,7 +8290,7 @@ var mcpProcessorToolTargetTuple = [
8318
8290
  "roo",
8319
8291
  "rovodev"
8320
8292
  ];
8321
- var McpProcessorToolTargetSchema = import_mini26.z.enum(mcpProcessorToolTargetTuple);
8293
+ var McpProcessorToolTargetSchema = import_mini25.z.enum(mcpProcessorToolTargetTuple);
8322
8294
  var toolMcpFactories = /* @__PURE__ */ new Map([
8323
8295
  [
8324
8296
  "claudecode",
@@ -8659,7 +8631,7 @@ var McpProcessor = class extends FeatureProcessor {
8659
8631
  // src/features/rules/rules-processor.ts
8660
8632
  var import_node_path132 = require("path");
8661
8633
  var import_toon = require("@toon-format/toon");
8662
- var import_mini66 = require("zod/mini");
8634
+ var import_mini65 = require("zod/mini");
8663
8635
 
8664
8636
  // src/constants/general.ts
8665
8637
  var SKILL_FILE_NAME = "SKILL.md";
@@ -8669,7 +8641,7 @@ var import_node_path65 = require("path");
8669
8641
 
8670
8642
  // src/features/skills/simulated-skill.ts
8671
8643
  var import_node_path64 = require("path");
8672
- var import_mini27 = require("zod/mini");
8644
+ var import_mini26 = require("zod/mini");
8673
8645
 
8674
8646
  // src/features/skills/tool-skill.ts
8675
8647
  var import_node_path63 = require("path");
@@ -8906,9 +8878,9 @@ var ToolSkill = class extends AiDir {
8906
8878
  };
8907
8879
 
8908
8880
  // src/features/skills/simulated-skill.ts
8909
- var SimulatedSkillFrontmatterSchema = import_mini27.z.looseObject({
8910
- name: import_mini27.z.string(),
8911
- description: import_mini27.z.string()
8881
+ var SimulatedSkillFrontmatterSchema = import_mini26.z.looseObject({
8882
+ name: import_mini26.z.string(),
8883
+ description: import_mini26.z.string()
8912
8884
  });
8913
8885
  var SimulatedSkill = class extends ToolSkill {
8914
8886
  frontmatter;
@@ -9135,49 +9107,49 @@ var FactorydroidSkill = class _FactorydroidSkill extends SimulatedSkill {
9135
9107
 
9136
9108
  // src/features/skills/rovodev-skill.ts
9137
9109
  var import_node_path68 = require("path");
9138
- var import_mini29 = require("zod/mini");
9110
+ var import_mini28 = require("zod/mini");
9139
9111
 
9140
9112
  // src/features/skills/rulesync-skill.ts
9141
9113
  var import_node_path67 = require("path");
9142
- var import_mini28 = require("zod/mini");
9143
- var RulesyncSkillFrontmatterSchemaInternal = import_mini28.z.looseObject({
9144
- name: import_mini28.z.string(),
9145
- description: import_mini28.z.string(),
9146
- targets: import_mini28.z._default(RulesyncTargetsSchema, ["*"]),
9147
- claudecode: import_mini28.z.optional(
9148
- import_mini28.z.looseObject({
9149
- "allowed-tools": import_mini28.z.optional(import_mini28.z.array(import_mini28.z.string())),
9150
- model: import_mini28.z.optional(import_mini28.z.string()),
9151
- "disable-model-invocation": import_mini28.z.optional(import_mini28.z.boolean())
9114
+ var import_mini27 = require("zod/mini");
9115
+ var RulesyncSkillFrontmatterSchemaInternal = import_mini27.z.looseObject({
9116
+ name: import_mini27.z.string(),
9117
+ description: import_mini27.z.string(),
9118
+ targets: import_mini27.z._default(RulesyncTargetsSchema, ["*"]),
9119
+ claudecode: import_mini27.z.optional(
9120
+ import_mini27.z.looseObject({
9121
+ "allowed-tools": import_mini27.z.optional(import_mini27.z.array(import_mini27.z.string())),
9122
+ model: import_mini27.z.optional(import_mini27.z.string()),
9123
+ "disable-model-invocation": import_mini27.z.optional(import_mini27.z.boolean())
9152
9124
  })
9153
9125
  ),
9154
- codexcli: import_mini28.z.optional(
9155
- import_mini28.z.looseObject({
9156
- "short-description": import_mini28.z.optional(import_mini28.z.string())
9126
+ codexcli: import_mini27.z.optional(
9127
+ import_mini27.z.looseObject({
9128
+ "short-description": import_mini27.z.optional(import_mini27.z.string())
9157
9129
  })
9158
9130
  ),
9159
- opencode: import_mini28.z.optional(
9160
- import_mini28.z.looseObject({
9161
- "allowed-tools": import_mini28.z.optional(import_mini28.z.array(import_mini28.z.string()))
9131
+ opencode: import_mini27.z.optional(
9132
+ import_mini27.z.looseObject({
9133
+ "allowed-tools": import_mini27.z.optional(import_mini27.z.array(import_mini27.z.string()))
9162
9134
  })
9163
9135
  ),
9164
- kilo: import_mini28.z.optional(
9165
- import_mini28.z.looseObject({
9166
- "allowed-tools": import_mini28.z.optional(import_mini28.z.array(import_mini28.z.string()))
9136
+ kilo: import_mini27.z.optional(
9137
+ import_mini27.z.looseObject({
9138
+ "allowed-tools": import_mini27.z.optional(import_mini27.z.array(import_mini27.z.string()))
9167
9139
  })
9168
9140
  ),
9169
- deepagents: import_mini28.z.optional(
9170
- import_mini28.z.looseObject({
9171
- "allowed-tools": import_mini28.z.optional(import_mini28.z.array(import_mini28.z.string()))
9141
+ deepagents: import_mini27.z.optional(
9142
+ import_mini27.z.looseObject({
9143
+ "allowed-tools": import_mini27.z.optional(import_mini27.z.array(import_mini27.z.string()))
9172
9144
  })
9173
9145
  ),
9174
- copilot: import_mini28.z.optional(
9175
- import_mini28.z.looseObject({
9176
- license: import_mini28.z.optional(import_mini28.z.string())
9146
+ copilot: import_mini27.z.optional(
9147
+ import_mini27.z.looseObject({
9148
+ license: import_mini27.z.optional(import_mini27.z.string())
9177
9149
  })
9178
9150
  ),
9179
- cline: import_mini28.z.optional(import_mini28.z.looseObject({})),
9180
- roo: import_mini28.z.optional(import_mini28.z.looseObject({}))
9151
+ cline: import_mini27.z.optional(import_mini27.z.looseObject({})),
9152
+ roo: import_mini27.z.optional(import_mini27.z.looseObject({}))
9181
9153
  });
9182
9154
  var RulesyncSkillFrontmatterSchema = RulesyncSkillFrontmatterSchemaInternal;
9183
9155
  var RulesyncSkill = class _RulesyncSkill extends AiDir {
@@ -9274,9 +9246,9 @@ var RulesyncSkill = class _RulesyncSkill extends AiDir {
9274
9246
  };
9275
9247
 
9276
9248
  // src/features/skills/rovodev-skill.ts
9277
- var RovodevSkillFrontmatterSchema = import_mini29.z.looseObject({
9278
- name: import_mini29.z.string(),
9279
- description: import_mini29.z.string()
9249
+ var RovodevSkillFrontmatterSchema = import_mini28.z.looseObject({
9250
+ name: import_mini28.z.string(),
9251
+ description: import_mini28.z.string()
9280
9252
  });
9281
9253
  var RovodevSkill = class _RovodevSkill extends ToolSkill {
9282
9254
  constructor({
@@ -9448,7 +9420,7 @@ var RovodevSkill = class _RovodevSkill extends ToolSkill {
9448
9420
 
9449
9421
  // src/features/skills/skills-processor.ts
9450
9422
  var import_node_path87 = require("path");
9451
- var import_mini46 = require("zod/mini");
9423
+ var import_mini45 = require("zod/mini");
9452
9424
 
9453
9425
  // src/types/dir-feature-processor.ts
9454
9426
  var import_node_path69 = require("path");
@@ -9587,10 +9559,10 @@ var DirFeatureProcessor = class {
9587
9559
 
9588
9560
  // src/features/skills/agentsskills-skill.ts
9589
9561
  var import_node_path70 = require("path");
9590
- var import_mini30 = require("zod/mini");
9591
- var AgentsSkillsSkillFrontmatterSchema = import_mini30.z.looseObject({
9592
- name: import_mini30.z.string(),
9593
- description: import_mini30.z.string()
9562
+ var import_mini29 = require("zod/mini");
9563
+ var AgentsSkillsSkillFrontmatterSchema = import_mini29.z.looseObject({
9564
+ name: import_mini29.z.string(),
9565
+ description: import_mini29.z.string()
9594
9566
  });
9595
9567
  var AgentsSkillsSkill = class _AgentsSkillsSkill extends ToolSkill {
9596
9568
  constructor({
@@ -9745,10 +9717,10 @@ var AgentsSkillsSkill = class _AgentsSkillsSkill extends ToolSkill {
9745
9717
 
9746
9718
  // src/features/skills/antigravity-skill.ts
9747
9719
  var import_node_path71 = require("path");
9748
- var import_mini31 = require("zod/mini");
9749
- var AntigravitySkillFrontmatterSchema = import_mini31.z.looseObject({
9750
- name: import_mini31.z.string(),
9751
- description: import_mini31.z.string()
9720
+ var import_mini30 = require("zod/mini");
9721
+ var AntigravitySkillFrontmatterSchema = import_mini30.z.looseObject({
9722
+ name: import_mini30.z.string(),
9723
+ description: import_mini30.z.string()
9752
9724
  });
9753
9725
  var AntigravitySkill = class _AntigravitySkill extends ToolSkill {
9754
9726
  constructor({
@@ -9906,13 +9878,13 @@ var AntigravitySkill = class _AntigravitySkill extends ToolSkill {
9906
9878
 
9907
9879
  // src/features/skills/claudecode-skill.ts
9908
9880
  var import_node_path72 = require("path");
9909
- var import_mini32 = require("zod/mini");
9910
- var ClaudecodeSkillFrontmatterSchema = import_mini32.z.looseObject({
9911
- name: import_mini32.z.string(),
9912
- description: import_mini32.z.string(),
9913
- "allowed-tools": import_mini32.z.optional(import_mini32.z.array(import_mini32.z.string())),
9914
- model: import_mini32.z.optional(import_mini32.z.string()),
9915
- "disable-model-invocation": import_mini32.z.optional(import_mini32.z.boolean())
9881
+ var import_mini31 = require("zod/mini");
9882
+ var ClaudecodeSkillFrontmatterSchema = import_mini31.z.looseObject({
9883
+ name: import_mini31.z.string(),
9884
+ description: import_mini31.z.string(),
9885
+ "allowed-tools": import_mini31.z.optional(import_mini31.z.array(import_mini31.z.string())),
9886
+ model: import_mini31.z.optional(import_mini31.z.string()),
9887
+ "disable-model-invocation": import_mini31.z.optional(import_mini31.z.boolean())
9916
9888
  });
9917
9889
  var ClaudecodeSkill = class _ClaudecodeSkill extends ToolSkill {
9918
9890
  constructor({
@@ -10082,10 +10054,10 @@ var ClaudecodeSkill = class _ClaudecodeSkill extends ToolSkill {
10082
10054
 
10083
10055
  // src/features/skills/cline-skill.ts
10084
10056
  var import_node_path73 = require("path");
10085
- var import_mini33 = require("zod/mini");
10086
- var ClineSkillFrontmatterSchema = import_mini33.z.looseObject({
10087
- name: import_mini33.z.string(),
10088
- description: import_mini33.z.string()
10057
+ var import_mini32 = require("zod/mini");
10058
+ var ClineSkillFrontmatterSchema = import_mini32.z.looseObject({
10059
+ name: import_mini32.z.string(),
10060
+ description: import_mini32.z.string()
10089
10061
  });
10090
10062
  var ClineSkill = class _ClineSkill extends ToolSkill {
10091
10063
  constructor({
@@ -10255,13 +10227,13 @@ var ClineSkill = class _ClineSkill extends ToolSkill {
10255
10227
 
10256
10228
  // src/features/skills/codexcli-skill.ts
10257
10229
  var import_node_path74 = require("path");
10258
- var import_mini34 = require("zod/mini");
10259
- var CodexCliSkillFrontmatterSchema = import_mini34.z.looseObject({
10260
- name: import_mini34.z.string(),
10261
- description: import_mini34.z.string(),
10262
- metadata: import_mini34.z.optional(
10263
- import_mini34.z.looseObject({
10264
- "short-description": import_mini34.z.optional(import_mini34.z.string())
10230
+ var import_mini33 = require("zod/mini");
10231
+ var CodexCliSkillFrontmatterSchema = import_mini33.z.looseObject({
10232
+ name: import_mini33.z.string(),
10233
+ description: import_mini33.z.string(),
10234
+ metadata: import_mini33.z.optional(
10235
+ import_mini33.z.looseObject({
10236
+ "short-description": import_mini33.z.optional(import_mini33.z.string())
10265
10237
  })
10266
10238
  )
10267
10239
  });
@@ -10426,11 +10398,11 @@ var CodexCliSkill = class _CodexCliSkill extends ToolSkill {
10426
10398
 
10427
10399
  // src/features/skills/copilot-skill.ts
10428
10400
  var import_node_path75 = require("path");
10429
- var import_mini35 = require("zod/mini");
10430
- var CopilotSkillFrontmatterSchema = import_mini35.z.looseObject({
10431
- name: import_mini35.z.string(),
10432
- description: import_mini35.z.string(),
10433
- license: import_mini35.z.optional(import_mini35.z.string())
10401
+ var import_mini34 = require("zod/mini");
10402
+ var CopilotSkillFrontmatterSchema = import_mini34.z.looseObject({
10403
+ name: import_mini34.z.string(),
10404
+ description: import_mini34.z.string(),
10405
+ license: import_mini34.z.optional(import_mini34.z.string())
10434
10406
  });
10435
10407
  var CopilotSkill = class _CopilotSkill extends ToolSkill {
10436
10408
  constructor({
@@ -10591,10 +10563,10 @@ var CopilotSkill = class _CopilotSkill extends ToolSkill {
10591
10563
 
10592
10564
  // src/features/skills/cursor-skill.ts
10593
10565
  var import_node_path76 = require("path");
10594
- var import_mini36 = require("zod/mini");
10595
- var CursorSkillFrontmatterSchema = import_mini36.z.looseObject({
10596
- name: import_mini36.z.string(),
10597
- description: import_mini36.z.string()
10566
+ var import_mini35 = require("zod/mini");
10567
+ var CursorSkillFrontmatterSchema = import_mini35.z.looseObject({
10568
+ name: import_mini35.z.string(),
10569
+ description: import_mini35.z.string()
10598
10570
  });
10599
10571
  var CursorSkill = class _CursorSkill extends ToolSkill {
10600
10572
  constructor({
@@ -10746,11 +10718,11 @@ var CursorSkill = class _CursorSkill extends ToolSkill {
10746
10718
 
10747
10719
  // src/features/skills/deepagents-skill.ts
10748
10720
  var import_node_path77 = require("path");
10749
- var import_mini37 = require("zod/mini");
10750
- var DeepagentsSkillFrontmatterSchema = import_mini37.z.looseObject({
10751
- name: import_mini37.z.string(),
10752
- description: import_mini37.z.string(),
10753
- "allowed-tools": import_mini37.z.optional(import_mini37.z.array(import_mini37.z.string()))
10721
+ var import_mini36 = require("zod/mini");
10722
+ var DeepagentsSkillFrontmatterSchema = import_mini36.z.looseObject({
10723
+ name: import_mini36.z.string(),
10724
+ description: import_mini36.z.string(),
10725
+ "allowed-tools": import_mini36.z.optional(import_mini36.z.array(import_mini36.z.string()))
10754
10726
  });
10755
10727
  var DeepagentsSkill = class _DeepagentsSkill extends ToolSkill {
10756
10728
  constructor({
@@ -10908,10 +10880,10 @@ var DeepagentsSkill = class _DeepagentsSkill extends ToolSkill {
10908
10880
 
10909
10881
  // src/features/skills/geminicli-skill.ts
10910
10882
  var import_node_path78 = require("path");
10911
- var import_mini38 = require("zod/mini");
10912
- var GeminiCliSkillFrontmatterSchema = import_mini38.z.looseObject({
10913
- name: import_mini38.z.string(),
10914
- description: import_mini38.z.string()
10883
+ var import_mini37 = require("zod/mini");
10884
+ var GeminiCliSkillFrontmatterSchema = import_mini37.z.looseObject({
10885
+ name: import_mini37.z.string(),
10886
+ description: import_mini37.z.string()
10915
10887
  });
10916
10888
  var GeminiCliSkill = class _GeminiCliSkill extends ToolSkill {
10917
10889
  constructor({
@@ -11065,10 +11037,10 @@ var GeminiCliSkill = class _GeminiCliSkill extends ToolSkill {
11065
11037
 
11066
11038
  // src/features/skills/junie-skill.ts
11067
11039
  var import_node_path79 = require("path");
11068
- var import_mini39 = require("zod/mini");
11069
- var JunieSkillFrontmatterSchema = import_mini39.z.looseObject({
11070
- name: import_mini39.z.string(),
11071
- description: import_mini39.z.string()
11040
+ var import_mini38 = require("zod/mini");
11041
+ var JunieSkillFrontmatterSchema = import_mini38.z.looseObject({
11042
+ name: import_mini38.z.string(),
11043
+ description: import_mini38.z.string()
11072
11044
  });
11073
11045
  var JunieSkill = class _JunieSkill extends ToolSkill {
11074
11046
  constructor({
@@ -11241,11 +11213,11 @@ var JunieSkill = class _JunieSkill extends ToolSkill {
11241
11213
 
11242
11214
  // src/features/skills/kilo-skill.ts
11243
11215
  var import_node_path80 = require("path");
11244
- var import_mini40 = require("zod/mini");
11245
- var KiloSkillFrontmatterSchema = import_mini40.z.looseObject({
11246
- name: import_mini40.z.string(),
11247
- description: import_mini40.z.string(),
11248
- "allowed-tools": import_mini40.z.optional(import_mini40.z.array(import_mini40.z.string()))
11216
+ var import_mini39 = require("zod/mini");
11217
+ var KiloSkillFrontmatterSchema = import_mini39.z.looseObject({
11218
+ name: import_mini39.z.string(),
11219
+ description: import_mini39.z.string(),
11220
+ "allowed-tools": import_mini39.z.optional(import_mini39.z.array(import_mini39.z.string()))
11249
11221
  });
11250
11222
  var KiloSkill = class _KiloSkill extends ToolSkill {
11251
11223
  constructor({
@@ -11402,10 +11374,10 @@ var KiloSkill = class _KiloSkill extends ToolSkill {
11402
11374
 
11403
11375
  // src/features/skills/kiro-skill.ts
11404
11376
  var import_node_path81 = require("path");
11405
- var import_mini41 = require("zod/mini");
11406
- var KiroSkillFrontmatterSchema = import_mini41.z.looseObject({
11407
- name: import_mini41.z.string(),
11408
- description: import_mini41.z.string()
11377
+ var import_mini40 = require("zod/mini");
11378
+ var KiroSkillFrontmatterSchema = import_mini40.z.looseObject({
11379
+ name: import_mini40.z.string(),
11380
+ description: import_mini40.z.string()
11409
11381
  });
11410
11382
  var KiroSkill = class _KiroSkill extends ToolSkill {
11411
11383
  constructor({
@@ -11579,11 +11551,11 @@ var KiroSkill = class _KiroSkill extends ToolSkill {
11579
11551
 
11580
11552
  // src/features/skills/opencode-skill.ts
11581
11553
  var import_node_path82 = require("path");
11582
- var import_mini42 = require("zod/mini");
11583
- var OpenCodeSkillFrontmatterSchema = import_mini42.z.looseObject({
11584
- name: import_mini42.z.string(),
11585
- description: import_mini42.z.string(),
11586
- "allowed-tools": import_mini42.z.optional(import_mini42.z.array(import_mini42.z.string()))
11554
+ var import_mini41 = require("zod/mini");
11555
+ var OpenCodeSkillFrontmatterSchema = import_mini41.z.looseObject({
11556
+ name: import_mini41.z.string(),
11557
+ description: import_mini41.z.string(),
11558
+ "allowed-tools": import_mini41.z.optional(import_mini41.z.array(import_mini41.z.string()))
11587
11559
  });
11588
11560
  var OpenCodeSkill = class _OpenCodeSkill extends ToolSkill {
11589
11561
  constructor({
@@ -11740,10 +11712,10 @@ var OpenCodeSkill = class _OpenCodeSkill extends ToolSkill {
11740
11712
 
11741
11713
  // src/features/skills/replit-skill.ts
11742
11714
  var import_node_path83 = require("path");
11743
- var import_mini43 = require("zod/mini");
11744
- var ReplitSkillFrontmatterSchema = import_mini43.z.looseObject({
11745
- name: import_mini43.z.string(),
11746
- description: import_mini43.z.string()
11715
+ var import_mini42 = require("zod/mini");
11716
+ var ReplitSkillFrontmatterSchema = import_mini42.z.looseObject({
11717
+ name: import_mini42.z.string(),
11718
+ description: import_mini42.z.string()
11747
11719
  });
11748
11720
  var ReplitSkill = class _ReplitSkill extends ToolSkill {
11749
11721
  constructor({
@@ -11898,10 +11870,10 @@ var ReplitSkill = class _ReplitSkill extends ToolSkill {
11898
11870
 
11899
11871
  // src/features/skills/roo-skill.ts
11900
11872
  var import_node_path84 = require("path");
11901
- var import_mini44 = require("zod/mini");
11902
- var RooSkillFrontmatterSchema = import_mini44.z.looseObject({
11903
- name: import_mini44.z.string(),
11904
- description: import_mini44.z.string()
11873
+ var import_mini43 = require("zod/mini");
11874
+ var RooSkillFrontmatterSchema = import_mini43.z.looseObject({
11875
+ name: import_mini43.z.string(),
11876
+ description: import_mini43.z.string()
11905
11877
  });
11906
11878
  var RooSkill = class _RooSkill extends ToolSkill {
11907
11879
  constructor({
@@ -12090,10 +12062,10 @@ async function getLocalSkillDirNames(baseDir) {
12090
12062
 
12091
12063
  // src/features/skills/windsurf-skill.ts
12092
12064
  var import_node_path86 = require("path");
12093
- var import_mini45 = require("zod/mini");
12094
- var WindsurfSkillFrontmatterSchema = import_mini45.z.looseObject({
12095
- name: import_mini45.z.string(),
12096
- description: import_mini45.z.string()
12065
+ var import_mini44 = require("zod/mini");
12066
+ var WindsurfSkillFrontmatterSchema = import_mini44.z.looseObject({
12067
+ name: import_mini44.z.string(),
12068
+ description: import_mini44.z.string()
12097
12069
  });
12098
12070
  var WindsurfSkill = class _WindsurfSkill extends ToolSkill {
12099
12071
  constructor({
@@ -12271,7 +12243,7 @@ var skillsProcessorToolTargetTuple = [
12271
12243
  "rovodev",
12272
12244
  "windsurf"
12273
12245
  ];
12274
- var SkillsProcessorToolTargetSchema = import_mini46.z.enum(skillsProcessorToolTargetTuple);
12246
+ var SkillsProcessorToolTargetSchema = import_mini45.z.enum(skillsProcessorToolTargetTuple);
12275
12247
  var toolSkillFactories = /* @__PURE__ */ new Map([
12276
12248
  [
12277
12249
  "agentsmd",
@@ -12650,7 +12622,7 @@ var import_node_path89 = require("path");
12650
12622
 
12651
12623
  // src/features/subagents/simulated-subagent.ts
12652
12624
  var import_node_path88 = require("path");
12653
- var import_mini47 = require("zod/mini");
12625
+ var import_mini46 = require("zod/mini");
12654
12626
 
12655
12627
  // src/features/subagents/tool-subagent.ts
12656
12628
  var ToolSubagent = class extends ToolFile {
@@ -12702,9 +12674,9 @@ var ToolSubagent = class extends ToolFile {
12702
12674
  };
12703
12675
 
12704
12676
  // src/features/subagents/simulated-subagent.ts
12705
- var SimulatedSubagentFrontmatterSchema = import_mini47.z.object({
12706
- name: import_mini47.z.string(),
12707
- description: import_mini47.z.optional(import_mini47.z.string())
12677
+ var SimulatedSubagentFrontmatterSchema = import_mini46.z.object({
12678
+ name: import_mini46.z.string(),
12679
+ description: import_mini46.z.optional(import_mini46.z.string())
12708
12680
  });
12709
12681
  var SimulatedSubagent = class extends ToolSubagent {
12710
12682
  frontmatter;
@@ -12862,15 +12834,15 @@ var FactorydroidSubagent = class _FactorydroidSubagent extends SimulatedSubagent
12862
12834
 
12863
12835
  // src/features/subagents/geminicli-subagent.ts
12864
12836
  var import_node_path92 = require("path");
12865
- var import_mini49 = require("zod/mini");
12837
+ var import_mini48 = require("zod/mini");
12866
12838
 
12867
12839
  // src/features/subagents/rulesync-subagent.ts
12868
12840
  var import_node_path91 = require("path");
12869
- var import_mini48 = require("zod/mini");
12870
- var RulesyncSubagentFrontmatterSchema = import_mini48.z.looseObject({
12871
- targets: import_mini48.z._default(RulesyncTargetsSchema, ["*"]),
12872
- name: import_mini48.z.string(),
12873
- description: import_mini48.z.optional(import_mini48.z.string())
12841
+ var import_mini47 = require("zod/mini");
12842
+ var RulesyncSubagentFrontmatterSchema = import_mini47.z.looseObject({
12843
+ targets: import_mini47.z._default(RulesyncTargetsSchema, ["*"]),
12844
+ name: import_mini47.z.string(),
12845
+ description: import_mini47.z.optional(import_mini47.z.string())
12874
12846
  });
12875
12847
  var RulesyncSubagent = class _RulesyncSubagent extends RulesyncFile {
12876
12848
  frontmatter;
@@ -12939,9 +12911,9 @@ var RulesyncSubagent = class _RulesyncSubagent extends RulesyncFile {
12939
12911
  };
12940
12912
 
12941
12913
  // src/features/subagents/geminicli-subagent.ts
12942
- var GeminiCliSubagentFrontmatterSchema = import_mini49.z.looseObject({
12943
- name: import_mini49.z.string(),
12944
- description: import_mini49.z.optional(import_mini49.z.string())
12914
+ var GeminiCliSubagentFrontmatterSchema = import_mini48.z.looseObject({
12915
+ name: import_mini48.z.string(),
12916
+ description: import_mini48.z.optional(import_mini48.z.string())
12945
12917
  });
12946
12918
  var GeminiCliSubagent = class _GeminiCliSubagent extends ToolSubagent {
12947
12919
  frontmatter;
@@ -13114,10 +13086,10 @@ var RooSubagent = class _RooSubagent extends SimulatedSubagent {
13114
13086
 
13115
13087
  // src/features/subagents/rovodev-subagent.ts
13116
13088
  var import_node_path94 = require("path");
13117
- var import_mini50 = require("zod/mini");
13118
- var RovodevSubagentFrontmatterSchema = import_mini50.z.looseObject({
13119
- name: import_mini50.z.string(),
13120
- description: import_mini50.z.optional(import_mini50.z.string())
13089
+ var import_mini49 = require("zod/mini");
13090
+ var RovodevSubagentFrontmatterSchema = import_mini49.z.looseObject({
13091
+ name: import_mini49.z.string(),
13092
+ description: import_mini49.z.optional(import_mini49.z.string())
13121
13093
  });
13122
13094
  var RovodevSubagent = class _RovodevSubagent extends ToolSubagent {
13123
13095
  frontmatter;
@@ -13259,18 +13231,18 @@ var RovodevSubagent = class _RovodevSubagent extends ToolSubagent {
13259
13231
 
13260
13232
  // src/features/subagents/subagents-processor.ts
13261
13233
  var import_node_path105 = require("path");
13262
- var import_mini59 = require("zod/mini");
13234
+ var import_mini58 = require("zod/mini");
13263
13235
 
13264
13236
  // src/features/subagents/claudecode-subagent.ts
13265
13237
  var import_node_path95 = require("path");
13266
- var import_mini51 = require("zod/mini");
13267
- var ClaudecodeSubagentFrontmatterSchema = import_mini51.z.looseObject({
13268
- name: import_mini51.z.string(),
13269
- description: import_mini51.z.optional(import_mini51.z.string()),
13270
- model: import_mini51.z.optional(import_mini51.z.string()),
13271
- tools: import_mini51.z.optional(import_mini51.z.union([import_mini51.z.string(), import_mini51.z.array(import_mini51.z.string())])),
13272
- permissionMode: import_mini51.z.optional(import_mini51.z.string()),
13273
- skills: import_mini51.z.optional(import_mini51.z.union([import_mini51.z.string(), import_mini51.z.array(import_mini51.z.string())]))
13238
+ var import_mini50 = require("zod/mini");
13239
+ var ClaudecodeSubagentFrontmatterSchema = import_mini50.z.looseObject({
13240
+ name: import_mini50.z.string(),
13241
+ description: import_mini50.z.optional(import_mini50.z.string()),
13242
+ model: import_mini50.z.optional(import_mini50.z.string()),
13243
+ tools: import_mini50.z.optional(import_mini50.z.union([import_mini50.z.string(), import_mini50.z.array(import_mini50.z.string())])),
13244
+ permissionMode: import_mini50.z.optional(import_mini50.z.string()),
13245
+ skills: import_mini50.z.optional(import_mini50.z.union([import_mini50.z.string(), import_mini50.z.array(import_mini50.z.string())]))
13274
13246
  });
13275
13247
  var ClaudecodeSubagent = class _ClaudecodeSubagent extends ToolSubagent {
13276
13248
  frontmatter;
@@ -13426,16 +13398,16 @@ var ClaudecodeSubagent = class _ClaudecodeSubagent extends ToolSubagent {
13426
13398
  // src/features/subagents/codexcli-subagent.ts
13427
13399
  var import_node_path96 = require("path");
13428
13400
  var smolToml4 = __toESM(require("smol-toml"), 1);
13429
- var import_mini52 = require("zod/mini");
13430
- var CodexCliSubagentTomlSchema = import_mini52.z.looseObject({
13431
- name: import_mini52.z.string(),
13432
- description: import_mini52.z.optional(import_mini52.z.string()),
13433
- developer_instructions: import_mini52.z.optional(import_mini52.z.string()),
13434
- model: import_mini52.z.optional(import_mini52.z.string()),
13435
- model_reasoning_effort: import_mini52.z.optional(import_mini52.z.string()),
13436
- sandbox_mode: import_mini52.z.optional(import_mini52.z.string())
13437
- });
13438
- function stringifyCodexCliSubagentToml(tomlObj) {
13401
+ var import_mini51 = require("zod/mini");
13402
+ var CodexCliSubagentTomlSchema = import_mini51.z.looseObject({
13403
+ name: import_mini51.z.string(),
13404
+ description: import_mini51.z.optional(import_mini51.z.string()),
13405
+ developer_instructions: import_mini51.z.optional(import_mini51.z.string()),
13406
+ model: import_mini51.z.optional(import_mini51.z.string()),
13407
+ model_reasoning_effort: import_mini51.z.optional(import_mini51.z.string()),
13408
+ sandbox_mode: import_mini51.z.optional(import_mini51.z.string())
13409
+ });
13410
+ function stringifyCodexCliSubagentToml(tomlObj) {
13439
13411
  const { developer_instructions, ...restFields } = tomlObj;
13440
13412
  const restToml = smolToml4.stringify(restFields).trimEnd();
13441
13413
  if (developer_instructions === void 0) {
@@ -13599,12 +13571,12 @@ var CodexCliSubagent = class _CodexCliSubagent extends ToolSubagent {
13599
13571
 
13600
13572
  // src/features/subagents/copilot-subagent.ts
13601
13573
  var import_node_path97 = require("path");
13602
- var import_mini53 = require("zod/mini");
13574
+ var import_mini52 = require("zod/mini");
13603
13575
  var REQUIRED_TOOL = "agent/runSubagent";
13604
- var CopilotSubagentFrontmatterSchema = import_mini53.z.looseObject({
13605
- name: import_mini53.z.string(),
13606
- description: import_mini53.z.optional(import_mini53.z.string()),
13607
- tools: import_mini53.z.optional(import_mini53.z.union([import_mini53.z.string(), import_mini53.z.array(import_mini53.z.string())]))
13576
+ var CopilotSubagentFrontmatterSchema = import_mini52.z.looseObject({
13577
+ name: import_mini52.z.string(),
13578
+ description: import_mini52.z.optional(import_mini52.z.string()),
13579
+ tools: import_mini52.z.optional(import_mini52.z.union([import_mini52.z.string(), import_mini52.z.array(import_mini52.z.string())]))
13608
13580
  });
13609
13581
  var normalizeTools = (tools) => {
13610
13582
  if (!tools) {
@@ -13616,6 +13588,21 @@ var ensureRequiredTool = (tools) => {
13616
13588
  const mergedTools = /* @__PURE__ */ new Set([REQUIRED_TOOL, ...tools]);
13617
13589
  return Array.from(mergedTools);
13618
13590
  };
13591
+ var toCopilotAgentFilePath = (relativeFilePath) => {
13592
+ if (relativeFilePath.endsWith(".agent.md")) {
13593
+ return relativeFilePath;
13594
+ }
13595
+ if (relativeFilePath.endsWith(".md")) {
13596
+ return relativeFilePath.replace(/\.md$/, ".agent.md");
13597
+ }
13598
+ return relativeFilePath;
13599
+ };
13600
+ var toRulesyncFilePath = (relativeFilePath) => {
13601
+ if (relativeFilePath.endsWith(".agent.md")) {
13602
+ return relativeFilePath.replace(/\.agent\.md$/, ".md");
13603
+ }
13604
+ return relativeFilePath;
13605
+ };
13619
13606
  var CopilotSubagent = class _CopilotSubagent extends ToolSubagent {
13620
13607
  frontmatter;
13621
13608
  body;
@@ -13662,7 +13649,7 @@ var CopilotSubagent = class _CopilotSubagent extends ToolSubagent {
13662
13649
  frontmatter: rulesyncFrontmatter,
13663
13650
  body: this.body,
13664
13651
  relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,
13665
- relativeFilePath: this.getRelativeFilePath(),
13652
+ relativeFilePath: toRulesyncFilePath(this.getRelativeFilePath()),
13666
13653
  validate: true
13667
13654
  });
13668
13655
  }
@@ -13688,16 +13675,12 @@ var CopilotSubagent = class _CopilotSubagent extends ToolSubagent {
13688
13675
  const body = rulesyncSubagent.getBody();
13689
13676
  const fileContent = stringifyFrontmatter(body, copilotFrontmatter);
13690
13677
  const paths = this.getSettablePaths({ global });
13691
- let relativeFilePath = rulesyncSubagent.getRelativeFilePath();
13692
- if (!relativeFilePath.endsWith(".agent.md")) {
13693
- relativeFilePath = relativeFilePath.replace(/\.md$/, ".agent.md");
13694
- }
13695
13678
  return new _CopilotSubagent({
13696
13679
  baseDir,
13697
13680
  frontmatter: copilotFrontmatter,
13698
13681
  body,
13699
13682
  relativeDirPath: paths.relativeDirPath,
13700
- relativeFilePath,
13683
+ relativeFilePath: toCopilotAgentFilePath(rulesyncSubagent.getRelativeFilePath()),
13701
13684
  fileContent,
13702
13685
  validate,
13703
13686
  global
@@ -13769,10 +13752,10 @@ var CopilotSubagent = class _CopilotSubagent extends ToolSubagent {
13769
13752
 
13770
13753
  // src/features/subagents/cursor-subagent.ts
13771
13754
  var import_node_path98 = require("path");
13772
- var import_mini54 = require("zod/mini");
13773
- var CursorSubagentFrontmatterSchema = import_mini54.z.looseObject({
13774
- name: import_mini54.z.string(),
13775
- description: import_mini54.z.optional(import_mini54.z.string())
13755
+ var import_mini53 = require("zod/mini");
13756
+ var CursorSubagentFrontmatterSchema = import_mini53.z.looseObject({
13757
+ name: import_mini53.z.string(),
13758
+ description: import_mini53.z.optional(import_mini53.z.string())
13776
13759
  });
13777
13760
  var CursorSubagent = class _CursorSubagent extends ToolSubagent {
13778
13761
  frontmatter;
@@ -13916,11 +13899,11 @@ var CursorSubagent = class _CursorSubagent extends ToolSubagent {
13916
13899
 
13917
13900
  // src/features/subagents/deepagents-subagent.ts
13918
13901
  var import_node_path99 = require("path");
13919
- var import_mini55 = require("zod/mini");
13920
- var DeepagentsSubagentFrontmatterSchema = import_mini55.z.looseObject({
13921
- name: import_mini55.z.string(),
13922
- description: import_mini55.z.optional(import_mini55.z.string()),
13923
- model: import_mini55.z.optional(import_mini55.z.string())
13902
+ var import_mini54 = require("zod/mini");
13903
+ var DeepagentsSubagentFrontmatterSchema = import_mini54.z.looseObject({
13904
+ name: import_mini54.z.string(),
13905
+ description: import_mini54.z.optional(import_mini54.z.string()),
13906
+ model: import_mini54.z.optional(import_mini54.z.string())
13924
13907
  });
13925
13908
  var DeepagentsSubagent = class _DeepagentsSubagent extends ToolSubagent {
13926
13909
  frontmatter;
@@ -14069,10 +14052,10 @@ var DeepagentsSubagent = class _DeepagentsSubagent extends ToolSubagent {
14069
14052
 
14070
14053
  // src/features/subagents/junie-subagent.ts
14071
14054
  var import_node_path100 = require("path");
14072
- var import_mini56 = require("zod/mini");
14073
- var JunieSubagentFrontmatterSchema = import_mini56.z.looseObject({
14074
- name: import_mini56.z.optional(import_mini56.z.string()),
14075
- description: import_mini56.z.string()
14055
+ var import_mini55 = require("zod/mini");
14056
+ var JunieSubagentFrontmatterSchema = import_mini55.z.looseObject({
14057
+ name: import_mini55.z.optional(import_mini55.z.string()),
14058
+ description: import_mini55.z.string()
14076
14059
  });
14077
14060
  var JunieSubagent = class _JunieSubagent extends ToolSubagent {
14078
14061
  frontmatter;
@@ -14230,11 +14213,11 @@ var import_node_path102 = require("path");
14230
14213
 
14231
14214
  // src/features/subagents/opencode-style-subagent.ts
14232
14215
  var import_node_path101 = require("path");
14233
- var import_mini57 = require("zod/mini");
14234
- var OpenCodeStyleSubagentFrontmatterSchema = import_mini57.z.looseObject({
14235
- description: import_mini57.z.optional(import_mini57.z.string()),
14236
- mode: import_mini57.z._default(import_mini57.z.string(), "subagent"),
14237
- name: import_mini57.z.optional(import_mini57.z.string())
14216
+ var import_mini56 = require("zod/mini");
14217
+ var OpenCodeStyleSubagentFrontmatterSchema = import_mini56.z.looseObject({
14218
+ description: import_mini56.z.optional(import_mini56.z.string()),
14219
+ mode: import_mini56.z._default(import_mini56.z.string(), "subagent"),
14220
+ name: import_mini56.z.optional(import_mini56.z.string())
14238
14221
  });
14239
14222
  var OpenCodeStyleSubagent = class extends ToolSubagent {
14240
14223
  frontmatter;
@@ -14383,22 +14366,22 @@ var KiloSubagent = class _KiloSubagent extends OpenCodeStyleSubagent {
14383
14366
 
14384
14367
  // src/features/subagents/kiro-subagent.ts
14385
14368
  var import_node_path103 = require("path");
14386
- var import_mini58 = require("zod/mini");
14387
- var KiroCliSubagentJsonSchema = import_mini58.z.looseObject({
14388
- name: import_mini58.z.string(),
14389
- description: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.string())),
14390
- prompt: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.string())),
14391
- tools: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.array(import_mini58.z.string()))),
14392
- toolAliases: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.record(import_mini58.z.string(), import_mini58.z.string()))),
14393
- toolSettings: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.unknown())),
14394
- toolSchema: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.unknown())),
14395
- hooks: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.record(import_mini58.z.string(), import_mini58.z.array(import_mini58.z.unknown())))),
14396
- model: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.string())),
14397
- mcpServers: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.record(import_mini58.z.string(), import_mini58.z.unknown()))),
14398
- useLegacyMcpJson: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.boolean())),
14399
- resources: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.array(import_mini58.z.string()))),
14400
- allowedTools: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.array(import_mini58.z.string()))),
14401
- includeMcpJson: import_mini58.z.optional(import_mini58.z.nullable(import_mini58.z.boolean()))
14369
+ var import_mini57 = require("zod/mini");
14370
+ var KiroCliSubagentJsonSchema = import_mini57.z.looseObject({
14371
+ name: import_mini57.z.string(),
14372
+ description: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.string())),
14373
+ prompt: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.string())),
14374
+ tools: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.array(import_mini57.z.string()))),
14375
+ toolAliases: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.record(import_mini57.z.string(), import_mini57.z.string()))),
14376
+ toolSettings: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.unknown())),
14377
+ toolSchema: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.unknown())),
14378
+ hooks: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.record(import_mini57.z.string(), import_mini57.z.array(import_mini57.z.unknown())))),
14379
+ model: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.string())),
14380
+ mcpServers: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.record(import_mini57.z.string(), import_mini57.z.unknown()))),
14381
+ useLegacyMcpJson: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.boolean())),
14382
+ resources: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.array(import_mini57.z.string()))),
14383
+ allowedTools: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.array(import_mini57.z.string()))),
14384
+ includeMcpJson: import_mini57.z.optional(import_mini57.z.nullable(import_mini57.z.boolean()))
14402
14385
  });
14403
14386
  var KiroSubagent = class _KiroSubagent extends ToolSubagent {
14404
14387
  body;
@@ -14659,7 +14642,7 @@ var subagentsProcessorToolTargetTuple = [
14659
14642
  "roo",
14660
14643
  "rovodev"
14661
14644
  ];
14662
- var SubagentsProcessorToolTargetSchema = import_mini59.z.enum(subagentsProcessorToolTargetTuple);
14645
+ var SubagentsProcessorToolTargetSchema = import_mini58.z.enum(subagentsProcessorToolTargetTuple);
14663
14646
  var toolSubagentFactories = /* @__PURE__ */ new Map([
14664
14647
  [
14665
14648
  "agentsmd",
@@ -14969,42 +14952,42 @@ var import_node_path107 = require("path");
14969
14952
 
14970
14953
  // src/features/rules/rulesync-rule.ts
14971
14954
  var import_node_path106 = require("path");
14972
- var import_mini60 = require("zod/mini");
14973
- var RulesyncRuleFrontmatterSchema = import_mini60.z.object({
14974
- root: import_mini60.z.optional(import_mini60.z.boolean()),
14975
- localRoot: import_mini60.z.optional(import_mini60.z.boolean()),
14976
- targets: import_mini60.z._default(RulesyncTargetsSchema, ["*"]),
14977
- description: import_mini60.z.optional(import_mini60.z.string()),
14978
- globs: import_mini60.z.optional(import_mini60.z.array(import_mini60.z.string())),
14979
- agentsmd: import_mini60.z.optional(
14980
- import_mini60.z.looseObject({
14955
+ var import_mini59 = require("zod/mini");
14956
+ var RulesyncRuleFrontmatterSchema = import_mini59.z.object({
14957
+ root: import_mini59.z.optional(import_mini59.z.boolean()),
14958
+ localRoot: import_mini59.z.optional(import_mini59.z.boolean()),
14959
+ targets: import_mini59.z._default(RulesyncTargetsSchema, ["*"]),
14960
+ description: import_mini59.z.optional(import_mini59.z.string()),
14961
+ globs: import_mini59.z.optional(import_mini59.z.array(import_mini59.z.string())),
14962
+ agentsmd: import_mini59.z.optional(
14963
+ import_mini59.z.looseObject({
14981
14964
  // @example "path/to/subproject"
14982
- subprojectPath: import_mini60.z.optional(import_mini60.z.string())
14965
+ subprojectPath: import_mini59.z.optional(import_mini59.z.string())
14983
14966
  })
14984
14967
  ),
14985
- claudecode: import_mini60.z.optional(
14986
- import_mini60.z.looseObject({
14968
+ claudecode: import_mini59.z.optional(
14969
+ import_mini59.z.looseObject({
14987
14970
  // Glob patterns for conditional rules (takes precedence over globs)
14988
14971
  // @example ["src/**/*.ts", "tests/**/*.test.ts"]
14989
- paths: import_mini60.z.optional(import_mini60.z.array(import_mini60.z.string()))
14972
+ paths: import_mini59.z.optional(import_mini59.z.array(import_mini59.z.string()))
14990
14973
  })
14991
14974
  ),
14992
- cursor: import_mini60.z.optional(
14993
- import_mini60.z.looseObject({
14994
- alwaysApply: import_mini60.z.optional(import_mini60.z.boolean()),
14995
- description: import_mini60.z.optional(import_mini60.z.string()),
14996
- globs: import_mini60.z.optional(import_mini60.z.array(import_mini60.z.string()))
14975
+ cursor: import_mini59.z.optional(
14976
+ import_mini59.z.looseObject({
14977
+ alwaysApply: import_mini59.z.optional(import_mini59.z.boolean()),
14978
+ description: import_mini59.z.optional(import_mini59.z.string()),
14979
+ globs: import_mini59.z.optional(import_mini59.z.array(import_mini59.z.string()))
14997
14980
  })
14998
14981
  ),
14999
- copilot: import_mini60.z.optional(
15000
- import_mini60.z.looseObject({
15001
- excludeAgent: import_mini60.z.optional(import_mini60.z.union([import_mini60.z.literal("code-review"), import_mini60.z.literal("coding-agent")]))
14982
+ copilot: import_mini59.z.optional(
14983
+ import_mini59.z.looseObject({
14984
+ excludeAgent: import_mini59.z.optional(import_mini59.z.union([import_mini59.z.literal("code-review"), import_mini59.z.literal("coding-agent")]))
15002
14985
  })
15003
14986
  ),
15004
- antigravity: import_mini60.z.optional(
15005
- import_mini60.z.looseObject({
15006
- trigger: import_mini60.z.optional(import_mini60.z.string()),
15007
- globs: import_mini60.z.optional(import_mini60.z.array(import_mini60.z.string()))
14987
+ antigravity: import_mini59.z.optional(
14988
+ import_mini59.z.looseObject({
14989
+ trigger: import_mini59.z.optional(import_mini59.z.string()),
14990
+ globs: import_mini59.z.optional(import_mini59.z.array(import_mini59.z.string()))
15008
14991
  })
15009
14992
  )
15010
14993
  });
@@ -15304,20 +15287,20 @@ var AgentsMdRule = class _AgentsMdRule extends ToolRule {
15304
15287
 
15305
15288
  // src/features/rules/antigravity-rule.ts
15306
15289
  var import_node_path109 = require("path");
15307
- var import_mini61 = require("zod/mini");
15308
- var AntigravityRuleFrontmatterSchema = import_mini61.z.looseObject({
15309
- trigger: import_mini61.z.optional(
15310
- import_mini61.z.union([
15311
- import_mini61.z.literal("always_on"),
15312
- import_mini61.z.literal("glob"),
15313
- import_mini61.z.literal("manual"),
15314
- import_mini61.z.literal("model_decision"),
15315
- import_mini61.z.string()
15290
+ var import_mini60 = require("zod/mini");
15291
+ var AntigravityRuleFrontmatterSchema = import_mini60.z.looseObject({
15292
+ trigger: import_mini60.z.optional(
15293
+ import_mini60.z.union([
15294
+ import_mini60.z.literal("always_on"),
15295
+ import_mini60.z.literal("glob"),
15296
+ import_mini60.z.literal("manual"),
15297
+ import_mini60.z.literal("model_decision"),
15298
+ import_mini60.z.string()
15316
15299
  // accepts any string for forward compatibility
15317
15300
  ])
15318
15301
  ),
15319
- globs: import_mini61.z.optional(import_mini61.z.string()),
15320
- description: import_mini61.z.optional(import_mini61.z.string())
15302
+ globs: import_mini60.z.optional(import_mini60.z.string()),
15303
+ description: import_mini60.z.optional(import_mini60.z.string())
15321
15304
  });
15322
15305
  function parseGlobsString(globs) {
15323
15306
  if (!globs) {
@@ -15904,9 +15887,9 @@ var ClaudecodeLegacyRule = class _ClaudecodeLegacyRule extends ToolRule {
15904
15887
 
15905
15888
  // src/features/rules/claudecode-rule.ts
15906
15889
  var import_node_path113 = require("path");
15907
- var import_mini62 = require("zod/mini");
15908
- var ClaudecodeRuleFrontmatterSchema = import_mini62.z.object({
15909
- paths: import_mini62.z.optional(import_mini62.z.array(import_mini62.z.string()))
15890
+ var import_mini61 = require("zod/mini");
15891
+ var ClaudecodeRuleFrontmatterSchema = import_mini61.z.object({
15892
+ paths: import_mini61.z.optional(import_mini61.z.array(import_mini61.z.string()))
15910
15893
  });
15911
15894
  var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
15912
15895
  frontmatter;
@@ -16122,9 +16105,9 @@ var ClaudecodeRule = class _ClaudecodeRule extends ToolRule {
16122
16105
 
16123
16106
  // src/features/rules/cline-rule.ts
16124
16107
  var import_node_path114 = require("path");
16125
- var import_mini63 = require("zod/mini");
16126
- var ClineRuleFrontmatterSchema = import_mini63.z.object({
16127
- description: import_mini63.z.string()
16108
+ var import_mini62 = require("zod/mini");
16109
+ var ClineRuleFrontmatterSchema = import_mini62.z.object({
16110
+ description: import_mini62.z.string()
16128
16111
  });
16129
16112
  var ClineRule = class _ClineRule extends ToolRule {
16130
16113
  static getSettablePaths(_options = {}) {
@@ -16303,11 +16286,11 @@ var CodexcliRule = class _CodexcliRule extends ToolRule {
16303
16286
 
16304
16287
  // src/features/rules/copilot-rule.ts
16305
16288
  var import_node_path116 = require("path");
16306
- var import_mini64 = require("zod/mini");
16307
- var CopilotRuleFrontmatterSchema = import_mini64.z.object({
16308
- description: import_mini64.z.optional(import_mini64.z.string()),
16309
- applyTo: import_mini64.z.optional(import_mini64.z.string()),
16310
- excludeAgent: import_mini64.z.optional(import_mini64.z.union([import_mini64.z.literal("code-review"), import_mini64.z.literal("coding-agent")]))
16289
+ var import_mini63 = require("zod/mini");
16290
+ var CopilotRuleFrontmatterSchema = import_mini63.z.object({
16291
+ description: import_mini63.z.optional(import_mini63.z.string()),
16292
+ applyTo: import_mini63.z.optional(import_mini63.z.string()),
16293
+ excludeAgent: import_mini63.z.optional(import_mini63.z.union([import_mini63.z.literal("code-review"), import_mini63.z.literal("coding-agent")]))
16311
16294
  });
16312
16295
  var CopilotRule = class _CopilotRule extends ToolRule {
16313
16296
  frontmatter;
@@ -16549,11 +16532,11 @@ var CopilotcliRule = class _CopilotcliRule extends CopilotRule {
16549
16532
 
16550
16533
  // src/features/rules/cursor-rule.ts
16551
16534
  var import_node_path117 = require("path");
16552
- var import_mini65 = require("zod/mini");
16553
- var CursorRuleFrontmatterSchema = import_mini65.z.object({
16554
- description: import_mini65.z.optional(import_mini65.z.string()),
16555
- globs: import_mini65.z.optional(import_mini65.z.string()),
16556
- alwaysApply: import_mini65.z.optional(import_mini65.z.boolean())
16535
+ var import_mini64 = require("zod/mini");
16536
+ var CursorRuleFrontmatterSchema = import_mini64.z.object({
16537
+ description: import_mini64.z.optional(import_mini64.z.string()),
16538
+ globs: import_mini64.z.optional(import_mini64.z.string()),
16539
+ alwaysApply: import_mini64.z.optional(import_mini64.z.boolean())
16557
16540
  });
16558
16541
  var CursorRule = class _CursorRule extends ToolRule {
16559
16542
  frontmatter;
@@ -18213,11 +18196,11 @@ var rulesProcessorToolTargets = [
18213
18196
  "warp",
18214
18197
  "windsurf"
18215
18198
  ];
18216
- var RulesProcessorToolTargetSchema = import_mini66.z.enum(rulesProcessorToolTargets);
18199
+ var RulesProcessorToolTargetSchema = import_mini65.z.enum(rulesProcessorToolTargets);
18217
18200
  var formatRulePaths = (rules) => rules.map((r) => (0, import_node_path132.join)(r.getRelativeDirPath(), r.getRelativeFilePath())).join(", ");
18218
- var RulesFeatureOptionsSchema = import_mini66.z.looseObject({
18219
- ruleDiscoveryMode: import_mini66.z.optional(import_mini66.z.enum(["none", "explicit"])),
18220
- includeLocalRoot: import_mini66.z.optional(import_mini66.z.boolean())
18201
+ var RulesFeatureOptionsSchema = import_mini65.z.looseObject({
18202
+ ruleDiscoveryMode: import_mini65.z.optional(import_mini65.z.enum(["none", "explicit"])),
18203
+ includeLocalRoot: import_mini65.z.optional(import_mini65.z.boolean())
18221
18204
  });
18222
18205
  var resolveRuleDiscoveryMode = ({
18223
18206
  defaultMode,
@@ -18238,8 +18221,8 @@ var resolveRuleDiscoveryMode = ({
18238
18221
  }
18239
18222
  return parsed.data.ruleDiscoveryMode === "none" ? "auto" : "toon";
18240
18223
  };
18241
- var IncludeLocalRootSchema = import_mini66.z.looseObject({
18242
- includeLocalRoot: import_mini66.z.optional(import_mini66.z.boolean())
18224
+ var IncludeLocalRootSchema = import_mini65.z.looseObject({
18225
+ includeLocalRoot: import_mini65.z.optional(import_mini65.z.boolean())
18243
18226
  });
18244
18227
  var resolveIncludeLocalRoot = (options) => {
18245
18228
  if (!options) return true;
@@ -19182,51 +19165,51 @@ var import_request_error = require("@octokit/request-error");
19182
19165
  var import_rest = require("@octokit/rest");
19183
19166
 
19184
19167
  // src/types/fetch.ts
19185
- var import_mini68 = require("zod/mini");
19168
+ var import_mini67 = require("zod/mini");
19186
19169
 
19187
19170
  // src/types/fetch-targets.ts
19188
- var import_mini67 = require("zod/mini");
19171
+ var import_mini66 = require("zod/mini");
19189
19172
  var ALL_FETCH_TARGETS = ["rulesync", ...ALL_TOOL_TARGETS];
19190
- var FetchTargetSchema = import_mini67.z.enum(ALL_FETCH_TARGETS);
19173
+ var FetchTargetSchema = import_mini66.z.enum(ALL_FETCH_TARGETS);
19191
19174
 
19192
19175
  // src/types/fetch.ts
19193
- var ConflictStrategySchema = import_mini68.z.enum(["skip", "overwrite"]);
19194
- var GitHubFileTypeSchema = import_mini68.z.enum(["file", "dir", "symlink", "submodule"]);
19195
- var GitHubFileEntrySchema = import_mini68.z.looseObject({
19196
- name: import_mini68.z.string(),
19197
- path: import_mini68.z.string(),
19198
- sha: import_mini68.z.string(),
19199
- size: import_mini68.z.number(),
19176
+ var ConflictStrategySchema = import_mini67.z.enum(["skip", "overwrite"]);
19177
+ var GitHubFileTypeSchema = import_mini67.z.enum(["file", "dir", "symlink", "submodule"]);
19178
+ var GitHubFileEntrySchema = import_mini67.z.looseObject({
19179
+ name: import_mini67.z.string(),
19180
+ path: import_mini67.z.string(),
19181
+ sha: import_mini67.z.string(),
19182
+ size: import_mini67.z.number(),
19200
19183
  type: GitHubFileTypeSchema,
19201
- download_url: import_mini68.z.nullable(import_mini68.z.string())
19184
+ download_url: import_mini67.z.nullable(import_mini67.z.string())
19202
19185
  });
19203
- var FetchOptionsSchema = import_mini68.z.looseObject({
19204
- target: import_mini68.z.optional(FetchTargetSchema),
19205
- features: import_mini68.z.optional(import_mini68.z.array(import_mini68.z.enum(ALL_FEATURES_WITH_WILDCARD))),
19206
- ref: import_mini68.z.optional(import_mini68.z.string()),
19207
- path: import_mini68.z.optional(import_mini68.z.string()),
19208
- output: import_mini68.z.optional(import_mini68.z.string()),
19209
- conflict: import_mini68.z.optional(ConflictStrategySchema),
19210
- token: import_mini68.z.optional(import_mini68.z.string()),
19211
- verbose: import_mini68.z.optional(import_mini68.z.boolean()),
19212
- silent: import_mini68.z.optional(import_mini68.z.boolean())
19186
+ var FetchOptionsSchema = import_mini67.z.looseObject({
19187
+ target: import_mini67.z.optional(FetchTargetSchema),
19188
+ features: import_mini67.z.optional(import_mini67.z.array(import_mini67.z.enum(ALL_FEATURES_WITH_WILDCARD))),
19189
+ ref: import_mini67.z.optional(import_mini67.z.string()),
19190
+ path: import_mini67.z.optional(import_mini67.z.string()),
19191
+ output: import_mini67.z.optional(import_mini67.z.string()),
19192
+ conflict: import_mini67.z.optional(ConflictStrategySchema),
19193
+ token: import_mini67.z.optional(import_mini67.z.string()),
19194
+ verbose: import_mini67.z.optional(import_mini67.z.boolean()),
19195
+ silent: import_mini67.z.optional(import_mini67.z.boolean())
19213
19196
  });
19214
- var FetchFileStatusSchema = import_mini68.z.enum(["created", "overwritten", "skipped"]);
19215
- var GitHubRepoInfoSchema = import_mini68.z.looseObject({
19216
- default_branch: import_mini68.z.string(),
19217
- private: import_mini68.z.boolean()
19197
+ var FetchFileStatusSchema = import_mini67.z.enum(["created", "overwritten", "skipped"]);
19198
+ var GitHubRepoInfoSchema = import_mini67.z.looseObject({
19199
+ default_branch: import_mini67.z.string(),
19200
+ private: import_mini67.z.boolean()
19218
19201
  });
19219
- var GitHubReleaseAssetSchema = import_mini68.z.looseObject({
19220
- name: import_mini68.z.string(),
19221
- browser_download_url: import_mini68.z.string(),
19222
- size: import_mini68.z.number()
19202
+ var GitHubReleaseAssetSchema = import_mini67.z.looseObject({
19203
+ name: import_mini67.z.string(),
19204
+ browser_download_url: import_mini67.z.string(),
19205
+ size: import_mini67.z.number()
19223
19206
  });
19224
- var GitHubReleaseSchema = import_mini68.z.looseObject({
19225
- tag_name: import_mini68.z.string(),
19226
- name: import_mini68.z.nullable(import_mini68.z.string()),
19227
- prerelease: import_mini68.z.boolean(),
19228
- draft: import_mini68.z.boolean(),
19229
- assets: import_mini68.z.array(GitHubReleaseAssetSchema)
19207
+ var GitHubReleaseSchema = import_mini67.z.looseObject({
19208
+ tag_name: import_mini67.z.string(),
19209
+ name: import_mini67.z.nullable(import_mini67.z.string()),
19210
+ prerelease: import_mini67.z.boolean(),
19211
+ draft: import_mini67.z.boolean(),
19212
+ assets: import_mini67.z.array(GitHubReleaseAssetSchema)
19230
19213
  });
19231
19214
 
19232
19215
  // src/lib/github-client.ts
@@ -19527,9 +19510,9 @@ async function listDirectoryRecursive(params) {
19527
19510
  }
19528
19511
 
19529
19512
  // src/types/git-provider.ts
19530
- var import_mini69 = require("zod/mini");
19513
+ var import_mini68 = require("zod/mini");
19531
19514
  var ALL_GIT_PROVIDERS = ["github", "gitlab"];
19532
- var GitProviderSchema = import_mini69.z.enum(ALL_GIT_PROVIDERS);
19515
+ var GitProviderSchema = import_mini68.z.enum(ALL_GIT_PROVIDERS);
19533
19516
 
19534
19517
  // src/lib/source-parser.ts
19535
19518
  var GITHUB_HOSTS = /* @__PURE__ */ new Set(["github.com", "www.github.com"]);
@@ -20152,7 +20135,7 @@ var import_jsonc_parser4 = require("jsonc-parser");
20152
20135
 
20153
20136
  // src/config/config.ts
20154
20137
  var import_node_path134 = require("path");
20155
- var import_mini70 = require("zod/mini");
20138
+ var import_mini69 = require("zod/mini");
20156
20139
 
20157
20140
  // src/utils/validation.ts
20158
20141
  function findControlCharacter(value) {
@@ -20170,49 +20153,55 @@ function hasControlCharacters(value) {
20170
20153
 
20171
20154
  // src/config/config.ts
20172
20155
  var GITIGNORE_DESTINATION_KEY = "gitignoreDestination";
20173
- var SourceEntrySchema = import_mini70.z.object({
20174
- source: import_mini70.z.string().check((0, import_mini70.minLength)(1, "source must be a non-empty string")),
20175
- skills: (0, import_mini70.optional)(import_mini70.z.array(import_mini70.z.string())),
20176
- transport: (0, import_mini70.optional)(import_mini70.z.enum(["github", "git"])),
20177
- ref: (0, import_mini70.optional)(
20178
- import_mini70.z.string().check(
20179
- (0, import_mini70.refine)((v) => !v.startsWith("-"), 'ref must not start with "-"'),
20180
- (0, import_mini70.refine)((v) => !hasControlCharacters(v), "ref must not contain control characters")
20156
+ var SourceEntrySchema = import_mini69.z.object({
20157
+ source: import_mini69.z.string().check((0, import_mini69.minLength)(1, "source must be a non-empty string")),
20158
+ skills: (0, import_mini69.optional)(import_mini69.z.array(import_mini69.z.string())),
20159
+ transport: (0, import_mini69.optional)(import_mini69.z.enum(["github", "git"])),
20160
+ ref: (0, import_mini69.optional)(
20161
+ import_mini69.z.string().check(
20162
+ (0, import_mini69.refine)((v) => !v.startsWith("-"), 'ref must not start with "-"'),
20163
+ (0, import_mini69.refine)((v) => !hasControlCharacters(v), "ref must not contain control characters")
20181
20164
  )
20182
20165
  ),
20183
- path: (0, import_mini70.optional)(
20184
- import_mini70.z.string().check(
20185
- (0, import_mini70.refine)((v) => !v.includes(".."), 'path must not contain ".."'),
20186
- (0, import_mini70.refine)((v) => !(0, import_node_path134.isAbsolute)(v), "path must not be absolute"),
20187
- (0, import_mini70.refine)((v) => !hasControlCharacters(v), "path must not contain control characters")
20166
+ path: (0, import_mini69.optional)(
20167
+ import_mini69.z.string().check(
20168
+ (0, import_mini69.refine)((v) => !v.includes(".."), 'path must not contain ".."'),
20169
+ (0, import_mini69.refine)((v) => !(0, import_node_path134.isAbsolute)(v), "path must not be absolute"),
20170
+ (0, import_mini69.refine)((v) => !hasControlCharacters(v), "path must not contain control characters")
20188
20171
  )
20189
- )
20172
+ ),
20173
+ // gh-mode-only fields. Ignored by --mode rulesync. Defaults applied at the
20174
+ // gh install site (`agent` defaults to "github-copilot", `scope` to "project").
20175
+ agent: (0, import_mini69.optional)(
20176
+ import_mini69.z.enum(["github-copilot", "claude-code", "cursor", "codex", "gemini", "antigravity"])
20177
+ ),
20178
+ scope: (0, import_mini69.optional)(import_mini69.z.enum(["project", "user"]))
20190
20179
  });
20191
- var ConfigParamsSchema = import_mini70.z.object({
20192
- baseDirs: import_mini70.z.array(import_mini70.z.string()),
20180
+ var ConfigParamsSchema = import_mini69.z.object({
20181
+ baseDirs: import_mini69.z.array(import_mini69.z.string()),
20193
20182
  targets: RulesyncConfigTargetsSchema,
20194
20183
  features: RulesyncFeaturesSchema,
20195
- verbose: import_mini70.z.boolean(),
20196
- delete: import_mini70.z.boolean(),
20184
+ verbose: import_mini69.z.boolean(),
20185
+ delete: import_mini69.z.boolean(),
20197
20186
  // New non-experimental options
20198
- global: (0, import_mini70.optional)(import_mini70.z.boolean()),
20199
- silent: (0, import_mini70.optional)(import_mini70.z.boolean()),
20200
- simulateCommands: (0, import_mini70.optional)(import_mini70.z.boolean()),
20201
- simulateSubagents: (0, import_mini70.optional)(import_mini70.z.boolean()),
20202
- simulateSkills: (0, import_mini70.optional)(import_mini70.z.boolean()),
20203
- gitignoreTargetsOnly: (0, import_mini70.optional)(import_mini70.z.boolean()),
20204
- gitignoreDestination: (0, import_mini70.optional)(GitignoreDestinationSchema),
20205
- dryRun: (0, import_mini70.optional)(import_mini70.z.boolean()),
20206
- check: (0, import_mini70.optional)(import_mini70.z.boolean()),
20187
+ global: (0, import_mini69.optional)(import_mini69.z.boolean()),
20188
+ silent: (0, import_mini69.optional)(import_mini69.z.boolean()),
20189
+ simulateCommands: (0, import_mini69.optional)(import_mini69.z.boolean()),
20190
+ simulateSubagents: (0, import_mini69.optional)(import_mini69.z.boolean()),
20191
+ simulateSkills: (0, import_mini69.optional)(import_mini69.z.boolean()),
20192
+ gitignoreTargetsOnly: (0, import_mini69.optional)(import_mini69.z.boolean()),
20193
+ gitignoreDestination: (0, import_mini69.optional)(GitignoreDestinationSchema),
20194
+ dryRun: (0, import_mini69.optional)(import_mini69.z.boolean()),
20195
+ check: (0, import_mini69.optional)(import_mini69.z.boolean()),
20207
20196
  // Declarative skill sources
20208
- sources: (0, import_mini70.optional)(import_mini70.z.array(SourceEntrySchema))
20197
+ sources: (0, import_mini69.optional)(import_mini69.z.array(SourceEntrySchema))
20209
20198
  });
20210
- var PartialConfigParamsSchema = import_mini70.z.partial(ConfigParamsSchema);
20211
- var ConfigFileSchema = import_mini70.z.object({
20212
- $schema: (0, import_mini70.optional)(import_mini70.z.string()),
20213
- ...import_mini70.z.partial(ConfigParamsSchema).shape
20199
+ var PartialConfigParamsSchema = import_mini69.z.partial(ConfigParamsSchema);
20200
+ var ConfigFileSchema = import_mini69.z.object({
20201
+ $schema: (0, import_mini69.optional)(import_mini69.z.string()),
20202
+ ...import_mini69.z.partial(ConfigParamsSchema).shape
20214
20203
  });
20215
- var RequiredConfigParamsSchema = import_mini70.z.required(ConfigParamsSchema);
20204
+ var RequiredConfigParamsSchema = import_mini69.z.required(ConfigParamsSchema);
20216
20205
  var CONFLICTING_TARGET_PAIRS = [
20217
20206
  ["augmentcode", "augmentcode-legacy"],
20218
20207
  ["claudecode", "claudecode-legacy"]
@@ -20702,7 +20691,7 @@ var import_node_path142 = require("path");
20702
20691
  var import_es_toolkit5 = require("es-toolkit");
20703
20692
 
20704
20693
  // src/features/permissions/permissions-processor.ts
20705
- var import_mini75 = require("zod/mini");
20694
+ var import_mini74 = require("zod/mini");
20706
20695
 
20707
20696
  // src/features/permissions/claudecode-permissions.ts
20708
20697
  var import_node_path137 = require("path");
@@ -20712,14 +20701,14 @@ var import_es_toolkit4 = require("es-toolkit");
20712
20701
  var import_node_path136 = require("path");
20713
20702
 
20714
20703
  // src/types/permissions.ts
20715
- var import_mini71 = require("zod/mini");
20716
- var PermissionActionSchema = import_mini71.z.enum(["allow", "ask", "deny"]);
20717
- var PermissionRulesSchema = import_mini71.z.record(import_mini71.z.string(), PermissionActionSchema);
20718
- var PermissionsConfigSchema = import_mini71.z.looseObject({
20719
- permission: import_mini71.z.record(import_mini71.z.string(), PermissionRulesSchema)
20704
+ var import_mini70 = require("zod/mini");
20705
+ var PermissionActionSchema = import_mini70.z.enum(["allow", "ask", "deny"]);
20706
+ var PermissionRulesSchema = import_mini70.z.record(import_mini70.z.string(), PermissionActionSchema);
20707
+ var PermissionsConfigSchema = import_mini70.z.looseObject({
20708
+ permission: import_mini70.z.record(import_mini70.z.string(), PermissionRulesSchema)
20720
20709
  });
20721
- var RulesyncPermissionsFileSchema = import_mini71.z.looseObject({
20722
- $schema: import_mini71.z.optional(import_mini71.z.string()),
20710
+ var RulesyncPermissionsFileSchema = import_mini70.z.looseObject({
20711
+ $schema: import_mini70.z.optional(import_mini70.z.string()),
20723
20712
  ...PermissionsConfigSchema.shape
20724
20713
  });
20725
20714
 
@@ -21289,7 +21278,7 @@ function mapBashActionToDecision(action) {
21289
21278
  // src/features/permissions/geminicli-permissions.ts
21290
21279
  var import_node_path139 = require("path");
21291
21280
  var smolToml6 = __toESM(require("smol-toml"), 1);
21292
- var import_mini72 = require("zod/mini");
21281
+ var import_mini71 = require("zod/mini");
21293
21282
 
21294
21283
  // src/utils/logger.ts
21295
21284
  var BaseLogger = class {
@@ -21763,14 +21752,14 @@ function regexToGlobPattern(regex) {
21763
21752
  }
21764
21753
  return glob;
21765
21754
  }
21766
- var GeminicliPolicyRuleSchema = import_mini72.z.looseObject({
21767
- toolName: import_mini72.z.string(),
21768
- decision: import_mini72.z.optional(import_mini72.z.unknown()),
21769
- commandPrefix: import_mini72.z.optional(import_mini72.z.string()),
21770
- argsPattern: import_mini72.z.optional(import_mini72.z.string())
21755
+ var GeminicliPolicyRuleSchema = import_mini71.z.looseObject({
21756
+ toolName: import_mini71.z.string(),
21757
+ decision: import_mini71.z.optional(import_mini71.z.unknown()),
21758
+ commandPrefix: import_mini71.z.optional(import_mini71.z.string()),
21759
+ argsPattern: import_mini71.z.optional(import_mini71.z.string())
21771
21760
  });
21772
- var GeminicliPolicyFileSchema = import_mini72.z.looseObject({
21773
- rule: import_mini72.z.optional(import_mini72.z.array(import_mini72.z.looseObject({})))
21761
+ var GeminicliPolicyFileSchema = import_mini71.z.looseObject({
21762
+ rule: import_mini71.z.optional(import_mini71.z.array(import_mini71.z.looseObject({})))
21774
21763
  });
21775
21764
  function extractRules(parsed, logger5) {
21776
21765
  const parsedFile = GeminicliPolicyFileSchema.safeParse(parsed);
@@ -21806,12 +21795,12 @@ function extractPattern(rule) {
21806
21795
 
21807
21796
  // src/features/permissions/kiro-permissions.ts
21808
21797
  var import_node_path140 = require("path");
21809
- var import_mini73 = require("zod/mini");
21810
- var KiroAgentSchema = import_mini73.z.looseObject({
21811
- allowedTools: import_mini73.z.optional(import_mini73.z.array(import_mini73.z.string())),
21812
- toolsSettings: import_mini73.z.optional(import_mini73.z.record(import_mini73.z.string(), import_mini73.z.unknown()))
21798
+ var import_mini72 = require("zod/mini");
21799
+ var KiroAgentSchema = import_mini72.z.looseObject({
21800
+ allowedTools: import_mini72.z.optional(import_mini72.z.array(import_mini72.z.string())),
21801
+ toolsSettings: import_mini72.z.optional(import_mini72.z.record(import_mini72.z.string(), import_mini72.z.unknown()))
21813
21802
  });
21814
- var UnknownRecordSchema = import_mini73.z.record(import_mini73.z.string(), import_mini73.z.unknown());
21803
+ var UnknownRecordSchema = import_mini72.z.record(import_mini72.z.string(), import_mini72.z.unknown());
21815
21804
  var KiroPermissions = class _KiroPermissions extends ToolPermissions {
21816
21805
  static getSettablePaths(_options = {}) {
21817
21806
  return {
@@ -21964,8 +21953,12 @@ function buildKiroPermissionsFromRulesync({
21964
21953
  );
21965
21954
  continue;
21966
21955
  }
21967
- if (action === "allow")
21968
- nextAllowedTools.add(category === "webfetch" ? "web_fetch" : "web_search");
21956
+ const toolName = category === "webfetch" ? "web_fetch" : "web_search";
21957
+ if (action === "allow") {
21958
+ nextAllowedTools.add(toolName);
21959
+ } else {
21960
+ nextAllowedTools.delete(toolName);
21961
+ }
21969
21962
  } else {
21970
21963
  logger5?.warn(`Kiro permissions do not support category: ${category}. Skipping.`);
21971
21964
  }
@@ -21991,13 +21984,13 @@ function asStringArray(value) {
21991
21984
  // src/features/permissions/opencode-permissions.ts
21992
21985
  var import_node_path141 = require("path");
21993
21986
  var import_jsonc_parser5 = require("jsonc-parser");
21994
- var import_mini74 = require("zod/mini");
21995
- var OpencodePermissionSchema = import_mini74.z.union([
21996
- import_mini74.z.enum(["allow", "ask", "deny"]),
21997
- import_mini74.z.record(import_mini74.z.string(), import_mini74.z.enum(["allow", "ask", "deny"]))
21987
+ var import_mini73 = require("zod/mini");
21988
+ var OpencodePermissionSchema = import_mini73.z.union([
21989
+ import_mini73.z.enum(["allow", "ask", "deny"]),
21990
+ import_mini73.z.record(import_mini73.z.string(), import_mini73.z.enum(["allow", "ask", "deny"]))
21998
21991
  ]);
21999
- var OpencodePermissionsConfigSchema = import_mini74.z.looseObject({
22000
- permission: import_mini74.z.optional(import_mini74.z.record(import_mini74.z.string(), OpencodePermissionSchema))
21992
+ var OpencodePermissionsConfigSchema = import_mini73.z.looseObject({
21993
+ permission: import_mini73.z.optional(import_mini73.z.record(import_mini73.z.string(), OpencodePermissionSchema))
22001
21994
  });
22002
21995
  var OpencodePermissions = class _OpencodePermissions extends ToolPermissions {
22003
21996
  json;
@@ -22128,7 +22121,7 @@ var permissionsProcessorToolTargetTuple = [
22128
22121
  "kiro",
22129
22122
  "opencode"
22130
22123
  ];
22131
- var PermissionsProcessorToolTargetSchema = import_mini75.z.enum(permissionsProcessorToolTargetTuple);
22124
+ var PermissionsProcessorToolTargetSchema = import_mini74.z.enum(permissionsProcessorToolTargetTuple);
22132
22125
  var toolPermissionsFactories = /* @__PURE__ */ new Map([
22133
22126
  [
22134
22127
  "claudecode",
@@ -23893,157 +23886,1315 @@ async function initCommand(logger5) {
23893
23886
  logger5.info("2. Run 'rulesync generate' to create configuration files");
23894
23887
  }
23895
23888
 
23896
- // src/lib/sources.ts
23889
+ // src/lib/apm/apm-install.ts
23890
+ var import_node_crypto = require("crypto");
23897
23891
  var import_node_path147 = require("path");
23898
23892
  var import_promise2 = require("es-toolkit/promise");
23899
23893
 
23900
- // src/lib/git-client.ts
23901
- var import_node_child_process = require("child_process");
23894
+ // src/lib/apm/apm-lock.ts
23902
23895
  var import_node_path145 = require("path");
23903
- var import_node_util2 = require("util");
23904
- var execFileAsync = (0, import_node_util2.promisify)(import_node_child_process.execFile);
23905
- var GIT_TIMEOUT_MS = 6e4;
23906
- var ALLOWED_URL_SCHEMES = /^(https?:\/\/|ssh:\/\/|git:\/\/|file:\/\/\/).+$|^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9.-]+:[a-zA-Z0-9_.+/~-]+$/;
23907
- var INSECURE_URL_SCHEMES = /^(git:\/\/|http:\/\/)/;
23908
- var GitClientError = class extends Error {
23909
- constructor(message, cause) {
23910
- super(message, { cause });
23911
- this.name = "GitClientError";
23896
+ var import_js_yaml3 = require("js-yaml");
23897
+ var import_mini75 = require("zod/mini");
23898
+ var APM_LOCKFILE_FILE_NAME = "rulesync-apm.lock.yaml";
23899
+ var APM_LOCKFILE_VERSION = "1";
23900
+ var RULESYNC_CONTENT_HASH_REGEX = /^sha256:[0-9a-f]{64}$/;
23901
+ var ApmLockDependencySchema = import_mini75.z.looseObject({
23902
+ repo_url: import_mini75.z.string(),
23903
+ resolved_commit: (0, import_mini75.optional)(
23904
+ import_mini75.z.string().check((0, import_mini75.refine)((v) => /^[0-9a-f]{40}$/.test(v), "resolved_commit must be a 40-char hex SHA"))
23905
+ ),
23906
+ resolved_ref: (0, import_mini75.optional)(import_mini75.z.string()),
23907
+ version: (0, import_mini75.optional)(import_mini75.z.string()),
23908
+ depth: import_mini75.z.int().check((0, import_mini75.nonnegative)()),
23909
+ resolved_by: (0, import_mini75.optional)(import_mini75.z.string()),
23910
+ package_type: import_mini75.z.string(),
23911
+ // Intentionally loose: the upstream `apm` CLI may write content_hash values
23912
+ // that do not match the strict rulesync format. We accept any string on read
23913
+ // so that a lockfile produced by `apm` round-trips through rulesync without
23914
+ // throwing. Rulesync itself always writes values matching
23915
+ // `RULESYNC_CONTENT_HASH_REGEX`, and `--frozen` integrity checks only
23916
+ // enforce the comparison when the recorded hash matches that shape.
23917
+ content_hash: (0, import_mini75.optional)(import_mini75.z.string()),
23918
+ is_dev: (0, import_mini75.optional)(import_mini75.z.boolean()),
23919
+ deployed_files: import_mini75.z.array(import_mini75.z.string()),
23920
+ source: (0, import_mini75.optional)(import_mini75.z.string()),
23921
+ local_path: (0, import_mini75.optional)(import_mini75.z.string()),
23922
+ virtual_path: (0, import_mini75.optional)(import_mini75.z.string()),
23923
+ is_virtual: (0, import_mini75.optional)(import_mini75.z.boolean())
23924
+ });
23925
+ var ApmLockSchema = import_mini75.z.looseObject({
23926
+ lockfile_version: import_mini75.z.literal("1"),
23927
+ generated_at: import_mini75.z.string(),
23928
+ apm_version: import_mini75.z.string(),
23929
+ dependencies: import_mini75.z.array(ApmLockDependencySchema),
23930
+ mcp_servers: (0, import_mini75.optional)(import_mini75.z.array(import_mini75.z.string()))
23931
+ });
23932
+ function getApmLockPath(baseDir) {
23933
+ return (0, import_node_path145.join)(baseDir, APM_LOCKFILE_FILE_NAME);
23934
+ }
23935
+ function createEmptyApmLock(params) {
23936
+ const base = params.existingLock ? { ...params.existingLock } : {};
23937
+ return {
23938
+ ...base,
23939
+ lockfile_version: APM_LOCKFILE_VERSION,
23940
+ generated_at: (/* @__PURE__ */ new Date()).toISOString(),
23941
+ apm_version: params.apmVersion,
23942
+ dependencies: []
23943
+ };
23944
+ }
23945
+ function parseApmLock(content) {
23946
+ if (!content.trim()) {
23947
+ return null;
23912
23948
  }
23913
- };
23914
- function validateGitUrl(url, options) {
23915
- const ctrl = findControlCharacter(url);
23916
- if (ctrl) {
23917
- throw new GitClientError(
23918
- `Git URL contains control character ${ctrl.hex} at position ${ctrl.position}`
23919
- );
23949
+ let loaded;
23950
+ try {
23951
+ loaded = (0, import_js_yaml3.load)(content);
23952
+ } catch {
23953
+ return null;
23920
23954
  }
23921
- if (!ALLOWED_URL_SCHEMES.test(url)) {
23922
- throw new GitClientError(
23923
- `Unsupported or unsafe git URL: "${url}". Use https, ssh, git, or file schemes.`
23955
+ if (!loaded || typeof loaded !== "object") {
23956
+ return null;
23957
+ }
23958
+ const parsed = ApmLockSchema.safeParse(loaded);
23959
+ if (!parsed.success) {
23960
+ const issues = parsed.error.issues.map((issue) => ` - ${issue.path.join(".") || "<root>"}: ${issue.message}`).join("\n");
23961
+ throw new Error(`Invalid ${APM_LOCKFILE_FILE_NAME}:
23962
+ ${issues}`);
23963
+ }
23964
+ return parsed.data;
23965
+ }
23966
+ async function readApmLock(baseDir) {
23967
+ const path4 = getApmLockPath(baseDir);
23968
+ if (!await fileExists(path4)) {
23969
+ return null;
23970
+ }
23971
+ const content = await readFileContent(path4);
23972
+ return parseApmLock(content);
23973
+ }
23974
+ async function writeApmLock(params) {
23975
+ const path4 = getApmLockPath(params.baseDir);
23976
+ const content = serializeApmLock(params.lock);
23977
+ await writeFileContent(path4, content);
23978
+ }
23979
+ function serializeApmLock(lock) {
23980
+ return (0, import_js_yaml3.dump)(lock, { noRefs: true, lineWidth: -1, sortKeys: false });
23981
+ }
23982
+ function findApmLockDependency(lock, repoUrl) {
23983
+ const target = repoUrl.toLowerCase();
23984
+ return lock.dependencies.find((d) => d.repo_url.toLowerCase() === target);
23985
+ }
23986
+
23987
+ // src/lib/apm/apm-manifest.ts
23988
+ var import_node_path146 = require("path");
23989
+ var import_js_yaml4 = require("js-yaml");
23990
+ var import_mini76 = require("zod/mini");
23991
+ var APM_MANIFEST_FILE_NAME = "apm.yml";
23992
+ var ApmObjectDependencySchema = import_mini76.z.looseObject({
23993
+ git: (0, import_mini76.optional)(import_mini76.z.string()),
23994
+ source: (0, import_mini76.optional)(import_mini76.z.string()),
23995
+ path: (0, import_mini76.optional)(import_mini76.z.string()),
23996
+ ref: (0, import_mini76.optional)(import_mini76.z.string()),
23997
+ alias: (0, import_mini76.optional)(import_mini76.z.string())
23998
+ });
23999
+ var ApmDependencyInputSchema = import_mini76.z.union([import_mini76.z.string(), ApmObjectDependencySchema]);
24000
+ var ApmManifestSchema = import_mini76.z.looseObject({
24001
+ name: (0, import_mini76.optional)(import_mini76.z.string()),
24002
+ version: (0, import_mini76.optional)(import_mini76.z.string()),
24003
+ dependencies: (0, import_mini76.optional)(
24004
+ import_mini76.z.looseObject({
24005
+ apm: (0, import_mini76.optional)(import_mini76.z.array(ApmDependencyInputSchema))
24006
+ })
24007
+ )
24008
+ });
24009
+ function getApmManifestPath(baseDir) {
24010
+ return (0, import_node_path146.join)(baseDir, APM_MANIFEST_FILE_NAME);
24011
+ }
24012
+ async function apmManifestExists(baseDir) {
24013
+ return fileExists(getApmManifestPath(baseDir));
24014
+ }
24015
+ function parseApmManifest(content) {
24016
+ const loaded = (0, import_js_yaml4.load)(content);
24017
+ if (loaded === void 0 || loaded === null) {
24018
+ return { dependencies: [] };
24019
+ }
24020
+ const parsed = ApmManifestSchema.safeParse(loaded);
24021
+ if (!parsed.success) {
24022
+ throw new Error(`Invalid apm.yml: ${parsed.error.message}`);
24023
+ }
24024
+ const raw = parsed.data;
24025
+ const rawDeps = raw.dependencies?.apm ?? [];
24026
+ const dependencies = rawDeps.map(
24027
+ (entry, index) => normalizeDependency(entry, index)
24028
+ );
24029
+ return {
24030
+ name: raw.name,
24031
+ version: raw.version,
24032
+ dependencies
24033
+ };
24034
+ }
24035
+ async function readApmManifest(baseDir) {
24036
+ const path4 = getApmManifestPath(baseDir);
24037
+ const content = await readFileContent(path4);
24038
+ return parseApmManifest(content);
24039
+ }
24040
+ function normalizeDependency(entry, index) {
24041
+ if (typeof entry === "string") {
24042
+ return normalizeStringDependency(entry, index);
24043
+ }
24044
+ const gitUrl = entry.git ?? entry.source;
24045
+ if (!gitUrl) {
24046
+ throw new Error(
24047
+ `apm.yml dependency #${index + 1}: object form requires a "git" field. Received: ${JSON.stringify(entry)}.`
23924
24048
  );
23925
24049
  }
23926
- if (INSECURE_URL_SCHEMES.test(url)) {
23927
- options?.logger?.warn(
23928
- `URL "${url}" uses an unencrypted protocol. Consider using https:// or ssh:// instead.`
24050
+ const parsedUrl = parseHttpsGitHubUrl(gitUrl);
24051
+ if (!parsedUrl) {
24052
+ throw new Error(
24053
+ `apm.yml dependency #${index + 1}: unsupported git URL "${gitUrl}". Only HTTPS GitHub URLs (https://github.com/owner/repo[.git]) are supported in this version. SSH, GitLab, Bitbucket, and other hosts are not yet supported.`
23929
24054
  );
23930
24055
  }
24056
+ if (entry.path !== void 0) {
24057
+ validateSubPath(entry.path, index);
24058
+ }
24059
+ return {
24060
+ gitUrl: parsedUrl.gitUrl,
24061
+ owner: parsedUrl.owner,
24062
+ repo: parsedUrl.repo,
24063
+ ref: entry.ref,
24064
+ path: entry.path,
24065
+ alias: entry.alias
24066
+ };
23931
24067
  }
23932
- function validateRef(ref) {
23933
- if (ref.startsWith("-")) {
23934
- throw new GitClientError(`Ref must not start with "-": "${ref}"`);
24068
+ function validateSubPath(subPath, index) {
24069
+ if (subPath === "" || subPath.startsWith("/") || subPath.startsWith("\\")) {
24070
+ throw new Error(
24071
+ `apm.yml dependency #${index + 1}: "path" must be a non-empty relative path without a leading slash. Received: ${JSON.stringify(subPath)}.`
24072
+ );
23935
24073
  }
23936
- const ctrl = findControlCharacter(ref);
23937
- if (ctrl) {
23938
- throw new GitClientError(
23939
- `Ref contains control character ${ctrl.hex} at position ${ctrl.position}`
24074
+ const segments = subPath.split(/[/\\]/);
24075
+ if (segments.includes("..")) {
24076
+ throw new Error(
24077
+ `apm.yml dependency #${index + 1}: "path" must not contain ".." segments. Received: ${JSON.stringify(subPath)}.`
23940
24078
  );
23941
24079
  }
23942
24080
  }
23943
- var gitChecked = false;
23944
- async function checkGitAvailable() {
23945
- if (gitChecked) return;
23946
- try {
23947
- await execFileAsync("git", ["--version"], { timeout: GIT_TIMEOUT_MS });
23948
- gitChecked = true;
23949
- } catch {
23950
- throw new GitClientError("git is not installed or not found in PATH");
24081
+ function normalizeStringDependency(entry, index) {
24082
+ const trimmed = entry.trim();
24083
+ if (!trimmed) {
24084
+ throw new Error(`apm.yml dependency #${index + 1}: entry must be a non-empty string.`);
24085
+ }
24086
+ rejectUnsupportedShorthand(trimmed, index);
24087
+ if (trimmed.startsWith("https://")) {
24088
+ const [urlPart, refPart2] = splitOnFirst(trimmed, "#");
24089
+ const parsed = parseHttpsGitHubUrl(urlPart);
24090
+ if (!parsed) {
24091
+ throw new Error(
24092
+ `apm.yml dependency #${index + 1}: unsupported URL "${urlPart}". Only HTTPS GitHub URLs (https://github.com/owner/repo[.git]) are supported in this version.`
24093
+ );
24094
+ }
24095
+ return {
24096
+ gitUrl: parsed.gitUrl,
24097
+ owner: parsed.owner,
24098
+ repo: parsed.repo,
24099
+ ref: refPart2 || void 0
24100
+ };
23951
24101
  }
23952
- }
23953
- async function resolveDefaultRef(url) {
23954
- validateGitUrl(url);
23955
- await checkGitAvailable();
23956
- try {
23957
- const { stdout } = await execFileAsync("git", ["ls-remote", "--symref", "--", url, "HEAD"], {
23958
- timeout: GIT_TIMEOUT_MS
23959
- });
23960
- const ref = stdout.match(/^ref: refs\/heads\/(.+)\tHEAD$/m)?.[1];
23961
- const sha = stdout.match(/^([0-9a-f]{40})\tHEAD$/m)?.[1];
23962
- if (!ref || !sha) throw new GitClientError(`Could not parse default branch from: ${url}`);
23963
- validateRef(ref);
23964
- return { ref, sha };
23965
- } catch (error) {
23966
- if (error instanceof GitClientError) throw error;
23967
- throw new GitClientError(`Failed to resolve default ref for ${url}`, error);
24102
+ const [ownerRepo, refPart] = splitOnFirst(trimmed, "#");
24103
+ const slashIndex = ownerRepo.indexOf("/");
24104
+ if (slashIndex === -1 || slashIndex === 0 || slashIndex === ownerRepo.length - 1) {
24105
+ throw new Error(
24106
+ `apm.yml dependency #${index + 1}: shorthand "${entry}" must be in the form "owner/repo[#ref]".`
24107
+ );
23968
24108
  }
23969
- }
23970
- async function resolveRefToSha(url, ref) {
23971
- validateGitUrl(url);
23972
- validateRef(ref);
23973
- await checkGitAvailable();
23974
- try {
23975
- const { stdout } = await execFileAsync("git", ["ls-remote", "--", url, ref], {
23976
- timeout: GIT_TIMEOUT_MS
23977
- });
23978
- const sha = stdout.match(/^([0-9a-f]{40})\t/m)?.[1];
23979
- if (!sha) throw new GitClientError(`Ref "${ref}" not found in ${url}`);
23980
- return sha;
23981
- } catch (error) {
23982
- if (error instanceof GitClientError) throw error;
23983
- throw new GitClientError(`Failed to resolve ref "${ref}" for ${url}`, error);
24109
+ if (ownerRepo.includes("/", slashIndex + 1)) {
24110
+ throw new Error(
24111
+ `apm.yml dependency #${index + 1}: FQDN shorthand or sub-path shorthand ("${entry}") is not yet supported. Use the object form with an explicit "git" URL.`
24112
+ );
23984
24113
  }
24114
+ const owner = ownerRepo.substring(0, slashIndex).toLowerCase();
24115
+ const repo = ownerRepo.substring(slashIndex + 1).toLowerCase();
24116
+ return {
24117
+ gitUrl: `https://github.com/${owner}/${repo}.git`,
24118
+ owner,
24119
+ repo,
24120
+ ref: refPart || void 0
24121
+ };
23985
24122
  }
23986
- async function fetchSkillFiles(params) {
23987
- const { url, ref, skillsPath, logger: logger5 } = params;
23988
- validateGitUrl(url, { logger: logger5 });
23989
- validateRef(ref);
23990
- if (skillsPath.split(/[/\\]/).includes("..") || (0, import_node_path145.isAbsolute)(skillsPath)) {
23991
- throw new GitClientError(
23992
- `Invalid skillsPath "${skillsPath}": must be a relative path without ".."`
24123
+ function rejectUnsupportedShorthand(entry, index) {
24124
+ if (entry.startsWith("./") || entry.startsWith("../") || entry.startsWith("/")) {
24125
+ throw new Error(
24126
+ `apm.yml dependency #${index + 1}: local path dependencies ("${entry}") are not yet supported by rulesync.`
23993
24127
  );
23994
24128
  }
23995
- const ctrl = findControlCharacter(skillsPath);
23996
- if (ctrl) {
23997
- throw new GitClientError(
23998
- `skillsPath contains control character ${ctrl.hex} at position ${ctrl.position}`
24129
+ if (entry.startsWith("git@") || entry.startsWith("ssh://")) {
24130
+ throw new Error(
24131
+ `apm.yml dependency #${index + 1}: SSH URL dependencies ("${entry}") are not yet supported. Use an HTTPS GitHub URL.`
23999
24132
  );
24000
24133
  }
24001
- await checkGitAvailable();
24002
- const tmpDir = await createTempDirectory("rulesync-git-");
24003
- try {
24004
- await execFileAsync(
24005
- "git",
24006
- [
24007
- "clone",
24008
- "--depth",
24009
- "1",
24010
- "--branch",
24011
- ref,
24012
- "--no-checkout",
24013
- "--filter=blob:none",
24014
- "--",
24015
- url,
24016
- tmpDir
24017
- ],
24018
- { timeout: GIT_TIMEOUT_MS }
24134
+ if (entry.includes("@marketplace")) {
24135
+ throw new Error(
24136
+ `apm.yml dependency #${index + 1}: APM marketplace dependencies ("${entry}") are not yet supported.`
24019
24137
  );
24020
- await execFileAsync("git", ["-C", tmpDir, "sparse-checkout", "set", "--", skillsPath], {
24021
- timeout: GIT_TIMEOUT_MS
24022
- });
24023
- await execFileAsync("git", ["-C", tmpDir, "checkout"], { timeout: GIT_TIMEOUT_MS });
24024
- const skillsDir = (0, import_node_path145.join)(tmpDir, skillsPath);
24025
- if (!await directoryExists(skillsDir)) return [];
24026
- return await walkDirectory(skillsDir, skillsDir, 0, { totalFiles: 0, totalSize: 0 }, logger5);
24027
- } catch (error) {
24028
- if (error instanceof GitClientError) throw error;
24029
- throw new GitClientError(`Failed to fetch skill files from ${url}`, error);
24030
- } finally {
24031
- await removeTempDirectory(tmpDir);
24032
24138
  }
24033
24139
  }
24034
- var MAX_WALK_DEPTH = 20;
24035
- var MAX_TOTAL_FILES = 1e4;
24036
- var MAX_TOTAL_SIZE = 100 * 1024 * 1024;
24037
- async function walkDirectory(dir, baseDir, depth = 0, ctx = { totalFiles: 0, totalSize: 0 }, logger5) {
24038
- if (depth > MAX_WALK_DEPTH) {
24039
- throw new GitClientError(
24040
- `Directory tree exceeds max depth of ${MAX_WALK_DEPTH}: "${dir}". Aborting to prevent resource exhaustion.`
24140
+ function parseHttpsGitHubUrl(url) {
24141
+ let parsed;
24142
+ try {
24143
+ parsed = new URL(url);
24144
+ } catch {
24145
+ return null;
24146
+ }
24147
+ const host = parsed.hostname.toLowerCase();
24148
+ if (host !== "github.com" && host !== "www.github.com") {
24149
+ return null;
24150
+ }
24151
+ const segments = parsed.pathname.split("/").filter(Boolean);
24152
+ if (segments.length < 2) {
24153
+ return null;
24154
+ }
24155
+ const rawOwner = segments[0];
24156
+ const rawRepo = segments[1];
24157
+ if (!rawOwner || !rawRepo) {
24158
+ return null;
24159
+ }
24160
+ const owner = rawOwner.toLowerCase();
24161
+ const repo = rawRepo.replace(/\.git$/, "").toLowerCase();
24162
+ return {
24163
+ gitUrl: `https://github.com/${owner}/${repo}.git`,
24164
+ owner,
24165
+ repo
24166
+ };
24167
+ }
24168
+ function splitOnFirst(input, separator) {
24169
+ const idx = input.indexOf(separator);
24170
+ if (idx === -1) return [input, void 0];
24171
+ return [input.substring(0, idx), input.substring(idx + 1)];
24172
+ }
24173
+
24174
+ // src/lib/apm/apm-install.ts
24175
+ var RULESYNC_APM_COMPAT_VERSION = "rulesync-compat/0.1";
24176
+ var APM_PRIMITIVES = [
24177
+ {
24178
+ sourceDir: ".apm/instructions",
24179
+ deployDir: ".github/instructions",
24180
+ packageType: "apm_package"
24181
+ },
24182
+ {
24183
+ sourceDir: ".apm/skills",
24184
+ deployDir: ".github/skills",
24185
+ packageType: "apm_package"
24186
+ }
24187
+ ];
24188
+ async function installApm(params) {
24189
+ const { baseDir, options = {}, logger: logger5 } = params;
24190
+ const manifest = await readApmManifest(baseDir);
24191
+ if (manifest.dependencies.length === 0) {
24192
+ logger5.warn("apm.yml has no dependencies.apm entries. Nothing to install.");
24193
+ return { dependenciesProcessed: 0, deployedFileCount: 0, failedDependencyCount: 0 };
24194
+ }
24195
+ const existingLock = await readApmLock(baseDir);
24196
+ if (options.frozen) {
24197
+ if (!existingLock) {
24198
+ throw new Error(
24199
+ "Frozen install failed: rulesync-apm.lock.yaml is missing. Run 'rulesync install --mode apm' to create it."
24200
+ );
24201
+ }
24202
+ const missing = manifest.dependencies.filter(
24203
+ (dep) => !findApmLockDependency(existingLock, canonicalRepoUrl(dep))
24204
+ );
24205
+ if (missing.length > 0) {
24206
+ const names = missing.map((d) => d.gitUrl).join(", ");
24207
+ throw new Error(
24208
+ `Frozen install failed: rulesync-apm.lock.yaml is missing entries for: ${names}. Run 'rulesync install --mode apm' to update the lockfile.`
24209
+ );
24210
+ }
24211
+ const drifted = manifest.dependencies.filter((dep) => {
24212
+ if (dep.ref === void 0) return false;
24213
+ const locked = findApmLockDependency(existingLock, canonicalRepoUrl(dep));
24214
+ return locked?.resolved_ref !== void 0 && locked.resolved_ref !== dep.ref;
24215
+ });
24216
+ if (drifted.length > 0) {
24217
+ const names = drifted.map((d) => {
24218
+ const locked = findApmLockDependency(existingLock, canonicalRepoUrl(d));
24219
+ return `${d.gitUrl} (manifest=${d.ref}, lock=${locked?.resolved_ref})`;
24220
+ }).join(", ");
24221
+ throw new Error(
24222
+ `Frozen install failed: manifest ref does not match rulesync-apm.lock.yaml for: ${names}. Run 'rulesync install --mode apm' to update the lockfile.`
24223
+ );
24224
+ }
24225
+ }
24226
+ const token = GitHubClient.resolveToken(options.token);
24227
+ const client = new GitHubClient({ token });
24228
+ const semaphore = new import_promise2.Semaphore(FETCH_CONCURRENCY_LIMIT);
24229
+ const newLock = createEmptyApmLock({
24230
+ apmVersion: existingLock?.apm_version ?? RULESYNC_APM_COMPAT_VERSION,
24231
+ existingLock
24232
+ });
24233
+ const frozen = options.frozen ?? false;
24234
+ const runOne = async (dep) => {
24235
+ const installed = await installDependency({
24236
+ dep,
24237
+ client,
24238
+ semaphore,
24239
+ baseDir,
24240
+ existingLock,
24241
+ frozen,
24242
+ update: options.update ?? false,
24243
+ logger: logger5
24244
+ });
24245
+ return {
24246
+ status: "ok",
24247
+ lockEntry: installed.lockEntry,
24248
+ deployedCount: installed.deployedFiles.length
24249
+ };
24250
+ };
24251
+ const results = frozen ? await Promise.all(manifest.dependencies.map(runOne)) : await Promise.all(
24252
+ manifest.dependencies.map(async (dep) => {
24253
+ try {
24254
+ return await runOne(dep);
24255
+ } catch (error) {
24256
+ logger5.error(`Failed to install apm dependency "${dep.gitUrl}": ${formatError(error)}`);
24257
+ if (error instanceof GitHubClientError) {
24258
+ logGitHubAuthHints({ error, logger: logger5 });
24259
+ }
24260
+ const previous = existingLock ? findApmLockDependency(existingLock, canonicalRepoUrl(dep)) : void 0;
24261
+ return { status: "failed", previous };
24262
+ }
24263
+ })
24264
+ );
24265
+ let totalDeployed = 0;
24266
+ let failedCount = 0;
24267
+ for (const result of results) {
24268
+ if (result.status === "ok") {
24269
+ newLock.dependencies.push(result.lockEntry);
24270
+ totalDeployed += result.deployedCount;
24271
+ } else {
24272
+ failedCount += 1;
24273
+ if (result.previous) {
24274
+ newLock.dependencies.push(result.previous);
24275
+ }
24276
+ }
24277
+ }
24278
+ if (existingLock) {
24279
+ const newDeployedFiles = new Set(newLock.dependencies.flatMap((d) => d.deployed_files));
24280
+ const toDelete = [];
24281
+ for (const prev of existingLock.dependencies) {
24282
+ for (const deployed of prev.deployed_files) {
24283
+ if (!newDeployedFiles.has(deployed)) {
24284
+ toDelete.push(deployed);
24285
+ }
24286
+ }
24287
+ }
24288
+ for (const relativePath of toDelete) {
24289
+ if (import_node_path147.posix.isAbsolute(relativePath) || relativePath.split(/[/\\]/).includes("..")) {
24290
+ logger5.warn(`Refusing to remove stale apm file with suspicious path: "${relativePath}".`);
24291
+ continue;
24292
+ }
24293
+ try {
24294
+ checkPathTraversal({ relativePath, intendedRootDir: baseDir });
24295
+ } catch {
24296
+ logger5.warn(`Refusing to remove stale apm file outside baseDir: "${relativePath}".`);
24297
+ continue;
24298
+ }
24299
+ const absolute = (0, import_node_path147.join)(baseDir, relativePath);
24300
+ await removeFile(absolute);
24301
+ logger5.debug(`Removed stale apm file: ${relativePath}`);
24302
+ }
24303
+ }
24304
+ if (!frozen) {
24305
+ newLock.generated_at = (/* @__PURE__ */ new Date()).toISOString();
24306
+ await writeApmLock({ baseDir, lock: newLock });
24307
+ if (failedCount === 0) {
24308
+ logger5.debug("rulesync-apm.lock.yaml updated.");
24309
+ } else {
24310
+ logger5.warn(
24311
+ `rulesync-apm.lock.yaml written with partially successful installs (${failedCount} dep(s) failed).`
24312
+ );
24313
+ }
24314
+ }
24315
+ return {
24316
+ dependenciesProcessed: manifest.dependencies.length,
24317
+ deployedFileCount: totalDeployed,
24318
+ failedDependencyCount: failedCount
24319
+ };
24320
+ }
24321
+ async function installDependency(params) {
24322
+ const { dep, client, semaphore, baseDir, existingLock, frozen, update, logger: logger5 } = params;
24323
+ const repoUrl = canonicalRepoUrl(dep);
24324
+ const locked = existingLock ? findApmLockDependency(existingLock, repoUrl) : void 0;
24325
+ let resolvedRef;
24326
+ let resolvedSha;
24327
+ if (locked && !update && locked.resolved_commit && locked.resolved_ref) {
24328
+ resolvedRef = locked.resolved_ref;
24329
+ resolvedSha = locked.resolved_commit;
24330
+ logger5.debug(`Using locked commit for ${repoUrl}: ${resolvedSha}`);
24331
+ } else {
24332
+ resolvedRef = dep.ref ?? await client.getDefaultBranch(dep.owner, dep.repo);
24333
+ resolvedSha = await client.resolveRefToSha(dep.owner, dep.repo, resolvedRef);
24334
+ logger5.debug(`Resolved ${repoUrl} ref "${resolvedRef}" -> ${resolvedSha}`);
24335
+ }
24336
+ const deployed = [];
24337
+ for (const primitive of APM_PRIMITIVES) {
24338
+ const remoteBase = dep.path ? toPosixPath(import_node_path147.posix.join(dep.path, primitive.sourceDir)) : primitive.sourceDir;
24339
+ const files = await listPrimitiveFiles({
24340
+ client,
24341
+ semaphore,
24342
+ owner: dep.owner,
24343
+ repo: dep.repo,
24344
+ ref: resolvedSha,
24345
+ remoteBase,
24346
+ logger: logger5
24347
+ });
24348
+ if (files.length === 0) continue;
24349
+ for (const file of files) {
24350
+ if (file.size > MAX_FILE_SIZE) {
24351
+ logger5.warn(
24352
+ `Skipping "${file.path}" from ${repoUrl}: ${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`
24353
+ );
24354
+ continue;
24355
+ }
24356
+ const relativeToBase = import_node_path147.posix.relative(remoteBase, toPosixPath(file.path));
24357
+ if (!relativeToBase || relativeToBase.startsWith("..") || import_node_path147.posix.isAbsolute(relativeToBase)) {
24358
+ logger5.warn(
24359
+ `Skipping "${file.path}" from ${repoUrl}: resolved outside of "${remoteBase}".`
24360
+ );
24361
+ continue;
24362
+ }
24363
+ const deployRelative = toPosixPath((0, import_node_path147.join)(primitive.deployDir, relativeToBase));
24364
+ checkPathTraversal({
24365
+ relativePath: deployRelative,
24366
+ intendedRootDir: baseDir
24367
+ });
24368
+ const content = await withSemaphore(
24369
+ semaphore,
24370
+ () => client.getFileContent(dep.owner, dep.repo, file.path, resolvedSha)
24371
+ );
24372
+ const byteLength = Buffer.byteLength(content, "utf8");
24373
+ if (byteLength > MAX_FILE_SIZE) {
24374
+ logger5.warn(
24375
+ `Skipping "${file.path}" from ${repoUrl}: fetched ${(byteLength / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`
24376
+ );
24377
+ continue;
24378
+ }
24379
+ deployed.push({ path: deployRelative, content });
24380
+ if (!frozen) {
24381
+ await writeFileContent((0, import_node_path147.join)(baseDir, deployRelative), content);
24382
+ }
24383
+ }
24384
+ }
24385
+ deployed.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
24386
+ const deployedFiles = deployed.map((d) => d.path);
24387
+ const contentHash = computeContentHash(deployed);
24388
+ if (frozen && locked?.content_hash) {
24389
+ if (RULESYNC_CONTENT_HASH_REGEX.test(locked.content_hash)) {
24390
+ if (locked.content_hash !== contentHash) {
24391
+ throw new Error(
24392
+ `content_hash mismatch for ${repoUrl}: lock=${locked.content_hash} computed=${contentHash}. Refuse to trust the deployment under --frozen.`
24393
+ );
24394
+ }
24395
+ } else {
24396
+ logger5.debug(
24397
+ `Skipping content_hash integrity check for ${repoUrl}: recorded hash "${locked.content_hash}" was not written by rulesync.`
24398
+ );
24399
+ }
24400
+ }
24401
+ if (frozen) {
24402
+ for (const { path: deployRelative, content } of deployed) {
24403
+ await writeFileContent((0, import_node_path147.join)(baseDir, deployRelative), content);
24404
+ }
24405
+ }
24406
+ const lockEntry = {
24407
+ repo_url: repoUrl,
24408
+ resolved_commit: resolvedSha,
24409
+ resolved_ref: resolvedRef,
24410
+ depth: 1,
24411
+ package_type: "apm_package",
24412
+ content_hash: contentHash,
24413
+ deployed_files: deployedFiles
24414
+ };
24415
+ if (dep.path) {
24416
+ lockEntry.virtual_path = dep.path;
24417
+ }
24418
+ logger5.info(`Installed ${deployedFiles.length} file(s) from ${repoUrl}@${shortSha(resolvedSha)}`);
24419
+ return { lockEntry, deployedFiles };
24420
+ }
24421
+ function computeContentHash(files) {
24422
+ const hash = (0, import_node_crypto.createHash)("sha256");
24423
+ for (const { path: path4, content } of files) {
24424
+ hash.update(path4);
24425
+ hash.update("\0");
24426
+ hash.update(content);
24427
+ hash.update("\0");
24428
+ }
24429
+ return `sha256:${hash.digest("hex")}`;
24430
+ }
24431
+ async function listPrimitiveFiles(params) {
24432
+ const { client, semaphore, owner, repo, ref, remoteBase, logger: logger5 } = params;
24433
+ try {
24434
+ return await listDirectoryRecursive({
24435
+ client,
24436
+ owner,
24437
+ repo,
24438
+ path: remoteBase,
24439
+ ref,
24440
+ semaphore
24441
+ });
24442
+ } catch (error) {
24443
+ if (error instanceof GitHubClientError && error.statusCode === 404) {
24444
+ logger5.debug(`No ${remoteBase}/ in ${owner}/${repo}, skipping.`);
24445
+ return [];
24446
+ }
24447
+ throw error;
24448
+ }
24449
+ }
24450
+ function canonicalRepoUrl(dep) {
24451
+ return `https://github.com/${dep.owner}/${dep.repo}`;
24452
+ }
24453
+ function shortSha(sha) {
24454
+ return sha.substring(0, 7);
24455
+ }
24456
+
24457
+ // src/lib/gh/gh-install.ts
24458
+ var import_node_crypto2 = require("crypto");
24459
+ var import_node_path150 = require("path");
24460
+ var import_promise3 = require("es-toolkit/promise");
24461
+
24462
+ // src/lib/gh/gh-frontmatter.ts
24463
+ var import_js_yaml5 = require("js-yaml");
24464
+ var FRONTMATTER_FENCE = "---";
24465
+ function injectSourceMetadata(params) {
24466
+ const { content, source, repository, ref } = params;
24467
+ const provenance = { source, repository, ref };
24468
+ let openFenceLen;
24469
+ if (content.startsWith(`${FRONTMATTER_FENCE}\r
24470
+ `)) {
24471
+ openFenceLen = 5;
24472
+ } else if (content.startsWith(`${FRONTMATTER_FENCE}
24473
+ `)) {
24474
+ openFenceLen = 4;
24475
+ } else if (content === FRONTMATTER_FENCE) {
24476
+ openFenceLen = 3;
24477
+ } else {
24478
+ const yaml2 = (0, import_js_yaml5.dump)(provenance, { noRefs: true, lineWidth: -1, sortKeys: false });
24479
+ return `${FRONTMATTER_FENCE}
24480
+ ${yaml2}${FRONTMATTER_FENCE}
24481
+ ${content}`;
24482
+ }
24483
+ const afterOpen = content.substring(openFenceLen);
24484
+ let fmBody;
24485
+ let rest;
24486
+ if (afterOpen.startsWith("---\n") || afterOpen.startsWith("---\r\n") || afterOpen === "---") {
24487
+ fmBody = "";
24488
+ const fenceLen = afterOpen.startsWith("---\r\n") ? 5 : afterOpen === "---" ? 3 : 4;
24489
+ rest = afterOpen.substring(fenceLen);
24490
+ } else {
24491
+ const match = /\n---(\r?\n|$)/.exec(afterOpen);
24492
+ if (!match) {
24493
+ throw new Error("invalid frontmatter");
24494
+ }
24495
+ fmBody = afterOpen.substring(0, match.index);
24496
+ rest = afterOpen.substring(match.index + match[0].length);
24497
+ }
24498
+ let loaded;
24499
+ try {
24500
+ loaded = (0, import_js_yaml5.load)(fmBody);
24501
+ } catch {
24502
+ throw new Error("invalid frontmatter");
24503
+ }
24504
+ if (loaded === null || loaded === void 0) {
24505
+ const yaml2 = (0, import_js_yaml5.dump)(provenance, { noRefs: true, lineWidth: -1, sortKeys: false });
24506
+ return `${FRONTMATTER_FENCE}
24507
+ ${yaml2}${FRONTMATTER_FENCE}
24508
+ ${rest}`;
24509
+ }
24510
+ if (typeof loaded !== "object" || Array.isArray(loaded)) {
24511
+ throw new Error("invalid frontmatter");
24512
+ }
24513
+ const existing = loaded;
24514
+ const merged = {
24515
+ ...existing,
24516
+ ...provenance
24517
+ };
24518
+ const yaml = (0, import_js_yaml5.dump)(merged, { noRefs: true, lineWidth: -1, sortKeys: false });
24519
+ return `${FRONTMATTER_FENCE}
24520
+ ${yaml}${FRONTMATTER_FENCE}
24521
+ ${rest}`;
24522
+ }
24523
+
24524
+ // src/lib/gh/gh-lock.ts
24525
+ var import_node_path148 = require("path");
24526
+ var import_js_yaml6 = require("js-yaml");
24527
+ var import_mini77 = require("zod/mini");
24528
+ var GH_LOCKFILE_FILE_NAME = "rulesync-gh.lock.yaml";
24529
+ var GH_LOCKFILE_VERSION = "1";
24530
+ var RULESYNC_CONTENT_HASH_REGEX2 = /^sha256:[0-9a-f]{64}$/;
24531
+ var ScopeSchema = import_mini77.z.enum(["project", "user"]);
24532
+ var GhLockInstallationSchema = import_mini77.z.looseObject({
24533
+ source: import_mini77.z.string(),
24534
+ owner: import_mini77.z.string(),
24535
+ repo: import_mini77.z.string(),
24536
+ agent: import_mini77.z.string(),
24537
+ scope: ScopeSchema,
24538
+ skill: import_mini77.z.string(),
24539
+ requested_ref: (0, import_mini77.optional)(import_mini77.z.string()),
24540
+ resolved_ref: import_mini77.z.string(),
24541
+ resolved_commit: import_mini77.z.string().check((0, import_mini77.refine)((v) => /^[0-9a-f]{40}$/.test(v), "resolved_commit must be a 40-char hex SHA")),
24542
+ install_dir: import_mini77.z.string(),
24543
+ deployed_files: import_mini77.z.array(import_mini77.z.string()),
24544
+ content_hash: (0, import_mini77.optional)(import_mini77.z.string())
24545
+ });
24546
+ var GhLockSchema = import_mini77.z.looseObject({
24547
+ lockfile_version: import_mini77.z.literal("1"),
24548
+ generated_at: import_mini77.z.string(),
24549
+ installations: import_mini77.z.array(GhLockInstallationSchema)
24550
+ });
24551
+ function getGhLockPath(baseDir) {
24552
+ return (0, import_node_path148.join)(baseDir, GH_LOCKFILE_FILE_NAME);
24553
+ }
24554
+ function createEmptyGhLock(params) {
24555
+ const base = params?.existingLock ? { ...params.existingLock } : {};
24556
+ return {
24557
+ ...base,
24558
+ lockfile_version: GH_LOCKFILE_VERSION,
24559
+ generated_at: (/* @__PURE__ */ new Date()).toISOString(),
24560
+ installations: []
24561
+ };
24562
+ }
24563
+ function parseGhLock(content) {
24564
+ if (!content.trim()) {
24565
+ return null;
24566
+ }
24567
+ let loaded;
24568
+ try {
24569
+ loaded = (0, import_js_yaml6.load)(content);
24570
+ } catch {
24571
+ return null;
24572
+ }
24573
+ if (!loaded || typeof loaded !== "object") {
24574
+ return null;
24575
+ }
24576
+ const parsed = GhLockSchema.safeParse(loaded);
24577
+ if (!parsed.success) {
24578
+ const issues = parsed.error.issues.map((issue) => ` - ${issue.path.join(".") || "<root>"}: ${issue.message}`).join("\n");
24579
+ throw new Error(`Invalid ${GH_LOCKFILE_FILE_NAME}:
24580
+ ${issues}`);
24581
+ }
24582
+ return parsed.data;
24583
+ }
24584
+ async function readGhLock(baseDir) {
24585
+ const path4 = getGhLockPath(baseDir);
24586
+ if (!await fileExists(path4)) {
24587
+ return null;
24588
+ }
24589
+ const content = await readFileContent(path4);
24590
+ return parseGhLock(content);
24591
+ }
24592
+ async function writeGhLock(params) {
24593
+ const path4 = getGhLockPath(params.baseDir);
24594
+ const content = serializeGhLock(params.lock);
24595
+ await writeFileContent(path4, content);
24596
+ }
24597
+ function serializeGhLock(lock) {
24598
+ return (0, import_js_yaml6.dump)(lock, { noRefs: true, lineWidth: -1, sortKeys: false });
24599
+ }
24600
+ function findGhLockInstallation(lock, params) {
24601
+ const target = params.source.toLowerCase();
24602
+ return lock.installations.find(
24603
+ (i) => i.source.toLowerCase() === target && i.agent === params.agent && i.scope === params.scope && i.skill === params.skill
24604
+ );
24605
+ }
24606
+
24607
+ // src/lib/gh/gh-paths.ts
24608
+ var import_node_path149 = require("path");
24609
+ var GH_AGENTS = [
24610
+ "github-copilot",
24611
+ "claude-code",
24612
+ "cursor",
24613
+ "codex",
24614
+ "gemini",
24615
+ "antigravity"
24616
+ ];
24617
+ function relativeInstallDirFor(params) {
24618
+ const { agent, scope } = params;
24619
+ if (scope === "project") {
24620
+ if (agent === "claude-code") {
24621
+ return (0, import_node_path149.join)(".claude", "skills");
24622
+ }
24623
+ return (0, import_node_path149.join)(".agents", "skills");
24624
+ }
24625
+ switch (agent) {
24626
+ case "github-copilot":
24627
+ return (0, import_node_path149.join)(".copilot", "skills");
24628
+ case "claude-code":
24629
+ return (0, import_node_path149.join)(".claude", "skills");
24630
+ case "cursor":
24631
+ return (0, import_node_path149.join)(".cursor", "skills");
24632
+ case "codex":
24633
+ return (0, import_node_path149.join)(".codex", "skills");
24634
+ case "gemini":
24635
+ return (0, import_node_path149.join)(".gemini", "skills");
24636
+ case "antigravity":
24637
+ return (0, import_node_path149.join)(".gemini", "antigravity", "skills");
24638
+ }
24639
+ }
24640
+
24641
+ // src/lib/gh/gh-install.ts
24642
+ var SKILLS_REMOTE_DIR = "skills";
24643
+ var SKILL_FILE_NAME2 = "SKILL.md";
24644
+ async function installGh(params) {
24645
+ const { baseDir, sources, options = {}, logger: logger5 } = params;
24646
+ if (sources.length === 0) {
24647
+ return { sourcesProcessed: 0, installedSkillCount: 0, failedSourceCount: 0 };
24648
+ }
24649
+ const resolvedSources = sources.map((entry) => {
24650
+ const parsed = parseSource(entry.source);
24651
+ if (parsed.provider !== "github") {
24652
+ throw new Error(
24653
+ `--mode gh only supports GitHub sources. "${entry.source}" resolves to provider "${parsed.provider}".`
24654
+ );
24655
+ }
24656
+ if (entry.transport !== void 0 && entry.transport !== "github") {
24657
+ throw new Error(
24658
+ `--mode gh: field "transport" is not supported (got "${entry.transport}" for source "${entry.source}"). Drop the field or switch to --mode rulesync.`
24659
+ );
24660
+ }
24661
+ if (entry.path !== void 0) {
24662
+ throw new Error(
24663
+ `--mode gh: field "path" is not supported for source "${entry.source}". The remote layout is fixed to "skills/<name>/SKILL.md".`
24664
+ );
24665
+ }
24666
+ const agent = entry.agent ?? "github-copilot";
24667
+ if (!GH_AGENTS.includes(agent)) {
24668
+ throw new Error(
24669
+ `--mode gh: unknown agent "${agent}" for source "${entry.source}". Valid agents: ${GH_AGENTS.join(", ")}.`
24670
+ );
24671
+ }
24672
+ const scope = entry.scope ?? "project";
24673
+ return {
24674
+ entry,
24675
+ owner: parsed.owner,
24676
+ repo: parsed.repo,
24677
+ ref: entry.ref ?? parsed.ref,
24678
+ agent,
24679
+ scope
24680
+ };
24681
+ });
24682
+ const existingLock = await readGhLock(baseDir);
24683
+ const frozen = options.frozen ?? false;
24684
+ const update = options.update ?? false;
24685
+ if (frozen && !existingLock) {
24686
+ throw new Error(
24687
+ "Frozen install failed: rulesync-gh.lock.yaml is missing. Run 'rulesync install --mode gh' to create it."
24688
+ );
24689
+ }
24690
+ if (frozen && existingLock) {
24691
+ const uncovered = [];
24692
+ for (const rs of resolvedSources) {
24693
+ const hasAny = existingLock.installations.some(
24694
+ (i) => i.source.toLowerCase() === rs.entry.source.toLowerCase() && i.agent === rs.agent && i.scope === rs.scope
24695
+ );
24696
+ if (!hasAny) {
24697
+ uncovered.push(`${rs.entry.source} (agent=${rs.agent}, scope=${rs.scope})`);
24698
+ }
24699
+ }
24700
+ if (uncovered.length > 0) {
24701
+ throw new Error(
24702
+ `Frozen install failed: rulesync-gh.lock.yaml is missing entries for: ${uncovered.join(", ")}. Run 'rulesync install --mode gh' to update the lockfile.`
24703
+ );
24704
+ }
24705
+ const drifted = [];
24706
+ for (const rs of resolvedSources) {
24707
+ if (!rs.ref) continue;
24708
+ const matches = existingLock.installations.filter(
24709
+ (i) => i.source.toLowerCase() === rs.entry.source.toLowerCase()
24710
+ );
24711
+ for (const m of matches) {
24712
+ if (m.requested_ref !== void 0 && m.requested_ref !== rs.ref) {
24713
+ drifted.push(`${rs.entry.source} (manifest=${rs.ref}, lock=${m.requested_ref})`);
24714
+ break;
24715
+ }
24716
+ }
24717
+ }
24718
+ if (drifted.length > 0) {
24719
+ throw new Error(
24720
+ `Frozen install failed: manifest ref does not match rulesync-gh.lock.yaml for: ${drifted.join(", ")}. Run 'rulesync install --mode gh' to update the lockfile.`
24721
+ );
24722
+ }
24723
+ }
24724
+ const token = GitHubClient.resolveToken(options.token);
24725
+ const client = new GitHubClient({ token });
24726
+ const semaphore = new import_promise3.Semaphore(FETCH_CONCURRENCY_LIMIT);
24727
+ const newLock = createEmptyGhLock({ existingLock });
24728
+ const runOne = async (rs) => {
24729
+ const installations = await installSource({
24730
+ rs,
24731
+ client,
24732
+ semaphore,
24733
+ baseDir,
24734
+ existingLock,
24735
+ frozen,
24736
+ update,
24737
+ logger: logger5
24738
+ });
24739
+ return { status: "ok", installations };
24740
+ };
24741
+ const results = frozen ? await Promise.all(resolvedSources.map(runOne)) : await Promise.all(
24742
+ resolvedSources.map(async (rs) => {
24743
+ try {
24744
+ return await runOne(rs);
24745
+ } catch (error) {
24746
+ logger5.error(`Failed to install gh source "${rs.entry.source}": ${formatError(error)}`);
24747
+ if (error instanceof GitHubClientError) {
24748
+ logGitHubAuthHints({ error, logger: logger5 });
24749
+ }
24750
+ const preserved = existingLock ? existingLock.installations.filter(
24751
+ (i) => i.source.toLowerCase() === rs.entry.source.toLowerCase()
24752
+ ) : [];
24753
+ return { status: "failed", preserved };
24754
+ }
24755
+ })
24756
+ );
24757
+ if (frozen) {
24758
+ for (const result of results) {
24759
+ if (result.status !== "ok") continue;
24760
+ for (const inst of result.installations) {
24761
+ for (const d of inst.deployed) {
24762
+ await writeFileContent(d.absolutePath, d.content);
24763
+ }
24764
+ }
24765
+ }
24766
+ }
24767
+ let totalInstalled = 0;
24768
+ let failedCount = 0;
24769
+ for (const result of results) {
24770
+ if (result.status === "ok") {
24771
+ for (const inst of result.installations) {
24772
+ newLock.installations.push(inst.installation);
24773
+ }
24774
+ totalInstalled += result.installations.length;
24775
+ } else {
24776
+ failedCount += 1;
24777
+ for (const preserved of result.preserved) {
24778
+ newLock.installations.push(preserved);
24779
+ }
24780
+ }
24781
+ }
24782
+ if (existingLock) {
24783
+ const newDeployed = /* @__PURE__ */ new Set();
24784
+ for (const inst of newLock.installations) {
24785
+ for (const file of inst.deployed_files) {
24786
+ newDeployed.add(`${inst.scope}::${file}`);
24787
+ }
24788
+ }
24789
+ for (const prev of existingLock.installations) {
24790
+ for (const deployed of prev.deployed_files) {
24791
+ const key = `${prev.scope}::${deployed}`;
24792
+ if (newDeployed.has(key)) continue;
24793
+ await removeStaleFile({
24794
+ relativePath: deployed,
24795
+ scope: prev.scope === "user" ? "user" : "project",
24796
+ baseDir,
24797
+ logger: logger5
24798
+ });
24799
+ }
24800
+ }
24801
+ }
24802
+ if (!frozen) {
24803
+ newLock.generated_at = (/* @__PURE__ */ new Date()).toISOString();
24804
+ await writeGhLock({ baseDir, lock: newLock });
24805
+ if (failedCount === 0) {
24806
+ logger5.debug("rulesync-gh.lock.yaml updated.");
24807
+ } else {
24808
+ logger5.warn(
24809
+ `rulesync-gh.lock.yaml written with partially successful installs (${failedCount} source(s) failed).`
24810
+ );
24811
+ }
24812
+ }
24813
+ return {
24814
+ sourcesProcessed: sources.length,
24815
+ installedSkillCount: totalInstalled,
24816
+ failedSourceCount: failedCount
24817
+ };
24818
+ }
24819
+ async function installSource(params) {
24820
+ const { rs, client, semaphore, baseDir, existingLock, frozen, update, logger: logger5 } = params;
24821
+ const { entry, owner, repo, agent, scope } = rs;
24822
+ const sourceKey = entry.source;
24823
+ let resolvedRef;
24824
+ let usedTag = false;
24825
+ if (rs.ref) {
24826
+ resolvedRef = rs.ref;
24827
+ } else {
24828
+ try {
24829
+ const release = await client.getLatestRelease(owner, repo);
24830
+ resolvedRef = release.tag_name;
24831
+ usedTag = true;
24832
+ } catch (error) {
24833
+ if (is404(error)) {
24834
+ resolvedRef = await client.getDefaultBranch(owner, repo);
24835
+ } else {
24836
+ throw error;
24837
+ }
24838
+ }
24839
+ }
24840
+ const resolvedSha = await client.resolveRefToSha(owner, repo, resolvedRef);
24841
+ logger5.debug(`Resolved ${sourceKey} -> ref=${resolvedRef} sha=${resolvedSha}`);
24842
+ let topLevel;
24843
+ try {
24844
+ topLevel = await client.listDirectory(owner, repo, SKILLS_REMOTE_DIR, resolvedSha);
24845
+ } catch (error) {
24846
+ if (is404(error)) {
24847
+ logger5.warn(`No skills/ directory found in ${sourceKey}. Skipping.`);
24848
+ return [];
24849
+ }
24850
+ throw error;
24851
+ }
24852
+ const skillDirs = topLevel.filter((e) => e.type === "dir").map((e) => ({ name: e.name, path: e.path }));
24853
+ const validatedSkills = [];
24854
+ for (const sk of skillDirs) {
24855
+ const info = await withSemaphore(
24856
+ semaphore,
24857
+ () => client.getFileInfo(owner, repo, import_node_path150.posix.join(sk.path, SKILL_FILE_NAME2), resolvedSha)
24858
+ );
24859
+ if (info) {
24860
+ validatedSkills.push(sk);
24861
+ }
24862
+ }
24863
+ let selected = validatedSkills;
24864
+ if (entry.skills && entry.skills.length > 0) {
24865
+ const requested = new Set(entry.skills);
24866
+ selected = validatedSkills.filter((s) => requested.has(s.name));
24867
+ const presentNames = new Set(validatedSkills.map((s) => s.name));
24868
+ for (const want of entry.skills) {
24869
+ if (!presentNames.has(want)) {
24870
+ logger5.warn(`Requested skill "${want}" not found in ${sourceKey} under skills/. Skipping.`);
24871
+ }
24872
+ }
24873
+ }
24874
+ if (frozen && existingLock) {
24875
+ const missing = [];
24876
+ for (const sk of selected) {
24877
+ const locked = findGhLockInstallation(existingLock, {
24878
+ source: sourceKey,
24879
+ agent,
24880
+ scope,
24881
+ skill: sk.name
24882
+ });
24883
+ if (!locked) {
24884
+ missing.push(sk.name);
24885
+ }
24886
+ }
24887
+ if (missing.length > 0) {
24888
+ throw new Error(
24889
+ `Frozen install failed: rulesync-gh.lock.yaml is missing entries for ${sourceKey} (agent=${agent}, scope=${scope}) skills: ${missing.join(", ")}. Run 'rulesync install --mode gh' to update the lockfile.`
24890
+ );
24891
+ }
24892
+ }
24893
+ const results = [];
24894
+ const installRelDir = relativeInstallDirFor({ agent, scope });
24895
+ const scopeRoot = scope === "user" ? getHomeDirectory() : baseDir;
24896
+ const sourceUrl = `https://github.com/${owner}/${repo}`;
24897
+ const repository = `${owner}/${repo}`;
24898
+ const provenanceRef = usedTag ? resolvedRef : resolvedSha;
24899
+ for (const sk of selected) {
24900
+ const locked = existingLock && !update ? findGhLockInstallation(existingLock, {
24901
+ source: sourceKey,
24902
+ agent,
24903
+ scope,
24904
+ skill: sk.name
24905
+ }) : void 0;
24906
+ const allFiles = await listDirectoryRecursive({
24907
+ client,
24908
+ owner,
24909
+ repo,
24910
+ path: sk.path,
24911
+ ref: resolvedSha,
24912
+ semaphore
24913
+ });
24914
+ const deployed = [];
24915
+ for (const file of allFiles) {
24916
+ if (file.size > MAX_FILE_SIZE) {
24917
+ logger5.warn(
24918
+ `Skipping "${file.path}" from ${sourceKey}: ${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`
24919
+ );
24920
+ continue;
24921
+ }
24922
+ const relativeToSkill = import_node_path150.posix.relative(sk.path, toPosixPath(file.path));
24923
+ if (!relativeToSkill || relativeToSkill.startsWith("..") || import_node_path150.posix.isAbsolute(relativeToSkill)) {
24924
+ logger5.warn(`Skipping "${file.path}" from ${sourceKey}: resolved outside of "${sk.path}".`);
24925
+ continue;
24926
+ }
24927
+ const deployRelative = toPosixPath((0, import_node_path150.join)(installRelDir, sk.name, relativeToSkill));
24928
+ checkPathTraversal({ relativePath: deployRelative, intendedRootDir: scopeRoot });
24929
+ const installAbs = (0, import_node_path150.join)(scopeRoot, installRelDir);
24930
+ const withinInstallDir = toPosixPath((0, import_node_path150.join)(sk.name, relativeToSkill));
24931
+ checkPathTraversal({ relativePath: withinInstallDir, intendedRootDir: installAbs });
24932
+ let content = await withSemaphore(
24933
+ semaphore,
24934
+ () => client.getFileContent(owner, repo, file.path, resolvedSha)
24935
+ );
24936
+ const byteLength = Buffer.byteLength(content, "utf8");
24937
+ if (byteLength > MAX_FILE_SIZE) {
24938
+ logger5.warn(
24939
+ `Skipping "${file.path}" from ${sourceKey}: fetched ${(byteLength / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit.`
24940
+ );
24941
+ continue;
24942
+ }
24943
+ if ((0, import_node_path150.basename)(file.path) === SKILL_FILE_NAME2) {
24944
+ try {
24945
+ content = injectSourceMetadata({
24946
+ content,
24947
+ source: sourceUrl,
24948
+ repository,
24949
+ ref: provenanceRef
24950
+ });
24951
+ } catch {
24952
+ logger5.warn(
24953
+ `Frontmatter in ${file.path} (${sourceKey}) is invalid. Prepending a fresh provenance block.`
24954
+ );
24955
+ content = `---
24956
+ source: ${sourceUrl}
24957
+ repository: ${repository}
24958
+ ref: ${provenanceRef}
24959
+ ---
24960
+ ${content}`;
24961
+ }
24962
+ }
24963
+ const absolutePath = (0, import_node_path150.join)(scopeRoot, deployRelative);
24964
+ deployed.push({ relativeToScopeRoot: deployRelative, absolutePath, content });
24965
+ if (!frozen) {
24966
+ await writeFileContent(absolutePath, content);
24967
+ }
24968
+ }
24969
+ deployed.sort(
24970
+ (a, b) => a.relativeToScopeRoot < b.relativeToScopeRoot ? -1 : a.relativeToScopeRoot > b.relativeToScopeRoot ? 1 : 0
24971
+ );
24972
+ const deployedFiles = deployed.map((d) => d.relativeToScopeRoot);
24973
+ const contentHash = computeContentHash2(deployed);
24974
+ if (frozen && locked?.content_hash) {
24975
+ if (RULESYNC_CONTENT_HASH_REGEX2.test(locked.content_hash)) {
24976
+ if (locked.content_hash !== contentHash) {
24977
+ throw new Error(
24978
+ `content_hash mismatch for ${sourceKey} skill "${sk.name}" (agent=${agent}, scope=${scope}): lock=${locked.content_hash} computed=${contentHash}. Refuse to trust the deployment under --frozen.`
24979
+ );
24980
+ }
24981
+ } else {
24982
+ logger5.debug(
24983
+ `Skipping content_hash integrity check for ${sourceKey} skill "${sk.name}": recorded hash "${locked.content_hash}" was not written by rulesync.`
24984
+ );
24985
+ }
24986
+ }
24987
+ const installation = {
24988
+ source: sourceKey,
24989
+ owner,
24990
+ repo,
24991
+ agent,
24992
+ scope,
24993
+ skill: sk.name,
24994
+ resolved_ref: resolvedRef,
24995
+ resolved_commit: resolvedSha,
24996
+ install_dir: toPosixPath(installRelDir),
24997
+ deployed_files: deployedFiles,
24998
+ content_hash: contentHash
24999
+ };
25000
+ if (rs.ref !== void 0) {
25001
+ installation.requested_ref = rs.ref;
25002
+ }
25003
+ results.push({ installation, deployed });
25004
+ logger5.info(
25005
+ `Installed gh skill "${sk.name}" from ${sourceKey} (agent=${agent}, scope=${scope}, ref=${resolvedRef})`
25006
+ );
25007
+ }
25008
+ return results;
25009
+ }
25010
+ async function removeStaleFile(params) {
25011
+ const { relativePath, scope, baseDir, logger: logger5 } = params;
25012
+ if (import_node_path150.posix.isAbsolute(relativePath) || relativePath.split(/[/\\]/).includes("..")) {
25013
+ logger5.warn(`Refusing to remove stale gh file with suspicious path: "${relativePath}".`);
25014
+ return;
25015
+ }
25016
+ const scopeRoot = scope === "user" ? getHomeDirectory() : baseDir;
25017
+ try {
25018
+ checkPathTraversal({ relativePath, intendedRootDir: scopeRoot });
25019
+ } catch {
25020
+ logger5.warn(`Refusing to remove stale gh file outside ${scope} root: "${relativePath}".`);
25021
+ return;
25022
+ }
25023
+ const absolute = (0, import_node_path150.join)(scopeRoot, relativePath);
25024
+ await removeFile(absolute);
25025
+ logger5.debug(`Removed stale gh file: ${relativePath}`);
25026
+ }
25027
+ function is404(error) {
25028
+ if (error instanceof GitHubClientError && error.statusCode === 404) {
25029
+ return true;
25030
+ }
25031
+ if (typeof error === "object" && error !== null && "statusCode" in error && error.statusCode === 404) {
25032
+ return true;
25033
+ }
25034
+ return false;
25035
+ }
25036
+ function computeContentHash2(files) {
25037
+ const hash = (0, import_node_crypto2.createHash)("sha256");
25038
+ for (const { relativeToScopeRoot, content } of files) {
25039
+ hash.update(relativeToScopeRoot);
25040
+ hash.update("\0");
25041
+ hash.update(content);
25042
+ hash.update("\0");
25043
+ }
25044
+ return `sha256:${hash.digest("hex")}`;
25045
+ }
25046
+
25047
+ // src/lib/sources.ts
25048
+ var import_node_path153 = require("path");
25049
+ var import_promise4 = require("es-toolkit/promise");
25050
+
25051
+ // src/lib/git-client.ts
25052
+ var import_node_child_process = require("child_process");
25053
+ var import_node_path151 = require("path");
25054
+ var import_node_util2 = require("util");
25055
+ var execFileAsync = (0, import_node_util2.promisify)(import_node_child_process.execFile);
25056
+ var GIT_TIMEOUT_MS = 6e4;
25057
+ var ALLOWED_URL_SCHEMES = /^(https?:\/\/|ssh:\/\/|git:\/\/|file:\/\/\/).+$|^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9.-]+:[a-zA-Z0-9_.+/~-]+$/;
25058
+ var INSECURE_URL_SCHEMES = /^(git:\/\/|http:\/\/)/;
25059
+ var GitClientError = class extends Error {
25060
+ constructor(message, cause) {
25061
+ super(message, { cause });
25062
+ this.name = "GitClientError";
25063
+ }
25064
+ };
25065
+ function validateGitUrl(url, options) {
25066
+ const ctrl = findControlCharacter(url);
25067
+ if (ctrl) {
25068
+ throw new GitClientError(
25069
+ `Git URL contains control character ${ctrl.hex} at position ${ctrl.position}`
25070
+ );
25071
+ }
25072
+ if (!ALLOWED_URL_SCHEMES.test(url)) {
25073
+ throw new GitClientError(
25074
+ `Unsupported or unsafe git URL: "${url}". Use https, ssh, git, or file schemes.`
25075
+ );
25076
+ }
25077
+ if (INSECURE_URL_SCHEMES.test(url)) {
25078
+ options?.logger?.warn(
25079
+ `URL "${url}" uses an unencrypted protocol. Consider using https:// or ssh:// instead.`
25080
+ );
25081
+ }
25082
+ }
25083
+ function validateRef(ref) {
25084
+ if (ref.startsWith("-")) {
25085
+ throw new GitClientError(`Ref must not start with "-": "${ref}"`);
25086
+ }
25087
+ const ctrl = findControlCharacter(ref);
25088
+ if (ctrl) {
25089
+ throw new GitClientError(
25090
+ `Ref contains control character ${ctrl.hex} at position ${ctrl.position}`
25091
+ );
25092
+ }
25093
+ }
25094
+ var gitChecked = false;
25095
+ async function checkGitAvailable() {
25096
+ if (gitChecked) return;
25097
+ try {
25098
+ await execFileAsync("git", ["--version"], { timeout: GIT_TIMEOUT_MS });
25099
+ gitChecked = true;
25100
+ } catch {
25101
+ throw new GitClientError("git is not installed or not found in PATH");
25102
+ }
25103
+ }
25104
+ async function resolveDefaultRef(url) {
25105
+ validateGitUrl(url);
25106
+ await checkGitAvailable();
25107
+ try {
25108
+ const { stdout } = await execFileAsync("git", ["ls-remote", "--symref", "--", url, "HEAD"], {
25109
+ timeout: GIT_TIMEOUT_MS
25110
+ });
25111
+ const ref = stdout.match(/^ref: refs\/heads\/(.+)\tHEAD$/m)?.[1];
25112
+ const sha = stdout.match(/^([0-9a-f]{40})\tHEAD$/m)?.[1];
25113
+ if (!ref || !sha) throw new GitClientError(`Could not parse default branch from: ${url}`);
25114
+ validateRef(ref);
25115
+ return { ref, sha };
25116
+ } catch (error) {
25117
+ if (error instanceof GitClientError) throw error;
25118
+ throw new GitClientError(`Failed to resolve default ref for ${url}`, error);
25119
+ }
25120
+ }
25121
+ async function resolveRefToSha(url, ref) {
25122
+ validateGitUrl(url);
25123
+ validateRef(ref);
25124
+ await checkGitAvailable();
25125
+ try {
25126
+ const { stdout } = await execFileAsync("git", ["ls-remote", "--", url, ref], {
25127
+ timeout: GIT_TIMEOUT_MS
25128
+ });
25129
+ const sha = stdout.match(/^([0-9a-f]{40})\t/m)?.[1];
25130
+ if (!sha) throw new GitClientError(`Ref "${ref}" not found in ${url}`);
25131
+ return sha;
25132
+ } catch (error) {
25133
+ if (error instanceof GitClientError) throw error;
25134
+ throw new GitClientError(`Failed to resolve ref "${ref}" for ${url}`, error);
25135
+ }
25136
+ }
25137
+ async function fetchSkillFiles(params) {
25138
+ const { url, ref, skillsPath, logger: logger5 } = params;
25139
+ validateGitUrl(url, { logger: logger5 });
25140
+ validateRef(ref);
25141
+ if (skillsPath.split(/[/\\]/).includes("..") || (0, import_node_path151.isAbsolute)(skillsPath)) {
25142
+ throw new GitClientError(
25143
+ `Invalid skillsPath "${skillsPath}": must be a relative path without ".."`
25144
+ );
25145
+ }
25146
+ const ctrl = findControlCharacter(skillsPath);
25147
+ if (ctrl) {
25148
+ throw new GitClientError(
25149
+ `skillsPath contains control character ${ctrl.hex} at position ${ctrl.position}`
25150
+ );
25151
+ }
25152
+ await checkGitAvailable();
25153
+ const tmpDir = await createTempDirectory("rulesync-git-");
25154
+ try {
25155
+ await execFileAsync(
25156
+ "git",
25157
+ [
25158
+ "clone",
25159
+ "--depth",
25160
+ "1",
25161
+ "--branch",
25162
+ ref,
25163
+ "--no-checkout",
25164
+ "--filter=blob:none",
25165
+ "--",
25166
+ url,
25167
+ tmpDir
25168
+ ],
25169
+ { timeout: GIT_TIMEOUT_MS }
25170
+ );
25171
+ await execFileAsync("git", ["-C", tmpDir, "sparse-checkout", "set", "--", skillsPath], {
25172
+ timeout: GIT_TIMEOUT_MS
25173
+ });
25174
+ await execFileAsync("git", ["-C", tmpDir, "checkout"], { timeout: GIT_TIMEOUT_MS });
25175
+ const skillsDir = (0, import_node_path151.join)(tmpDir, skillsPath);
25176
+ if (!await directoryExists(skillsDir)) return [];
25177
+ return await walkDirectory(skillsDir, skillsDir, 0, { totalFiles: 0, totalSize: 0 }, logger5);
25178
+ } catch (error) {
25179
+ if (error instanceof GitClientError) throw error;
25180
+ throw new GitClientError(`Failed to fetch skill files from ${url}`, error);
25181
+ } finally {
25182
+ await removeTempDirectory(tmpDir);
25183
+ }
25184
+ }
25185
+ var MAX_WALK_DEPTH = 20;
25186
+ var MAX_TOTAL_FILES = 1e4;
25187
+ var MAX_TOTAL_SIZE = 100 * 1024 * 1024;
25188
+ async function walkDirectory(dir, baseDir, depth = 0, ctx = { totalFiles: 0, totalSize: 0 }, logger5) {
25189
+ if (depth > MAX_WALK_DEPTH) {
25190
+ throw new GitClientError(
25191
+ `Directory tree exceeds max depth of ${MAX_WALK_DEPTH}: "${dir}". Aborting to prevent resource exhaustion.`
24041
25192
  );
24042
25193
  }
24043
25194
  const results = [];
24044
25195
  for (const name of await listDirectoryFiles(dir)) {
24045
25196
  if (name === ".git") continue;
24046
- const fullPath = (0, import_node_path145.join)(dir, name);
25197
+ const fullPath = (0, import_node_path151.join)(dir, name);
24047
25198
  if (await isSymlink(fullPath)) {
24048
25199
  logger5?.warn(`Skipping symlink "${fullPath}".`);
24049
25200
  continue;
@@ -24071,36 +25222,36 @@ async function walkDirectory(dir, baseDir, depth = 0, ctx = { totalFiles: 0, tot
24071
25222
  );
24072
25223
  }
24073
25224
  const content = await readFileContent(fullPath);
24074
- results.push({ relativePath: (0, import_node_path145.relative)(baseDir, fullPath), content, size });
25225
+ results.push({ relativePath: (0, import_node_path151.relative)(baseDir, fullPath), content, size });
24075
25226
  }
24076
25227
  }
24077
25228
  return results;
24078
25229
  }
24079
25230
 
24080
25231
  // src/lib/sources-lock.ts
24081
- var import_node_crypto = require("crypto");
24082
- var import_node_path146 = require("path");
24083
- var import_mini76 = require("zod/mini");
25232
+ var import_node_crypto3 = require("crypto");
25233
+ var import_node_path152 = require("path");
25234
+ var import_mini78 = require("zod/mini");
24084
25235
  var LOCKFILE_VERSION = 1;
24085
- var LockedSkillSchema = import_mini76.z.object({
24086
- integrity: import_mini76.z.string()
25236
+ var LockedSkillSchema = import_mini78.z.object({
25237
+ integrity: import_mini78.z.string()
24087
25238
  });
24088
- var LockedSourceSchema = import_mini76.z.object({
24089
- requestedRef: (0, import_mini76.optional)(import_mini76.z.string()),
24090
- resolvedRef: import_mini76.z.string().check((0, import_mini76.refine)((v) => /^[0-9a-f]{40}$/.test(v), "resolvedRef must be a 40-character hex SHA")),
24091
- resolvedAt: (0, import_mini76.optional)(import_mini76.z.string()),
24092
- skills: import_mini76.z.record(import_mini76.z.string(), LockedSkillSchema)
25239
+ var LockedSourceSchema = import_mini78.z.object({
25240
+ requestedRef: (0, import_mini78.optional)(import_mini78.z.string()),
25241
+ resolvedRef: import_mini78.z.string().check((0, import_mini78.refine)((v) => /^[0-9a-f]{40}$/.test(v), "resolvedRef must be a 40-character hex SHA")),
25242
+ resolvedAt: (0, import_mini78.optional)(import_mini78.z.string()),
25243
+ skills: import_mini78.z.record(import_mini78.z.string(), LockedSkillSchema)
24093
25244
  });
24094
- var SourcesLockSchema = import_mini76.z.object({
24095
- lockfileVersion: import_mini76.z.number(),
24096
- sources: import_mini76.z.record(import_mini76.z.string(), LockedSourceSchema)
25245
+ var SourcesLockSchema = import_mini78.z.object({
25246
+ lockfileVersion: import_mini78.z.number(),
25247
+ sources: import_mini78.z.record(import_mini78.z.string(), LockedSourceSchema)
24097
25248
  });
24098
- var LegacyLockedSourceSchema = import_mini76.z.object({
24099
- resolvedRef: import_mini76.z.string(),
24100
- skills: import_mini76.z.array(import_mini76.z.string())
25249
+ var LegacyLockedSourceSchema = import_mini78.z.object({
25250
+ resolvedRef: import_mini78.z.string(),
25251
+ skills: import_mini78.z.array(import_mini78.z.string())
24101
25252
  });
24102
- var LegacySourcesLockSchema = import_mini76.z.object({
24103
- sources: import_mini76.z.record(import_mini76.z.string(), LegacyLockedSourceSchema)
25253
+ var LegacySourcesLockSchema = import_mini78.z.object({
25254
+ sources: import_mini78.z.record(import_mini78.z.string(), LegacyLockedSourceSchema)
24104
25255
  });
24105
25256
  function migrateLegacyLock(params) {
24106
25257
  const { legacy, logger: logger5 } = params;
@@ -24125,7 +25276,7 @@ function createEmptyLock() {
24125
25276
  }
24126
25277
  async function readLockFile(params) {
24127
25278
  const { logger: logger5 } = params;
24128
- const lockPath = (0, import_node_path146.join)(params.baseDir, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);
25279
+ const lockPath = (0, import_node_path152.join)(params.baseDir, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);
24129
25280
  if (!await fileExists(lockPath)) {
24130
25281
  logger5.debug("No sources lockfile found, starting fresh.");
24131
25282
  return createEmptyLock();
@@ -24154,13 +25305,13 @@ async function readLockFile(params) {
24154
25305
  }
24155
25306
  async function writeLockFile(params) {
24156
25307
  const { logger: logger5 } = params;
24157
- const lockPath = (0, import_node_path146.join)(params.baseDir, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);
25308
+ const lockPath = (0, import_node_path152.join)(params.baseDir, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);
24158
25309
  const content = JSON.stringify(params.lock, null, 2) + "\n";
24159
25310
  await writeFileContent(lockPath, content);
24160
25311
  logger5.debug(`Wrote sources lockfile to ${lockPath}`);
24161
25312
  }
24162
25313
  function computeSkillIntegrity(files) {
24163
- const hash = (0, import_node_crypto.createHash)("sha256");
25314
+ const hash = (0, import_node_crypto3.createHash)("sha256");
24164
25315
  const sorted = files.toSorted((a, b) => a.path.localeCompare(b.path));
24165
25316
  for (const file of sorted) {
24166
25317
  hash.update(file.path);
@@ -24328,7 +25479,7 @@ function logGitClientHints(params) {
24328
25479
  async function checkLockedSkillsExist(curatedDir, skillNames) {
24329
25480
  if (skillNames.length === 0) return true;
24330
25481
  for (const name of skillNames) {
24331
- if (!await directoryExists((0, import_node_path147.join)(curatedDir, name))) {
25482
+ if (!await directoryExists((0, import_node_path153.join)(curatedDir, name))) {
24332
25483
  return false;
24333
25484
  }
24334
25485
  }
@@ -24336,10 +25487,10 @@ async function checkLockedSkillsExist(curatedDir, skillNames) {
24336
25487
  }
24337
25488
  async function cleanPreviousCuratedSkills(params) {
24338
25489
  const { curatedDir, lockedSkillNames, logger: logger5 } = params;
24339
- const resolvedCuratedDir = (0, import_node_path147.resolve)(curatedDir);
25490
+ const resolvedCuratedDir = (0, import_node_path153.resolve)(curatedDir);
24340
25491
  for (const prevSkill of lockedSkillNames) {
24341
- const prevDir = (0, import_node_path147.join)(curatedDir, prevSkill);
24342
- if (!(0, import_node_path147.resolve)(prevDir).startsWith(resolvedCuratedDir + import_node_path147.sep)) {
25492
+ const prevDir = (0, import_node_path153.join)(curatedDir, prevSkill);
25493
+ if (!(0, import_node_path153.resolve)(prevDir).startsWith(resolvedCuratedDir + import_node_path153.sep)) {
24343
25494
  logger5.warn(
24344
25495
  `Skipping removal of "${prevSkill}": resolved path is outside the curated directory.`
24345
25496
  );
@@ -24378,9 +25529,9 @@ async function writeSkillAndComputeIntegrity(params) {
24378
25529
  for (const file of files) {
24379
25530
  checkPathTraversal({
24380
25531
  relativePath: file.relativePath,
24381
- intendedRootDir: (0, import_node_path147.join)(curatedDir, skillName)
25532
+ intendedRootDir: (0, import_node_path153.join)(curatedDir, skillName)
24382
25533
  });
24383
- await writeFileContent((0, import_node_path147.join)(curatedDir, skillName, file.relativePath), file.content);
25534
+ await writeFileContent((0, import_node_path153.join)(curatedDir, skillName, file.relativePath), file.content);
24384
25535
  written.push({ path: file.relativePath, content: file.content });
24385
25536
  }
24386
25537
  const integrity = computeSkillIntegrity(written);
@@ -24424,6 +25575,40 @@ function buildLockUpdate(params) {
24424
25575
  );
24425
25576
  return { updatedLock, fetchedNames };
24426
25577
  }
25578
+ function getFirstPathSeparatorIndex(path4) {
25579
+ const slashIndex = path4.indexOf("/");
25580
+ const backslashIndex = path4.indexOf("\\");
25581
+ if (slashIndex === -1) return backslashIndex;
25582
+ if (backslashIndex === -1) return slashIndex;
25583
+ return Math.min(slashIndex, backslashIndex);
25584
+ }
25585
+ function groupRemoteFilesBySkillRoot(params) {
25586
+ const { remoteFiles, skillFilter, isWildcard } = params;
25587
+ const grouped = /* @__PURE__ */ new Map();
25588
+ const rootLevelFiles = [];
25589
+ for (const file of remoteFiles) {
25590
+ const separatorIndex = getFirstPathSeparatorIndex(file.relativePath);
25591
+ if (separatorIndex === -1) {
25592
+ rootLevelFiles.push(file);
25593
+ continue;
25594
+ }
25595
+ const skillName = file.relativePath.substring(0, separatorIndex);
25596
+ if (skillName.length === 0) {
25597
+ continue;
25598
+ }
25599
+ const innerPath = file.relativePath.substring(separatorIndex + 1);
25600
+ const groupedFiles = grouped.get(skillName) ?? [];
25601
+ groupedFiles.push({ relativePath: innerPath, content: file.content });
25602
+ grouped.set(skillName, groupedFiles);
25603
+ }
25604
+ if (grouped.size === 0 && !isWildcard && skillFilter.length === 1) {
25605
+ const [singleSkillName] = skillFilter;
25606
+ if (singleSkillName !== void 0 && rootLevelFiles.length > 0) {
25607
+ grouped.set(singleSkillName, rootLevelFiles);
25608
+ }
25609
+ }
25610
+ return grouped;
25611
+ }
24427
25612
  async function fetchSource(params) {
24428
25613
  const {
24429
25614
  sourceEntry,
@@ -24457,7 +25642,7 @@ async function fetchSource(params) {
24457
25642
  ref = resolvedSha;
24458
25643
  logger5.debug(`Resolved ${sourceKey} ref "${requestedRef}" to SHA: ${resolvedSha}`);
24459
25644
  }
24460
- const curatedDir = (0, import_node_path147.join)(baseDir, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);
25645
+ const curatedDir = (0, import_node_path153.join)(baseDir, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);
24461
25646
  if (locked && resolvedSha === locked.resolvedRef && !updateSources) {
24462
25647
  const allExist = await checkLockedSkillsExist(curatedDir, lockedSkillNames);
24463
25648
  if (allExist) {
@@ -24471,11 +25656,60 @@ async function fetchSource(params) {
24471
25656
  }
24472
25657
  const skillFilter = sourceEntry.skills ?? ["*"];
24473
25658
  const isWildcard = skillFilter.length === 1 && skillFilter[0] === "*";
25659
+ const semaphore = new import_promise4.Semaphore(FETCH_CONCURRENCY_LIMIT);
25660
+ const fetchedSkills = {};
24474
25661
  const skillsBasePath = parsed.path ?? "skills";
24475
25662
  let remoteSkillDirs;
25663
+ let remoteSkillNames = [];
25664
+ let fallbackHandled = false;
24476
25665
  try {
24477
25666
  const entries = await client.listDirectory(parsed.owner, parsed.repo, skillsBasePath, ref);
24478
25667
  remoteSkillDirs = entries.filter((e) => e.type === "dir").map((e) => ({ name: e.name, path: e.path }));
25668
+ if (remoteSkillDirs.length === 0 && !isWildcard && skillFilter.length === 1) {
25669
+ const rootFiles = entries.filter((entry) => entry.type === "file");
25670
+ const rootSkillFiles = [];
25671
+ for (const file of rootFiles) {
25672
+ if (file.size > MAX_FILE_SIZE) {
25673
+ logger5.warn(
25674
+ `Skipping file "${file.path}" (${(file.size / 1024 / 1024).toFixed(2)}MB exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit).`
25675
+ );
25676
+ continue;
25677
+ }
25678
+ const content = await withSemaphore(
25679
+ semaphore,
25680
+ () => client.getFileContent(parsed.owner, parsed.repo, file.path, ref)
25681
+ );
25682
+ rootSkillFiles.push({ relativePath: file.name, content });
25683
+ }
25684
+ const groupedRootFiles = groupRemoteFilesBySkillRoot({
25685
+ remoteFiles: rootSkillFiles,
25686
+ skillFilter,
25687
+ isWildcard
25688
+ });
25689
+ const [fallbackSkillName] = groupedRootFiles.keys();
25690
+ if (fallbackSkillName !== void 0) {
25691
+ fallbackHandled = true;
25692
+ remoteSkillNames = [fallbackSkillName];
25693
+ if (!shouldSkipSkill({
25694
+ skillName: fallbackSkillName,
25695
+ sourceKey,
25696
+ localSkillNames,
25697
+ alreadyFetchedSkillNames,
25698
+ logger: logger5
25699
+ })) {
25700
+ fetchedSkills[fallbackSkillName] = await writeSkillAndComputeIntegrity({
25701
+ skillName: fallbackSkillName,
25702
+ files: groupedRootFiles.get(fallbackSkillName) ?? [],
25703
+ curatedDir,
25704
+ locked,
25705
+ resolvedSha,
25706
+ sourceKey,
25707
+ logger: logger5
25708
+ });
25709
+ logger5.debug(`Fetched skill "${fallbackSkillName}" from ${sourceKey}`);
25710
+ }
25711
+ }
25712
+ }
24479
25713
  } catch (error) {
24480
25714
  if (error instanceof GitHubClientError && error.statusCode === 404) {
24481
25715
  logger5.warn(`No skills/ directory found in ${sourceKey}. Skipping.`);
@@ -24484,8 +25718,9 @@ async function fetchSource(params) {
24484
25718
  throw error;
24485
25719
  }
24486
25720
  const filteredDirs = isWildcard ? remoteSkillDirs : remoteSkillDirs.filter((d) => skillFilter.includes(d.name));
24487
- const semaphore = new import_promise2.Semaphore(FETCH_CONCURRENCY_LIMIT);
24488
- const fetchedSkills = {};
25721
+ if (!fallbackHandled) {
25722
+ remoteSkillNames = filteredDirs.map((d) => d.name);
25723
+ }
24489
25724
  if (locked) {
24490
25725
  await cleanPreviousCuratedSkills({ curatedDir, lockedSkillNames, logger: logger5 });
24491
25726
  }
@@ -24543,7 +25778,7 @@ async function fetchSource(params) {
24543
25778
  locked,
24544
25779
  requestedRef,
24545
25780
  resolvedSha,
24546
- remoteSkillNames: filteredDirs.map((d) => d.name),
25781
+ remoteSkillNames,
24547
25782
  logger: logger5
24548
25783
  });
24549
25784
  return {
@@ -24582,7 +25817,7 @@ async function fetchSourceViaGit(params) {
24582
25817
  requestedRef = def.ref;
24583
25818
  resolvedSha = def.sha;
24584
25819
  }
24585
- const curatedDir = (0, import_node_path147.join)(baseDir, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);
25820
+ const curatedDir = (0, import_node_path153.join)(baseDir, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);
24586
25821
  if (locked && resolvedSha === locked.resolvedRef && !updateSources) {
24587
25822
  if (await checkLockedSkillsExist(curatedDir, lockedSkillNames)) {
24588
25823
  return { skillCount: 0, fetchedSkillNames: lockedSkillNames, updatedLock: lock };
@@ -24605,28 +25840,7 @@ async function fetchSourceViaGit(params) {
24605
25840
  ref: requestedRef,
24606
25841
  skillsPath: sourceEntry.path ?? "skills"
24607
25842
  });
24608
- const skillFileMap = /* @__PURE__ */ new Map();
24609
- for (const file of remoteFiles) {
24610
- const idx = file.relativePath.indexOf("/");
24611
- if (idx === -1) continue;
24612
- const name = file.relativePath.substring(0, idx);
24613
- const inner = file.relativePath.substring(idx + 1);
24614
- const arr = skillFileMap.get(name) ?? [];
24615
- arr.push({ relativePath: inner, content: file.content });
24616
- skillFileMap.set(name, arr);
24617
- }
24618
- if (skillFileMap.size === 0 && !isWildcard && skillFilter.length === 1) {
24619
- const [singleSkillName] = skillFilter;
24620
- if (singleSkillName !== void 0) {
24621
- const rootFiles = remoteFiles.filter((f) => f.relativePath.indexOf("/") === -1);
24622
- if (rootFiles.length > 0) {
24623
- skillFileMap.set(
24624
- singleSkillName,
24625
- rootFiles.map((f) => ({ relativePath: f.relativePath, content: f.content }))
24626
- );
24627
- }
24628
- }
24629
- }
25843
+ const skillFileMap = groupRemoteFilesBySkillRoot({ remoteFiles, skillFilter, isWildcard });
24630
25844
  const allNames = [...skillFileMap.keys()];
24631
25845
  const filteredNames = isWildcard ? allNames : allNames.filter((n) => skillFilter.includes(n));
24632
25846
  if (locked) {
@@ -24671,21 +25885,47 @@ async function fetchSourceViaGit(params) {
24671
25885
  }
24672
25886
 
24673
25887
  // src/cli/commands/install.ts
25888
+ var INSTALL_MODES = ["rulesync", "apm", "gh"];
24674
25889
  async function installCommand(logger5, options) {
25890
+ const mode = options.mode ?? "rulesync";
25891
+ if (mode === "gh") {
25892
+ await runGhInstall(logger5, options);
25893
+ return;
25894
+ }
25895
+ if (mode === "apm") {
25896
+ await runApmInstall(logger5, options);
25897
+ return;
25898
+ }
25899
+ await runRulesyncInstall(logger5, options);
25900
+ }
25901
+ async function runRulesyncInstall(logger5, options) {
25902
+ const baseDir = process.cwd();
25903
+ const apmExists = await apmManifestExists(baseDir);
24675
25904
  const config = await ConfigResolver.resolve({
24676
25905
  configPath: options.configPath,
24677
25906
  verbose: options.verbose,
24678
25907
  silent: options.silent
24679
25908
  });
24680
25909
  const sources = config.getSources();
25910
+ if (apmExists && sources.length > 0) {
25911
+ throw new Error(
25912
+ "Both apm.yml and rulesync.jsonc `sources` are defined. Pass --mode apm or --mode rulesync to disambiguate."
25913
+ );
25914
+ }
24681
25915
  if (sources.length === 0) {
25916
+ if (apmExists) {
25917
+ logger5.warn(
25918
+ "No sources defined in rulesync.jsonc, but apm.yml is present. Did you mean --mode apm?"
25919
+ );
25920
+ return;
25921
+ }
24682
25922
  logger5.warn("No sources defined in configuration. Nothing to install.");
24683
25923
  return;
24684
25924
  }
24685
25925
  logger5.debug(`Installing skills from ${sources.length} source(s)...`);
24686
25926
  const result = await resolveAndFetchSources({
24687
25927
  sources,
24688
- baseDir: process.cwd(),
25928
+ baseDir,
24689
25929
  options: {
24690
25930
  updateSources: options.update,
24691
25931
  frozen: options.frozen,
@@ -24705,21 +25945,95 @@ async function installCommand(logger5, options) {
24705
25945
  logger5.success(`All skills up to date (${result.sourcesProcessed} source(s) checked).`);
24706
25946
  }
24707
25947
  }
25948
+ async function runApmInstall(logger5, options) {
25949
+ const baseDir = process.cwd();
25950
+ if (!await apmManifestExists(baseDir)) {
25951
+ throw new Error(
25952
+ "--mode apm requires an apm.yml at the project root. Create one or drop --mode apm to fall back to rulesync mode."
25953
+ );
25954
+ }
25955
+ const result = await installApm({
25956
+ baseDir,
25957
+ options: {
25958
+ update: options.update,
25959
+ frozen: options.frozen,
25960
+ token: options.token
25961
+ },
25962
+ logger: logger5
25963
+ });
25964
+ if (logger5.jsonMode) {
25965
+ logger5.captureData("dependenciesProcessed", result.dependenciesProcessed);
25966
+ logger5.captureData("deployedFileCount", result.deployedFileCount);
25967
+ logger5.captureData("failedDependencyCount", result.failedDependencyCount);
25968
+ }
25969
+ if (result.failedDependencyCount > 0) {
25970
+ throw new Error(
25971
+ `Failed to install ${result.failedDependencyCount} of ${result.dependenciesProcessed} apm dependency(ies). See the log above for details.`
25972
+ );
25973
+ }
25974
+ if (result.deployedFileCount > 0) {
25975
+ logger5.success(
25976
+ `Installed ${result.deployedFileCount} file(s) from ${result.dependenciesProcessed} apm dependency(ies).`
25977
+ );
25978
+ } else {
25979
+ logger5.success(`All apm dependencies up to date (${result.dependenciesProcessed} checked).`);
25980
+ }
25981
+ }
25982
+ async function runGhInstall(logger5, options) {
25983
+ const baseDir = process.cwd();
25984
+ const config = await ConfigResolver.resolve({
25985
+ configPath: options.configPath,
25986
+ verbose: options.verbose,
25987
+ silent: options.silent
25988
+ });
25989
+ const sources = config.getSources();
25990
+ if (sources.length === 0) {
25991
+ logger5.warn("No sources defined in configuration. Nothing to install.");
25992
+ return;
25993
+ }
25994
+ const result = await installGh({
25995
+ baseDir,
25996
+ sources,
25997
+ options: {
25998
+ update: options.update,
25999
+ frozen: options.frozen,
26000
+ token: options.token
26001
+ },
26002
+ logger: logger5
26003
+ });
26004
+ if (logger5.jsonMode) {
26005
+ logger5.captureData("sourcesProcessed", result.sourcesProcessed);
26006
+ logger5.captureData("installedSkillCount", result.installedSkillCount);
26007
+ logger5.captureData("failedSourceCount", result.failedSourceCount);
26008
+ }
26009
+ if (result.failedSourceCount > 0) {
26010
+ throw new Error(
26011
+ `Failed to install ${result.failedSourceCount} of ${result.sourcesProcessed} gh source(s). See the log above for details.`
26012
+ );
26013
+ }
26014
+ if (result.installedSkillCount > 0) {
26015
+ logger5.success(
26016
+ `Installed ${result.installedSkillCount} skill(s) from ${result.sourcesProcessed} gh source(s).`
26017
+ );
26018
+ } else {
26019
+ logger5.success(`All gh sources up to date (${result.sourcesProcessed} checked).`);
26020
+ }
26021
+ }
24708
26022
 
24709
26023
  // src/cli/commands/mcp.ts
24710
26024
  var import_fastmcp = require("fastmcp");
24711
26025
 
24712
26026
  // src/mcp/tools.ts
24713
- var import_mini85 = require("zod/mini");
26027
+ var import_mini89 = require("zod/mini");
24714
26028
 
24715
26029
  // src/mcp/commands.ts
24716
- var import_node_path148 = require("path");
24717
- var import_mini77 = require("zod/mini");
26030
+ var import_node_path154 = require("path");
26031
+ var import_mini79 = require("zod/mini");
24718
26032
  var logger = new ConsoleLogger({ verbose: false, silent: true });
24719
26033
  var maxCommandSizeBytes = 1024 * 1024;
24720
26034
  var maxCommandsCount = 1e3;
24721
26035
  async function listCommands() {
24722
- const commandsDir = (0, import_node_path148.join)(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
26036
+ const commandsDir = (0, import_node_path154.join)(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
24723
26037
  try {
24724
26038
  const files = await listDirectoryFiles(commandsDir);
24725
26039
  const mdFiles = files.filter((file) => file.endsWith(".md"));
@@ -24735,7 +26049,7 @@ async function listCommands() {
24735
26049
  });
24736
26050
  const frontmatter = command.getFrontmatter();
24737
26051
  return {
24738
- relativePathFromCwd: (0, import_node_path148.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, file),
26052
+ relativePathFromCwd: (0, import_node_path154.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, file),
24739
26053
  frontmatter
24740
26054
  };
24741
26055
  } catch (error) {
@@ -24757,13 +26071,13 @@ async function getCommand({ relativePathFromCwd }) {
24757
26071
  relativePath: relativePathFromCwd,
24758
26072
  intendedRootDir: process.cwd()
24759
26073
  });
24760
- const filename = (0, import_node_path148.basename)(relativePathFromCwd);
26074
+ const filename = (0, import_node_path154.basename)(relativePathFromCwd);
24761
26075
  try {
24762
26076
  const command = await RulesyncCommand.fromFile({
24763
26077
  relativeFilePath: filename
24764
26078
  });
24765
26079
  return {
24766
- relativePathFromCwd: (0, import_node_path148.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
26080
+ relativePathFromCwd: (0, import_node_path154.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
24767
26081
  frontmatter: command.getFrontmatter(),
24768
26082
  body: command.getBody()
24769
26083
  };
@@ -24782,7 +26096,7 @@ async function putCommand({
24782
26096
  relativePath: relativePathFromCwd,
24783
26097
  intendedRootDir: process.cwd()
24784
26098
  });
24785
- const filename = (0, import_node_path148.basename)(relativePathFromCwd);
26099
+ const filename = (0, import_node_path154.basename)(relativePathFromCwd);
24786
26100
  const estimatedSize = JSON.stringify(frontmatter).length + body.length;
24787
26101
  if (estimatedSize > maxCommandSizeBytes) {
24788
26102
  throw new Error(
@@ -24792,7 +26106,7 @@ async function putCommand({
24792
26106
  try {
24793
26107
  const existingCommands = await listCommands();
24794
26108
  const isUpdate = existingCommands.some(
24795
- (command2) => command2.relativePathFromCwd === (0, import_node_path148.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
26109
+ (command2) => command2.relativePathFromCwd === (0, import_node_path154.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
24796
26110
  );
24797
26111
  if (!isUpdate && existingCommands.length >= maxCommandsCount) {
24798
26112
  throw new Error(
@@ -24809,11 +26123,11 @@ async function putCommand({
24809
26123
  fileContent,
24810
26124
  validate: true
24811
26125
  });
24812
- const commandsDir = (0, import_node_path148.join)(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
26126
+ const commandsDir = (0, import_node_path154.join)(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH);
24813
26127
  await ensureDir(commandsDir);
24814
26128
  await writeFileContent(command.getFilePath(), command.getFileContent());
24815
26129
  return {
24816
- relativePathFromCwd: (0, import_node_path148.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
26130
+ relativePathFromCwd: (0, import_node_path154.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename),
24817
26131
  frontmatter: command.getFrontmatter(),
24818
26132
  body: command.getBody()
24819
26133
  };
@@ -24828,12 +26142,12 @@ async function deleteCommand({ relativePathFromCwd }) {
24828
26142
  relativePath: relativePathFromCwd,
24829
26143
  intendedRootDir: process.cwd()
24830
26144
  });
24831
- const filename = (0, import_node_path148.basename)(relativePathFromCwd);
24832
- const fullPath = (0, import_node_path148.join)(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename);
26145
+ const filename = (0, import_node_path154.basename)(relativePathFromCwd);
26146
+ const fullPath = (0, import_node_path154.join)(process.cwd(), RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename);
24833
26147
  try {
24834
26148
  await removeFile(fullPath);
24835
26149
  return {
24836
- relativePathFromCwd: (0, import_node_path148.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
26150
+ relativePathFromCwd: (0, import_node_path154.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, filename)
24837
26151
  };
24838
26152
  } catch (error) {
24839
26153
  throw new Error(`Failed to delete command file ${relativePathFromCwd}: ${formatError(error)}`, {
@@ -24842,23 +26156,23 @@ async function deleteCommand({ relativePathFromCwd }) {
24842
26156
  }
24843
26157
  }
24844
26158
  var commandToolSchemas = {
24845
- listCommands: import_mini77.z.object({}),
24846
- getCommand: import_mini77.z.object({
24847
- relativePathFromCwd: import_mini77.z.string()
26159
+ listCommands: import_mini79.z.object({}),
26160
+ getCommand: import_mini79.z.object({
26161
+ relativePathFromCwd: import_mini79.z.string()
24848
26162
  }),
24849
- putCommand: import_mini77.z.object({
24850
- relativePathFromCwd: import_mini77.z.string(),
26163
+ putCommand: import_mini79.z.object({
26164
+ relativePathFromCwd: import_mini79.z.string(),
24851
26165
  frontmatter: RulesyncCommandFrontmatterSchema,
24852
- body: import_mini77.z.string()
26166
+ body: import_mini79.z.string()
24853
26167
  }),
24854
- deleteCommand: import_mini77.z.object({
24855
- relativePathFromCwd: import_mini77.z.string()
26168
+ deleteCommand: import_mini79.z.object({
26169
+ relativePathFromCwd: import_mini79.z.string()
24856
26170
  })
24857
26171
  };
24858
26172
  var commandTools = {
24859
26173
  listCommands: {
24860
26174
  name: "listCommands",
24861
- description: `List all commands from ${(0, import_node_path148.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
26175
+ description: `List all commands from ${(0, import_node_path154.join)(RULESYNC_COMMANDS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
24862
26176
  parameters: commandToolSchemas.listCommands,
24863
26177
  execute: async () => {
24864
26178
  const commands = await listCommands();
@@ -24900,15 +26214,15 @@ var commandTools = {
24900
26214
  };
24901
26215
 
24902
26216
  // src/mcp/generate.ts
24903
- var import_mini78 = require("zod/mini");
24904
- var generateOptionsSchema = import_mini78.z.object({
24905
- targets: import_mini78.z.optional(import_mini78.z.array(import_mini78.z.string())),
24906
- features: import_mini78.z.optional(import_mini78.z.array(import_mini78.z.string())),
24907
- delete: import_mini78.z.optional(import_mini78.z.boolean()),
24908
- global: import_mini78.z.optional(import_mini78.z.boolean()),
24909
- simulateCommands: import_mini78.z.optional(import_mini78.z.boolean()),
24910
- simulateSubagents: import_mini78.z.optional(import_mini78.z.boolean()),
24911
- simulateSkills: import_mini78.z.optional(import_mini78.z.boolean())
26217
+ var import_mini80 = require("zod/mini");
26218
+ var generateOptionsSchema = import_mini80.z.object({
26219
+ targets: import_mini80.z.optional(import_mini80.z.array(import_mini80.z.string())),
26220
+ features: import_mini80.z.optional(import_mini80.z.array(import_mini80.z.string())),
26221
+ delete: import_mini80.z.optional(import_mini80.z.boolean()),
26222
+ global: import_mini80.z.optional(import_mini80.z.boolean()),
26223
+ simulateCommands: import_mini80.z.optional(import_mini80.z.boolean()),
26224
+ simulateSubagents: import_mini80.z.optional(import_mini80.z.boolean()),
26225
+ simulateSkills: import_mini80.z.optional(import_mini80.z.boolean())
24912
26226
  });
24913
26227
  async function executeGenerate(options = {}) {
24914
26228
  try {
@@ -24986,12 +26300,139 @@ var generateTools = {
24986
26300
  }
24987
26301
  };
24988
26302
 
26303
+ // src/mcp/hooks.ts
26304
+ var import_node_path155 = require("path");
26305
+ var import_mini81 = require("zod/mini");
26306
+ var maxHooksSizeBytes = 1024 * 1024;
26307
+ async function getHooksFile() {
26308
+ try {
26309
+ const rulesyncHooks = await RulesyncHooks.fromFile({
26310
+ validate: true
26311
+ });
26312
+ const relativePathFromCwd = (0, import_node_path155.join)(
26313
+ rulesyncHooks.getRelativeDirPath(),
26314
+ rulesyncHooks.getRelativeFilePath()
26315
+ );
26316
+ return {
26317
+ relativePathFromCwd,
26318
+ content: rulesyncHooks.getFileContent()
26319
+ };
26320
+ } catch (error) {
26321
+ throw new Error(
26322
+ `Failed to read hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,
26323
+ {
26324
+ cause: error
26325
+ }
26326
+ );
26327
+ }
26328
+ }
26329
+ async function putHooksFile({ content }) {
26330
+ if (content.length > maxHooksSizeBytes) {
26331
+ throw new Error(
26332
+ `Hooks file size ${content.length} bytes exceeds maximum ${maxHooksSizeBytes} bytes (1MB) for ${RULESYNC_HOOKS_RELATIVE_FILE_PATH}`
26333
+ );
26334
+ }
26335
+ try {
26336
+ JSON.parse(content);
26337
+ } catch (error) {
26338
+ throw new Error(
26339
+ `Invalid JSON format in hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,
26340
+ {
26341
+ cause: error
26342
+ }
26343
+ );
26344
+ }
26345
+ try {
26346
+ const baseDir = process.cwd();
26347
+ const paths = RulesyncHooks.getSettablePaths();
26348
+ const relativeDirPath = paths.relativeDirPath;
26349
+ const relativeFilePath = paths.relativeFilePath;
26350
+ const fullPath = (0, import_node_path155.join)(baseDir, relativeDirPath, relativeFilePath);
26351
+ const rulesyncHooks = new RulesyncHooks({
26352
+ baseDir,
26353
+ relativeDirPath,
26354
+ relativeFilePath,
26355
+ fileContent: content,
26356
+ validate: true
26357
+ });
26358
+ await ensureDir((0, import_node_path155.join)(baseDir, relativeDirPath));
26359
+ await writeFileContent(fullPath, content);
26360
+ const relativePathFromCwd = (0, import_node_path155.join)(relativeDirPath, relativeFilePath);
26361
+ return {
26362
+ relativePathFromCwd,
26363
+ content: rulesyncHooks.getFileContent()
26364
+ };
26365
+ } catch (error) {
26366
+ throw new Error(
26367
+ `Failed to write hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,
26368
+ {
26369
+ cause: error
26370
+ }
26371
+ );
26372
+ }
26373
+ }
26374
+ async function deleteHooksFile() {
26375
+ try {
26376
+ const baseDir = process.cwd();
26377
+ const paths = RulesyncHooks.getSettablePaths();
26378
+ const filePath = (0, import_node_path155.join)(baseDir, paths.relativeDirPath, paths.relativeFilePath);
26379
+ await removeFile(filePath);
26380
+ const relativePathFromCwd = (0, import_node_path155.join)(paths.relativeDirPath, paths.relativeFilePath);
26381
+ return {
26382
+ relativePathFromCwd
26383
+ };
26384
+ } catch (error) {
26385
+ throw new Error(
26386
+ `Failed to delete hooks file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}): ${formatError(error)}`,
26387
+ {
26388
+ cause: error
26389
+ }
26390
+ );
26391
+ }
26392
+ }
26393
+ var hooksToolSchemas = {
26394
+ getHooksFile: import_mini81.z.object({}),
26395
+ putHooksFile: import_mini81.z.object({
26396
+ content: import_mini81.z.string()
26397
+ }),
26398
+ deleteHooksFile: import_mini81.z.object({})
26399
+ };
26400
+ var hooksTools = {
26401
+ getHooksFile: {
26402
+ name: "getHooksFile",
26403
+ description: `Get the hooks configuration file (${RULESYNC_HOOKS_RELATIVE_FILE_PATH}).`,
26404
+ parameters: hooksToolSchemas.getHooksFile,
26405
+ execute: async () => {
26406
+ const result = await getHooksFile();
26407
+ return JSON.stringify(result, null, 2);
26408
+ }
26409
+ },
26410
+ putHooksFile: {
26411
+ name: "putHooksFile",
26412
+ description: "Create or update the hooks configuration file (upsert operation). content parameter is required and must be valid JSON.",
26413
+ parameters: hooksToolSchemas.putHooksFile,
26414
+ execute: async (args) => {
26415
+ const result = await putHooksFile({ content: args.content });
26416
+ return JSON.stringify(result, null, 2);
26417
+ }
26418
+ },
26419
+ deleteHooksFile: {
26420
+ name: "deleteHooksFile",
26421
+ description: "Delete the hooks configuration file.",
26422
+ parameters: hooksToolSchemas.deleteHooksFile,
26423
+ execute: async () => {
26424
+ const result = await deleteHooksFile();
26425
+ return JSON.stringify(result, null, 2);
26426
+ }
26427
+ }
26428
+ };
26429
+
24989
26430
  // src/mcp/ignore.ts
24990
- var import_node_path149 = require("path");
24991
- var import_mini79 = require("zod/mini");
26431
+ var import_node_path156 = require("path");
26432
+ var import_mini82 = require("zod/mini");
24992
26433
  var maxIgnoreFileSizeBytes = 100 * 1024;
24993
26434
  async function getIgnoreFile() {
24994
- const ignoreFilePath = (0, import_node_path149.join)(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
26435
+ const ignoreFilePath = (0, import_node_path156.join)(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
24995
26436
  try {
24996
26437
  const content = await readFileContent(ignoreFilePath);
24997
26438
  return {
@@ -25008,7 +26449,7 @@ async function getIgnoreFile() {
25008
26449
  }
25009
26450
  }
25010
26451
  async function putIgnoreFile({ content }) {
25011
- const ignoreFilePath = (0, import_node_path149.join)(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
26452
+ const ignoreFilePath = (0, import_node_path156.join)(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
25012
26453
  const contentSizeBytes = Buffer.byteLength(content, "utf8");
25013
26454
  if (contentSizeBytes > maxIgnoreFileSizeBytes) {
25014
26455
  throw new Error(
@@ -25032,8 +26473,8 @@ async function putIgnoreFile({ content }) {
25032
26473
  }
25033
26474
  }
25034
26475
  async function deleteIgnoreFile() {
25035
- const aiignorePath = (0, import_node_path149.join)(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
25036
- const legacyIgnorePath = (0, import_node_path149.join)(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);
26476
+ const aiignorePath = (0, import_node_path156.join)(process.cwd(), RULESYNC_AIIGNORE_RELATIVE_FILE_PATH);
26477
+ const legacyIgnorePath = (0, import_node_path156.join)(process.cwd(), RULESYNC_IGNORE_RELATIVE_FILE_PATH);
25037
26478
  try {
25038
26479
  await Promise.all([removeFile(aiignorePath), removeFile(legacyIgnorePath)]);
25039
26480
  return {
@@ -25051,11 +26492,11 @@ async function deleteIgnoreFile() {
25051
26492
  }
25052
26493
  }
25053
26494
  var ignoreToolSchemas = {
25054
- getIgnoreFile: import_mini79.z.object({}),
25055
- putIgnoreFile: import_mini79.z.object({
25056
- content: import_mini79.z.string()
26495
+ getIgnoreFile: import_mini82.z.object({}),
26496
+ putIgnoreFile: import_mini82.z.object({
26497
+ content: import_mini82.z.string()
25057
26498
  }),
25058
- deleteIgnoreFile: import_mini79.z.object({})
26499
+ deleteIgnoreFile: import_mini82.z.object({})
25059
26500
  };
25060
26501
  var ignoreTools = {
25061
26502
  getIgnoreFile: {
@@ -25088,11 +26529,11 @@ var ignoreTools = {
25088
26529
  };
25089
26530
 
25090
26531
  // src/mcp/import.ts
25091
- var import_mini80 = require("zod/mini");
25092
- var importOptionsSchema = import_mini80.z.object({
25093
- target: import_mini80.z.string(),
25094
- features: import_mini80.z.optional(import_mini80.z.array(import_mini80.z.string())),
25095
- global: import_mini80.z.optional(import_mini80.z.boolean())
26532
+ var import_mini83 = require("zod/mini");
26533
+ var importOptionsSchema = import_mini83.z.object({
26534
+ target: import_mini83.z.string(),
26535
+ features: import_mini83.z.optional(import_mini83.z.array(import_mini83.z.string())),
26536
+ global: import_mini83.z.optional(import_mini83.z.boolean())
25096
26537
  });
25097
26538
  async function executeImport(options) {
25098
26539
  try {
@@ -25163,15 +26604,15 @@ var importTools = {
25163
26604
  };
25164
26605
 
25165
26606
  // src/mcp/mcp.ts
25166
- var import_node_path150 = require("path");
25167
- var import_mini81 = require("zod/mini");
26607
+ var import_node_path157 = require("path");
26608
+ var import_mini84 = require("zod/mini");
25168
26609
  var maxMcpSizeBytes = 1024 * 1024;
25169
26610
  async function getMcpFile() {
25170
26611
  try {
25171
26612
  const rulesyncMcp = await RulesyncMcp.fromFile({
25172
26613
  validate: true
25173
26614
  });
25174
- const relativePathFromCwd = (0, import_node_path150.join)(
26615
+ const relativePathFromCwd = (0, import_node_path157.join)(
25175
26616
  rulesyncMcp.getRelativeDirPath(),
25176
26617
  rulesyncMcp.getRelativeFilePath()
25177
26618
  );
@@ -25209,7 +26650,7 @@ async function putMcpFile({ content }) {
25209
26650
  const paths = RulesyncMcp.getSettablePaths();
25210
26651
  const relativeDirPath = paths.recommended.relativeDirPath;
25211
26652
  const relativeFilePath = paths.recommended.relativeFilePath;
25212
- const fullPath = (0, import_node_path150.join)(baseDir, relativeDirPath, relativeFilePath);
26653
+ const fullPath = (0, import_node_path157.join)(baseDir, relativeDirPath, relativeFilePath);
25213
26654
  const rulesyncMcp = new RulesyncMcp({
25214
26655
  baseDir,
25215
26656
  relativeDirPath,
@@ -25217,9 +26658,9 @@ async function putMcpFile({ content }) {
25217
26658
  fileContent: content,
25218
26659
  validate: true
25219
26660
  });
25220
- await ensureDir((0, import_node_path150.join)(baseDir, relativeDirPath));
26661
+ await ensureDir((0, import_node_path157.join)(baseDir, relativeDirPath));
25221
26662
  await writeFileContent(fullPath, content);
25222
- const relativePathFromCwd = (0, import_node_path150.join)(relativeDirPath, relativeFilePath);
26663
+ const relativePathFromCwd = (0, import_node_path157.join)(relativeDirPath, relativeFilePath);
25223
26664
  return {
25224
26665
  relativePathFromCwd,
25225
26666
  content: rulesyncMcp.getFileContent()
@@ -25237,15 +26678,15 @@ async function deleteMcpFile() {
25237
26678
  try {
25238
26679
  const baseDir = process.cwd();
25239
26680
  const paths = RulesyncMcp.getSettablePaths();
25240
- const recommendedPath = (0, import_node_path150.join)(
26681
+ const recommendedPath = (0, import_node_path157.join)(
25241
26682
  baseDir,
25242
26683
  paths.recommended.relativeDirPath,
25243
26684
  paths.recommended.relativeFilePath
25244
26685
  );
25245
- const legacyPath = (0, import_node_path150.join)(baseDir, paths.legacy.relativeDirPath, paths.legacy.relativeFilePath);
26686
+ const legacyPath = (0, import_node_path157.join)(baseDir, paths.legacy.relativeDirPath, paths.legacy.relativeFilePath);
25246
26687
  await removeFile(recommendedPath);
25247
26688
  await removeFile(legacyPath);
25248
- const relativePathFromCwd = (0, import_node_path150.join)(
26689
+ const relativePathFromCwd = (0, import_node_path157.join)(
25249
26690
  paths.recommended.relativeDirPath,
25250
26691
  paths.recommended.relativeFilePath
25251
26692
  );
@@ -25262,11 +26703,11 @@ async function deleteMcpFile() {
25262
26703
  }
25263
26704
  }
25264
26705
  var mcpToolSchemas = {
25265
- getMcpFile: import_mini81.z.object({}),
25266
- putMcpFile: import_mini81.z.object({
25267
- content: import_mini81.z.string()
26706
+ getMcpFile: import_mini84.z.object({}),
26707
+ putMcpFile: import_mini84.z.object({
26708
+ content: import_mini84.z.string()
25268
26709
  }),
25269
- deleteMcpFile: import_mini81.z.object({})
26710
+ deleteMcpFile: import_mini84.z.object({})
25270
26711
  };
25271
26712
  var mcpTools = {
25272
26713
  getMcpFile: {
@@ -25298,14 +26739,141 @@ var mcpTools = {
25298
26739
  }
25299
26740
  };
25300
26741
 
26742
+ // src/mcp/permissions.ts
26743
+ var import_node_path158 = require("path");
26744
+ var import_mini85 = require("zod/mini");
26745
+ var maxPermissionsSizeBytes = 1024 * 1024;
26746
+ async function getPermissionsFile() {
26747
+ try {
26748
+ const rulesyncPermissions = await RulesyncPermissions.fromFile({
26749
+ validate: true
26750
+ });
26751
+ const relativePathFromCwd = (0, import_node_path158.join)(
26752
+ rulesyncPermissions.getRelativeDirPath(),
26753
+ rulesyncPermissions.getRelativeFilePath()
26754
+ );
26755
+ return {
26756
+ relativePathFromCwd,
26757
+ content: rulesyncPermissions.getFileContent()
26758
+ };
26759
+ } catch (error) {
26760
+ throw new Error(
26761
+ `Failed to read permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,
26762
+ {
26763
+ cause: error
26764
+ }
26765
+ );
26766
+ }
26767
+ }
26768
+ async function putPermissionsFile({ content }) {
26769
+ if (content.length > maxPermissionsSizeBytes) {
26770
+ throw new Error(
26771
+ `Permissions file size ${content.length} bytes exceeds maximum ${maxPermissionsSizeBytes} bytes (1MB) for ${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}`
26772
+ );
26773
+ }
26774
+ try {
26775
+ JSON.parse(content);
26776
+ } catch (error) {
26777
+ throw new Error(
26778
+ `Invalid JSON format in permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,
26779
+ {
26780
+ cause: error
26781
+ }
26782
+ );
26783
+ }
26784
+ try {
26785
+ const baseDir = process.cwd();
26786
+ const paths = RulesyncPermissions.getSettablePaths();
26787
+ const relativeDirPath = paths.relativeDirPath;
26788
+ const relativeFilePath = paths.relativeFilePath;
26789
+ const fullPath = (0, import_node_path158.join)(baseDir, relativeDirPath, relativeFilePath);
26790
+ const rulesyncPermissions = new RulesyncPermissions({
26791
+ baseDir,
26792
+ relativeDirPath,
26793
+ relativeFilePath,
26794
+ fileContent: content,
26795
+ validate: true
26796
+ });
26797
+ await ensureDir((0, import_node_path158.join)(baseDir, relativeDirPath));
26798
+ await writeFileContent(fullPath, content);
26799
+ const relativePathFromCwd = (0, import_node_path158.join)(relativeDirPath, relativeFilePath);
26800
+ return {
26801
+ relativePathFromCwd,
26802
+ content: rulesyncPermissions.getFileContent()
26803
+ };
26804
+ } catch (error) {
26805
+ throw new Error(
26806
+ `Failed to write permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,
26807
+ {
26808
+ cause: error
26809
+ }
26810
+ );
26811
+ }
26812
+ }
26813
+ async function deletePermissionsFile() {
26814
+ try {
26815
+ const baseDir = process.cwd();
26816
+ const paths = RulesyncPermissions.getSettablePaths();
26817
+ const filePath = (0, import_node_path158.join)(baseDir, paths.relativeDirPath, paths.relativeFilePath);
26818
+ await removeFile(filePath);
26819
+ const relativePathFromCwd = (0, import_node_path158.join)(paths.relativeDirPath, paths.relativeFilePath);
26820
+ return {
26821
+ relativePathFromCwd
26822
+ };
26823
+ } catch (error) {
26824
+ throw new Error(
26825
+ `Failed to delete permissions file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}): ${formatError(error)}`,
26826
+ {
26827
+ cause: error
26828
+ }
26829
+ );
26830
+ }
26831
+ }
26832
+ var permissionsToolSchemas = {
26833
+ getPermissionsFile: import_mini85.z.object({}),
26834
+ putPermissionsFile: import_mini85.z.object({
26835
+ content: import_mini85.z.string()
26836
+ }),
26837
+ deletePermissionsFile: import_mini85.z.object({})
26838
+ };
26839
+ var permissionsTools = {
26840
+ getPermissionsFile: {
26841
+ name: "getPermissionsFile",
26842
+ description: `Get the permissions configuration file (${RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH}).`,
26843
+ parameters: permissionsToolSchemas.getPermissionsFile,
26844
+ execute: async () => {
26845
+ const result = await getPermissionsFile();
26846
+ return JSON.stringify(result, null, 2);
26847
+ }
26848
+ },
26849
+ putPermissionsFile: {
26850
+ name: "putPermissionsFile",
26851
+ description: "Create or update the permissions configuration file (upsert operation). content parameter is required and must be valid JSON.",
26852
+ parameters: permissionsToolSchemas.putPermissionsFile,
26853
+ execute: async (args) => {
26854
+ const result = await putPermissionsFile({ content: args.content });
26855
+ return JSON.stringify(result, null, 2);
26856
+ }
26857
+ },
26858
+ deletePermissionsFile: {
26859
+ name: "deletePermissionsFile",
26860
+ description: "Delete the permissions configuration file.",
26861
+ parameters: permissionsToolSchemas.deletePermissionsFile,
26862
+ execute: async () => {
26863
+ const result = await deletePermissionsFile();
26864
+ return JSON.stringify(result, null, 2);
26865
+ }
26866
+ }
26867
+ };
26868
+
25301
26869
  // src/mcp/rules.ts
25302
- var import_node_path151 = require("path");
25303
- var import_mini82 = require("zod/mini");
26870
+ var import_node_path159 = require("path");
26871
+ var import_mini86 = require("zod/mini");
25304
26872
  var logger2 = new ConsoleLogger({ verbose: false, silent: true });
25305
26873
  var maxRuleSizeBytes = 1024 * 1024;
25306
26874
  var maxRulesCount = 1e3;
25307
26875
  async function listRules() {
25308
- const rulesDir = (0, import_node_path151.join)(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
26876
+ const rulesDir = (0, import_node_path159.join)(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
25309
26877
  try {
25310
26878
  const files = await listDirectoryFiles(rulesDir);
25311
26879
  const mdFiles = files.filter((file) => file.endsWith(".md"));
@@ -25318,7 +26886,7 @@ async function listRules() {
25318
26886
  });
25319
26887
  const frontmatter = rule.getFrontmatter();
25320
26888
  return {
25321
- relativePathFromCwd: (0, import_node_path151.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, file),
26889
+ relativePathFromCwd: (0, import_node_path159.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, file),
25322
26890
  frontmatter
25323
26891
  };
25324
26892
  } catch (error) {
@@ -25340,14 +26908,14 @@ async function getRule({ relativePathFromCwd }) {
25340
26908
  relativePath: relativePathFromCwd,
25341
26909
  intendedRootDir: process.cwd()
25342
26910
  });
25343
- const filename = (0, import_node_path151.basename)(relativePathFromCwd);
26911
+ const filename = (0, import_node_path159.basename)(relativePathFromCwd);
25344
26912
  try {
25345
26913
  const rule = await RulesyncRule.fromFile({
25346
26914
  relativeFilePath: filename,
25347
26915
  validate: true
25348
26916
  });
25349
26917
  return {
25350
- relativePathFromCwd: (0, import_node_path151.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
26918
+ relativePathFromCwd: (0, import_node_path159.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
25351
26919
  frontmatter: rule.getFrontmatter(),
25352
26920
  body: rule.getBody()
25353
26921
  };
@@ -25366,7 +26934,7 @@ async function putRule({
25366
26934
  relativePath: relativePathFromCwd,
25367
26935
  intendedRootDir: process.cwd()
25368
26936
  });
25369
- const filename = (0, import_node_path151.basename)(relativePathFromCwd);
26937
+ const filename = (0, import_node_path159.basename)(relativePathFromCwd);
25370
26938
  const estimatedSize = JSON.stringify(frontmatter).length + body.length;
25371
26939
  if (estimatedSize > maxRuleSizeBytes) {
25372
26940
  throw new Error(
@@ -25376,7 +26944,7 @@ async function putRule({
25376
26944
  try {
25377
26945
  const existingRules = await listRules();
25378
26946
  const isUpdate = existingRules.some(
25379
- (rule2) => rule2.relativePathFromCwd === (0, import_node_path151.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
26947
+ (rule2) => rule2.relativePathFromCwd === (0, import_node_path159.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
25380
26948
  );
25381
26949
  if (!isUpdate && existingRules.length >= maxRulesCount) {
25382
26950
  throw new Error(
@@ -25391,11 +26959,11 @@ async function putRule({
25391
26959
  body,
25392
26960
  validate: true
25393
26961
  });
25394
- const rulesDir = (0, import_node_path151.join)(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
26962
+ const rulesDir = (0, import_node_path159.join)(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH);
25395
26963
  await ensureDir(rulesDir);
25396
26964
  await writeFileContent(rule.getFilePath(), rule.getFileContent());
25397
26965
  return {
25398
- relativePathFromCwd: (0, import_node_path151.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
26966
+ relativePathFromCwd: (0, import_node_path159.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, filename),
25399
26967
  frontmatter: rule.getFrontmatter(),
25400
26968
  body: rule.getBody()
25401
26969
  };
@@ -25410,12 +26978,12 @@ async function deleteRule({ relativePathFromCwd }) {
25410
26978
  relativePath: relativePathFromCwd,
25411
26979
  intendedRootDir: process.cwd()
25412
26980
  });
25413
- const filename = (0, import_node_path151.basename)(relativePathFromCwd);
25414
- const fullPath = (0, import_node_path151.join)(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH, filename);
26981
+ const filename = (0, import_node_path159.basename)(relativePathFromCwd);
26982
+ const fullPath = (0, import_node_path159.join)(process.cwd(), RULESYNC_RULES_RELATIVE_DIR_PATH, filename);
25415
26983
  try {
25416
26984
  await removeFile(fullPath);
25417
26985
  return {
25418
- relativePathFromCwd: (0, import_node_path151.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
26986
+ relativePathFromCwd: (0, import_node_path159.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, filename)
25419
26987
  };
25420
26988
  } catch (error) {
25421
26989
  throw new Error(`Failed to delete rule file ${relativePathFromCwd}: ${formatError(error)}`, {
@@ -25424,23 +26992,23 @@ async function deleteRule({ relativePathFromCwd }) {
25424
26992
  }
25425
26993
  }
25426
26994
  var ruleToolSchemas = {
25427
- listRules: import_mini82.z.object({}),
25428
- getRule: import_mini82.z.object({
25429
- relativePathFromCwd: import_mini82.z.string()
26995
+ listRules: import_mini86.z.object({}),
26996
+ getRule: import_mini86.z.object({
26997
+ relativePathFromCwd: import_mini86.z.string()
25430
26998
  }),
25431
- putRule: import_mini82.z.object({
25432
- relativePathFromCwd: import_mini82.z.string(),
26999
+ putRule: import_mini86.z.object({
27000
+ relativePathFromCwd: import_mini86.z.string(),
25433
27001
  frontmatter: RulesyncRuleFrontmatterSchema,
25434
- body: import_mini82.z.string()
27002
+ body: import_mini86.z.string()
25435
27003
  }),
25436
- deleteRule: import_mini82.z.object({
25437
- relativePathFromCwd: import_mini82.z.string()
27004
+ deleteRule: import_mini86.z.object({
27005
+ relativePathFromCwd: import_mini86.z.string()
25438
27006
  })
25439
27007
  };
25440
27008
  var ruleTools = {
25441
27009
  listRules: {
25442
27010
  name: "listRules",
25443
- description: `List all rules from ${(0, import_node_path151.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
27011
+ description: `List all rules from ${(0, import_node_path159.join)(RULESYNC_RULES_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
25444
27012
  parameters: ruleToolSchemas.listRules,
25445
27013
  execute: async () => {
25446
27014
  const rules = await listRules();
@@ -25482,8 +27050,8 @@ var ruleTools = {
25482
27050
  };
25483
27051
 
25484
27052
  // src/mcp/skills.ts
25485
- var import_node_path152 = require("path");
25486
- var import_mini83 = require("zod/mini");
27053
+ var import_node_path160 = require("path");
27054
+ var import_mini87 = require("zod/mini");
25487
27055
  var logger3 = new ConsoleLogger({ verbose: false, silent: true });
25488
27056
  var maxSkillSizeBytes = 1024 * 1024;
25489
27057
  var maxSkillsCount = 1e3;
@@ -25500,19 +27068,19 @@ function mcpSkillFileToAiDirFile(file) {
25500
27068
  };
25501
27069
  }
25502
27070
  function extractDirName(relativeDirPathFromCwd) {
25503
- const dirName = (0, import_node_path152.basename)(relativeDirPathFromCwd);
27071
+ const dirName = (0, import_node_path160.basename)(relativeDirPathFromCwd);
25504
27072
  if (!dirName) {
25505
27073
  throw new Error(`Invalid path: ${relativeDirPathFromCwd}`);
25506
27074
  }
25507
27075
  return dirName;
25508
27076
  }
25509
27077
  async function listSkills() {
25510
- const skillsDir = (0, import_node_path152.join)(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH);
27078
+ const skillsDir = (0, import_node_path160.join)(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH);
25511
27079
  try {
25512
- const skillDirPaths = await findFilesByGlobs((0, import_node_path152.join)(skillsDir, "*"), { type: "dir" });
27080
+ const skillDirPaths = await findFilesByGlobs((0, import_node_path160.join)(skillsDir, "*"), { type: "dir" });
25513
27081
  const skills = await Promise.all(
25514
27082
  skillDirPaths.map(async (dirPath) => {
25515
- const dirName = (0, import_node_path152.basename)(dirPath);
27083
+ const dirName = (0, import_node_path160.basename)(dirPath);
25516
27084
  if (!dirName) return null;
25517
27085
  try {
25518
27086
  const skill = await RulesyncSkill.fromDir({
@@ -25520,7 +27088,7 @@ async function listSkills() {
25520
27088
  });
25521
27089
  const frontmatter = skill.getFrontmatter();
25522
27090
  return {
25523
- relativeDirPathFromCwd: (0, import_node_path152.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
27091
+ relativeDirPathFromCwd: (0, import_node_path160.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
25524
27092
  frontmatter
25525
27093
  };
25526
27094
  } catch (error) {
@@ -25548,7 +27116,7 @@ async function getSkill({ relativeDirPathFromCwd }) {
25548
27116
  dirName
25549
27117
  });
25550
27118
  return {
25551
- relativeDirPathFromCwd: (0, import_node_path152.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
27119
+ relativeDirPathFromCwd: (0, import_node_path160.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
25552
27120
  frontmatter: skill.getFrontmatter(),
25553
27121
  body: skill.getBody(),
25554
27122
  otherFiles: skill.getOtherFiles().map(aiDirFileToMcpSkillFile)
@@ -25582,7 +27150,7 @@ async function putSkill({
25582
27150
  try {
25583
27151
  const existingSkills = await listSkills();
25584
27152
  const isUpdate = existingSkills.some(
25585
- (skill2) => skill2.relativeDirPathFromCwd === (0, import_node_path152.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName)
27153
+ (skill2) => skill2.relativeDirPathFromCwd === (0, import_node_path160.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName)
25586
27154
  );
25587
27155
  if (!isUpdate && existingSkills.length >= maxSkillsCount) {
25588
27156
  throw new Error(
@@ -25599,9 +27167,9 @@ async function putSkill({
25599
27167
  otherFiles: aiDirFiles,
25600
27168
  validate: true
25601
27169
  });
25602
- const skillDirPath = (0, import_node_path152.join)(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);
27170
+ const skillDirPath = (0, import_node_path160.join)(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);
25603
27171
  await ensureDir(skillDirPath);
25604
- const skillFilePath = (0, import_node_path152.join)(skillDirPath, SKILL_FILE_NAME);
27172
+ const skillFilePath = (0, import_node_path160.join)(skillDirPath, SKILL_FILE_NAME);
25605
27173
  const skillFileContent = stringifyFrontmatter(body, frontmatter);
25606
27174
  await writeFileContent(skillFilePath, skillFileContent);
25607
27175
  for (const file of otherFiles) {
@@ -25609,15 +27177,15 @@ async function putSkill({
25609
27177
  relativePath: file.name,
25610
27178
  intendedRootDir: skillDirPath
25611
27179
  });
25612
- const filePath = (0, import_node_path152.join)(skillDirPath, file.name);
25613
- const fileDir = (0, import_node_path152.join)(skillDirPath, (0, import_node_path152.dirname)(file.name));
27180
+ const filePath = (0, import_node_path160.join)(skillDirPath, file.name);
27181
+ const fileDir = (0, import_node_path160.join)(skillDirPath, (0, import_node_path160.dirname)(file.name));
25614
27182
  if (fileDir !== skillDirPath) {
25615
27183
  await ensureDir(fileDir);
25616
27184
  }
25617
27185
  await writeFileContent(filePath, file.body);
25618
27186
  }
25619
27187
  return {
25620
- relativeDirPathFromCwd: (0, import_node_path152.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
27188
+ relativeDirPathFromCwd: (0, import_node_path160.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName),
25621
27189
  frontmatter: skill.getFrontmatter(),
25622
27190
  body: skill.getBody(),
25623
27191
  otherFiles: skill.getOtherFiles().map(aiDirFileToMcpSkillFile)
@@ -25639,13 +27207,13 @@ async function deleteSkill({
25639
27207
  intendedRootDir: process.cwd()
25640
27208
  });
25641
27209
  const dirName = extractDirName(relativeDirPathFromCwd);
25642
- const skillDirPath = (0, import_node_path152.join)(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);
27210
+ const skillDirPath = (0, import_node_path160.join)(process.cwd(), RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName);
25643
27211
  try {
25644
27212
  if (await directoryExists(skillDirPath)) {
25645
27213
  await removeDirectory(skillDirPath);
25646
27214
  }
25647
27215
  return {
25648
- relativeDirPathFromCwd: (0, import_node_path152.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName)
27216
+ relativeDirPathFromCwd: (0, import_node_path160.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, dirName)
25649
27217
  };
25650
27218
  } catch (error) {
25651
27219
  throw new Error(
@@ -25656,29 +27224,29 @@ async function deleteSkill({
25656
27224
  );
25657
27225
  }
25658
27226
  }
25659
- var McpSkillFileSchema = import_mini83.z.object({
25660
- name: import_mini83.z.string(),
25661
- body: import_mini83.z.string()
27227
+ var McpSkillFileSchema = import_mini87.z.object({
27228
+ name: import_mini87.z.string(),
27229
+ body: import_mini87.z.string()
25662
27230
  });
25663
27231
  var skillToolSchemas = {
25664
- listSkills: import_mini83.z.object({}),
25665
- getSkill: import_mini83.z.object({
25666
- relativeDirPathFromCwd: import_mini83.z.string()
27232
+ listSkills: import_mini87.z.object({}),
27233
+ getSkill: import_mini87.z.object({
27234
+ relativeDirPathFromCwd: import_mini87.z.string()
25667
27235
  }),
25668
- putSkill: import_mini83.z.object({
25669
- relativeDirPathFromCwd: import_mini83.z.string(),
27236
+ putSkill: import_mini87.z.object({
27237
+ relativeDirPathFromCwd: import_mini87.z.string(),
25670
27238
  frontmatter: RulesyncSkillFrontmatterSchema,
25671
- body: import_mini83.z.string(),
25672
- otherFiles: import_mini83.z.optional(import_mini83.z.array(McpSkillFileSchema))
27239
+ body: import_mini87.z.string(),
27240
+ otherFiles: import_mini87.z.optional(import_mini87.z.array(McpSkillFileSchema))
25673
27241
  }),
25674
- deleteSkill: import_mini83.z.object({
25675
- relativeDirPathFromCwd: import_mini83.z.string()
27242
+ deleteSkill: import_mini87.z.object({
27243
+ relativeDirPathFromCwd: import_mini87.z.string()
25676
27244
  })
25677
27245
  };
25678
27246
  var skillTools = {
25679
27247
  listSkills: {
25680
27248
  name: "listSkills",
25681
- description: `List all skills from ${(0, import_node_path152.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, "*", SKILL_FILE_NAME)} with their frontmatter.`,
27249
+ description: `List all skills from ${(0, import_node_path160.join)(RULESYNC_SKILLS_RELATIVE_DIR_PATH, "*", SKILL_FILE_NAME)} with their frontmatter.`,
25682
27250
  parameters: skillToolSchemas.listSkills,
25683
27251
  execute: async () => {
25684
27252
  const skills = await listSkills();
@@ -25721,13 +27289,13 @@ var skillTools = {
25721
27289
  };
25722
27290
 
25723
27291
  // src/mcp/subagents.ts
25724
- var import_node_path153 = require("path");
25725
- var import_mini84 = require("zod/mini");
27292
+ var import_node_path161 = require("path");
27293
+ var import_mini88 = require("zod/mini");
25726
27294
  var logger4 = new ConsoleLogger({ verbose: false, silent: true });
25727
27295
  var maxSubagentSizeBytes = 1024 * 1024;
25728
27296
  var maxSubagentsCount = 1e3;
25729
27297
  async function listSubagents() {
25730
- const subagentsDir = (0, import_node_path153.join)(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
27298
+ const subagentsDir = (0, import_node_path161.join)(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
25731
27299
  try {
25732
27300
  const files = await listDirectoryFiles(subagentsDir);
25733
27301
  const mdFiles = files.filter((file) => file.endsWith(".md"));
@@ -25740,7 +27308,7 @@ async function listSubagents() {
25740
27308
  });
25741
27309
  const frontmatter = subagent.getFrontmatter();
25742
27310
  return {
25743
- relativePathFromCwd: (0, import_node_path153.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, file),
27311
+ relativePathFromCwd: (0, import_node_path161.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, file),
25744
27312
  frontmatter
25745
27313
  };
25746
27314
  } catch (error) {
@@ -25764,14 +27332,14 @@ async function getSubagent({ relativePathFromCwd }) {
25764
27332
  relativePath: relativePathFromCwd,
25765
27333
  intendedRootDir: process.cwd()
25766
27334
  });
25767
- const filename = (0, import_node_path153.basename)(relativePathFromCwd);
27335
+ const filename = (0, import_node_path161.basename)(relativePathFromCwd);
25768
27336
  try {
25769
27337
  const subagent = await RulesyncSubagent.fromFile({
25770
27338
  relativeFilePath: filename,
25771
27339
  validate: true
25772
27340
  });
25773
27341
  return {
25774
- relativePathFromCwd: (0, import_node_path153.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
27342
+ relativePathFromCwd: (0, import_node_path161.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
25775
27343
  frontmatter: subagent.getFrontmatter(),
25776
27344
  body: subagent.getBody()
25777
27345
  };
@@ -25790,7 +27358,7 @@ async function putSubagent({
25790
27358
  relativePath: relativePathFromCwd,
25791
27359
  intendedRootDir: process.cwd()
25792
27360
  });
25793
- const filename = (0, import_node_path153.basename)(relativePathFromCwd);
27361
+ const filename = (0, import_node_path161.basename)(relativePathFromCwd);
25794
27362
  const estimatedSize = JSON.stringify(frontmatter).length + body.length;
25795
27363
  if (estimatedSize > maxSubagentSizeBytes) {
25796
27364
  throw new Error(
@@ -25800,7 +27368,7 @@ async function putSubagent({
25800
27368
  try {
25801
27369
  const existingSubagents = await listSubagents();
25802
27370
  const isUpdate = existingSubagents.some(
25803
- (subagent2) => subagent2.relativePathFromCwd === (0, import_node_path153.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
27371
+ (subagent2) => subagent2.relativePathFromCwd === (0, import_node_path161.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
25804
27372
  );
25805
27373
  if (!isUpdate && existingSubagents.length >= maxSubagentsCount) {
25806
27374
  throw new Error(
@@ -25815,11 +27383,11 @@ async function putSubagent({
25815
27383
  body,
25816
27384
  validate: true
25817
27385
  });
25818
- const subagentsDir = (0, import_node_path153.join)(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
27386
+ const subagentsDir = (0, import_node_path161.join)(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH);
25819
27387
  await ensureDir(subagentsDir);
25820
27388
  await writeFileContent(subagent.getFilePath(), subagent.getFileContent());
25821
27389
  return {
25822
- relativePathFromCwd: (0, import_node_path153.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
27390
+ relativePathFromCwd: (0, import_node_path161.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename),
25823
27391
  frontmatter: subagent.getFrontmatter(),
25824
27392
  body: subagent.getBody()
25825
27393
  };
@@ -25834,12 +27402,12 @@ async function deleteSubagent({ relativePathFromCwd }) {
25834
27402
  relativePath: relativePathFromCwd,
25835
27403
  intendedRootDir: process.cwd()
25836
27404
  });
25837
- const filename = (0, import_node_path153.basename)(relativePathFromCwd);
25838
- const fullPath = (0, import_node_path153.join)(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename);
27405
+ const filename = (0, import_node_path161.basename)(relativePathFromCwd);
27406
+ const fullPath = (0, import_node_path161.join)(process.cwd(), RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename);
25839
27407
  try {
25840
27408
  await removeFile(fullPath);
25841
27409
  return {
25842
- relativePathFromCwd: (0, import_node_path153.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
27410
+ relativePathFromCwd: (0, import_node_path161.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, filename)
25843
27411
  };
25844
27412
  } catch (error) {
25845
27413
  throw new Error(
@@ -25851,23 +27419,23 @@ async function deleteSubagent({ relativePathFromCwd }) {
25851
27419
  }
25852
27420
  }
25853
27421
  var subagentToolSchemas = {
25854
- listSubagents: import_mini84.z.object({}),
25855
- getSubagent: import_mini84.z.object({
25856
- relativePathFromCwd: import_mini84.z.string()
27422
+ listSubagents: import_mini88.z.object({}),
27423
+ getSubagent: import_mini88.z.object({
27424
+ relativePathFromCwd: import_mini88.z.string()
25857
27425
  }),
25858
- putSubagent: import_mini84.z.object({
25859
- relativePathFromCwd: import_mini84.z.string(),
27426
+ putSubagent: import_mini88.z.object({
27427
+ relativePathFromCwd: import_mini88.z.string(),
25860
27428
  frontmatter: RulesyncSubagentFrontmatterSchema,
25861
- body: import_mini84.z.string()
27429
+ body: import_mini88.z.string()
25862
27430
  }),
25863
- deleteSubagent: import_mini84.z.object({
25864
- relativePathFromCwd: import_mini84.z.string()
27431
+ deleteSubagent: import_mini88.z.object({
27432
+ relativePathFromCwd: import_mini88.z.string()
25865
27433
  })
25866
27434
  };
25867
27435
  var subagentTools = {
25868
27436
  listSubagents: {
25869
27437
  name: "listSubagents",
25870
- description: `List all subagents from ${(0, import_node_path153.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
27438
+ description: `List all subagents from ${(0, import_node_path161.join)(RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, "*.md")} with their frontmatter.`,
25871
27439
  parameters: subagentToolSchemas.listSubagents,
25872
27440
  execute: async () => {
25873
27441
  const subagents = await listSubagents();
@@ -25909,31 +27477,33 @@ var subagentTools = {
25909
27477
  };
25910
27478
 
25911
27479
  // src/mcp/tools.ts
25912
- var rulesyncFeatureSchema = import_mini85.z.enum([
27480
+ var rulesyncFeatureSchema = import_mini89.z.enum([
25913
27481
  "rule",
25914
27482
  "command",
25915
27483
  "subagent",
25916
27484
  "skill",
25917
27485
  "ignore",
25918
27486
  "mcp",
27487
+ "permissions",
27488
+ "hooks",
25919
27489
  "generate",
25920
27490
  "import"
25921
27491
  ]);
25922
- var rulesyncOperationSchema = import_mini85.z.enum(["list", "get", "put", "delete", "run"]);
25923
- var skillFileSchema = import_mini85.z.object({
25924
- name: import_mini85.z.string(),
25925
- body: import_mini85.z.string()
27492
+ var rulesyncOperationSchema = import_mini89.z.enum(["list", "get", "put", "delete", "run"]);
27493
+ var skillFileSchema = import_mini89.z.object({
27494
+ name: import_mini89.z.string(),
27495
+ body: import_mini89.z.string()
25926
27496
  });
25927
- var rulesyncToolSchema = import_mini85.z.object({
27497
+ var rulesyncToolSchema = import_mini89.z.object({
25928
27498
  feature: rulesyncFeatureSchema,
25929
27499
  operation: rulesyncOperationSchema,
25930
- targetPathFromCwd: import_mini85.z.optional(import_mini85.z.string()),
25931
- frontmatter: import_mini85.z.optional(import_mini85.z.unknown()),
25932
- body: import_mini85.z.optional(import_mini85.z.string()),
25933
- otherFiles: import_mini85.z.optional(import_mini85.z.array(skillFileSchema)),
25934
- content: import_mini85.z.optional(import_mini85.z.string()),
25935
- generateOptions: import_mini85.z.optional(generateOptionsSchema),
25936
- importOptions: import_mini85.z.optional(importOptionsSchema)
27500
+ targetPathFromCwd: import_mini89.z.optional(import_mini89.z.string()),
27501
+ frontmatter: import_mini89.z.optional(import_mini89.z.unknown()),
27502
+ body: import_mini89.z.optional(import_mini89.z.string()),
27503
+ otherFiles: import_mini89.z.optional(import_mini89.z.array(skillFileSchema)),
27504
+ content: import_mini89.z.optional(import_mini89.z.string()),
27505
+ generateOptions: import_mini89.z.optional(generateOptionsSchema),
27506
+ importOptions: import_mini89.z.optional(importOptionsSchema)
25937
27507
  });
25938
27508
  var supportedOperationsByFeature = {
25939
27509
  rule: ["list", "get", "put", "delete"],
@@ -25942,6 +27512,8 @@ var supportedOperationsByFeature = {
25942
27512
  skill: ["list", "get", "put", "delete"],
25943
27513
  ignore: ["get", "put", "delete"],
25944
27514
  mcp: ["get", "put", "delete"],
27515
+ permissions: ["get", "put", "delete"],
27516
+ hooks: ["get", "put", "delete"],
25945
27517
  generate: ["run"],
25946
27518
  import: ["run"]
25947
27519
  };
@@ -25991,7 +27563,7 @@ function ensureBody({ body, feature, operation }) {
25991
27563
  }
25992
27564
  var rulesyncTool = {
25993
27565
  name: "rulesyncTool",
25994
- description: "Manage Rulesync files through a single MCP tool. Features: rule/command/subagent/skill support list/get/put/delete; ignore/mcp support get/put/delete only; generate supports run only; import supports run only. Parameters: list requires no targetPathFromCwd (lists all items); get/delete require targetPathFromCwd; put requires targetPathFromCwd, frontmatter, and body (or content for ignore/mcp); generate/run uses generateOptions to configure generation; import/run uses importOptions to configure import.",
27566
+ description: "Manage Rulesync files through a single MCP tool. Features: rule/command/subagent/skill support list/get/put/delete; ignore/mcp/permissions/hooks support get/put/delete only; generate supports run only; import supports run only. Parameters: list requires no targetPathFromCwd (lists all items); get/delete require targetPathFromCwd; put requires targetPathFromCwd, frontmatter, and body (or content for ignore/mcp/permissions/hooks); generate/run uses generateOptions to configure generation; import/run uses importOptions to configure import.",
25995
27567
  parameters: rulesyncToolSchema,
25996
27568
  execute: async (args) => {
25997
27569
  const parsed = rulesyncToolSchema.parse(args);
@@ -26108,6 +27680,30 @@ var rulesyncTool = {
26108
27680
  }
26109
27681
  return mcpTools.deleteMcpFile.execute();
26110
27682
  }
27683
+ case "permissions": {
27684
+ if (parsed.operation === "get") {
27685
+ return permissionsTools.getPermissionsFile.execute();
27686
+ }
27687
+ if (parsed.operation === "put") {
27688
+ if (!parsed.content) {
27689
+ throw new Error("content is required for permissions put operation");
27690
+ }
27691
+ return permissionsTools.putPermissionsFile.execute({ content: parsed.content });
27692
+ }
27693
+ return permissionsTools.deletePermissionsFile.execute();
27694
+ }
27695
+ case "hooks": {
27696
+ if (parsed.operation === "get") {
27697
+ return hooksTools.getHooksFile.execute();
27698
+ }
27699
+ if (parsed.operation === "put") {
27700
+ if (!parsed.content) {
27701
+ throw new Error("content is required for hooks put operation");
27702
+ }
27703
+ return hooksTools.putHooksFile.execute({ content: parsed.content });
27704
+ }
27705
+ return hooksTools.deleteHooksFile.execute();
27706
+ }
26111
27707
  case "generate": {
26112
27708
  return generateTools.executeGenerate.execute(parsed.generateOptions ?? {});
26113
27709
  }
@@ -26140,7 +27736,7 @@ async function mcpCommand(logger5, { version }) {
26140
27736
  }
26141
27737
 
26142
27738
  // src/cli/commands/resolve-gitignore-targets.ts
26143
- var import_node_path154 = require("path");
27739
+ var import_node_path162 = require("path");
26144
27740
  var resolveGitignoreTargets = async ({
26145
27741
  cliTargets,
26146
27742
  cwd = process.cwd()
@@ -26148,8 +27744,8 @@ var resolveGitignoreTargets = async ({
26148
27744
  if (cliTargets !== void 0) {
26149
27745
  return cliTargets;
26150
27746
  }
26151
- const baseConfigPath = (0, import_node_path154.join)(cwd, RULESYNC_CONFIG_RELATIVE_FILE_PATH);
26152
- const localConfigPath = (0, import_node_path154.join)(cwd, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH);
27747
+ const baseConfigPath = (0, import_node_path162.join)(cwd, RULESYNC_CONFIG_RELATIVE_FILE_PATH);
27748
+ const localConfigPath = (0, import_node_path162.join)(cwd, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH);
26153
27749
  const [hasBase, hasLocal] = await Promise.all([
26154
27750
  fileExists(baseConfigPath),
26155
27751
  fileExists(localConfigPath)
@@ -26570,7 +28166,7 @@ function wrapCommand({
26570
28166
  }
26571
28167
 
26572
28168
  // src/cli/index.ts
26573
- var getVersion = () => "8.6.0";
28169
+ var getVersion = () => "8.7.0";
26574
28170
  function wrapCommand2(name, errorCode, handler) {
26575
28171
  return wrapCommand({ name, errorCode, handler, getVersion });
26576
28172
  }
@@ -26636,12 +28232,18 @@ var main = async () => {
26636
28232
  await mcpCommand(logger5, { version });
26637
28233
  })
26638
28234
  );
26639
- program.command("install").description("Install skills from declarative sources in rulesync.jsonc").option("--update", "Force re-resolve all source refs, ignoring lockfile").option(
28235
+ program.command("install").description("Install skills/primitives from declarative sources (rulesync.jsonc) or apm.yml").option(
28236
+ "--mode <mode>",
28237
+ `Install layout to produce (${INSTALL_MODES.join("|")}). Default: rulesync`
28238
+ ).option("--update", "Force re-resolve all source refs, ignoring lockfile").option(
26640
28239
  "--frozen",
26641
28240
  "Fail if lockfile is missing or out of sync (for CI); fetches missing skills using locked refs"
26642
28241
  ).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(
26643
28242
  wrapCommand2("install", "INSTALL_FAILED", async (logger5, options) => {
28243
+ const rawMode = options.mode;
28244
+ const mode = parseInstallMode(rawMode);
26644
28245
  await installCommand(logger5, {
28246
+ mode,
26645
28247
  // eslint-disable-next-line no-type-assertion/no-type-assertion
26646
28248
  update: options.update,
26647
28249
  // eslint-disable-next-line no-type-assertion/no-type-assertion
@@ -26690,6 +28292,14 @@ var main = async () => {
26690
28292
  );
26691
28293
  program.parse();
26692
28294
  };
28295
+ function parseInstallMode(raw) {
28296
+ if (raw === void 0) return void 0;
28297
+ const match = INSTALL_MODES.find((m) => m === raw);
28298
+ if (!match) {
28299
+ throw new Error(`Invalid --mode value "${raw}". Expected one of: ${INSTALL_MODES.join(", ")}.`);
28300
+ }
28301
+ return match;
28302
+ }
26693
28303
  main().catch((error) => {
26694
28304
  console.error(formatError(error));
26695
28305
  process.exit(1);