rulesync 36.0.2 → 37.0.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.
@@ -4040,7 +4040,7 @@ function isPlainObject$1(value) {
4040
4040
  /**
4041
4041
  * Type guard to check if a value is an array of strings.
4042
4042
  */
4043
- function isStringArray$2(value) {
4043
+ function isStringArray$1(value) {
4044
4044
  return Array.isArray(value) && value.every((item) => typeof item === "string");
4045
4045
  }
4046
4046
  //#endregion
@@ -12835,6 +12835,10 @@ const SHARED_CONFIG_OWNERSHIP = {
12835
12835
  kind: "replace-owned-keys",
12836
12836
  ownedKeys: ["disableAllHooks"]
12837
12837
  },
12838
+ mcp: {
12839
+ kind: "replace-owned-keys",
12840
+ ownedKeys: ["disabledMcpServers"]
12841
+ },
12838
12842
  permissions: {
12839
12843
  kind: "replace-owned-keys",
12840
12844
  ownedKeys: ["deniedUrls"]
@@ -12849,6 +12853,10 @@ const SHARED_CONFIG_OWNERSHIP = {
12849
12853
  kind: "replace-owned-keys",
12850
12854
  ownedKeys: ["disableAllHooks"]
12851
12855
  },
12856
+ mcp: {
12857
+ kind: "replace-owned-keys",
12858
+ ownedKeys: ["disabledMcpServers"]
12859
+ },
12852
12860
  permissions: {
12853
12861
  kind: "replace-owned-keys",
12854
12862
  ownedKeys: ["allowedUrls", "deniedUrls"]
@@ -22113,7 +22121,7 @@ function getOpencodeCommandEntries({ config, layout = OPENCODE_LAYOUT }) {
22113
22121
  */
22114
22122
  function getOpencodeSkillPaths({ config, layout = OPENCODE_LAYOUT }) {
22115
22123
  if (Array.isArray(config.skills)) {
22116
- if (!layout.readsV2ConfigSpellings || !isStringArray$2(config.skills)) return [];
22124
+ if (!layout.readsV2ConfigSpellings || !isStringArray$1(config.skills)) return [];
22117
22125
  return config.skills.filter((value) => !/^https?:\/\//i.test(value));
22118
22126
  }
22119
22127
  const skills = asOpencodeEntries(config.skills);
@@ -26359,7 +26367,7 @@ const isBooleanValue = ({ value }) => typeof value === "boolean";
26359
26367
  const isFiniteNumber = ({ value }) => Number.isFinite(value);
26360
26368
  const isNonEmptyString = (value) => typeof value === "string" && value !== "";
26361
26369
  const isEmittableString = ({ value }) => isNonEmptyString(value);
26362
- const isEmittableArray = ({ value }) => isStringArray$1(value);
26370
+ const isEmittableArray = ({ value }) => isStringArray(value);
26363
26371
  const isEmittableRecord = ({ value }) => isSafeStringRecord(value);
26364
26372
  const isImportableArray = ({ value }) => isSafeStringArray(value);
26365
26373
  const isEmittableObject = ({ value }) => isPlainObject$1(value);
@@ -26801,7 +26809,7 @@ function isStringRecord(value) {
26801
26809
  if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
26802
26810
  return Object.values(value).every((v) => typeof v === "string");
26803
26811
  }
26804
- function isStringArray$1(value) {
26812
+ function isStringArray(value) {
26805
26813
  return Array.isArray(value) && value.every((v) => typeof v === "string");
26806
26814
  }
26807
26815
  /** Compare object values without letting key order decide the answer. */
@@ -26846,7 +26854,7 @@ function isSafeEnvEntry(entry) {
26846
26854
  * skipped wholesale when that read fails.
26847
26855
  */
26848
26856
  function isSafeStringArray(value) {
26849
- return isStringArray$1(value) && value.every((entry) => !CONTROL_CHARS.some((char) => entry.includes(char)));
26857
+ return isStringArray(value) && value.every((entry) => !CONTROL_CHARS.some((char) => entry.includes(char)));
26850
26858
  }
26851
26859
  /**
26852
26860
  * A raw string kept only if the canonical field it would land in accepts it,
@@ -26888,7 +26896,7 @@ function importTypePayloadFields({ h, hookType, warn }) {
26888
26896
  return {
26889
26897
  ...url !== void 0 && { url },
26890
26898
  ...isStringRecord(headers) && { headers },
26891
- ...isStringArray$1(h.allowedEnvVars) && { allowedEnvVars: h.allowedEnvVars }
26899
+ ...isStringArray(h.allowedEnvVars) && { allowedEnvVars: h.allowedEnvVars }
26892
26900
  };
26893
26901
  }
26894
26902
  if (hookType === "mcp_tool") {
@@ -28049,7 +28057,8 @@ function mergeMatcherGroups({ existing, generated, event, seen, previouslyOwned,
28049
28057
  logger
28050
28058
  })).map((handler) => cloneHandler(handler));
28051
28059
  if (preserved.length === 0) continue;
28052
- const target = merged.find((candidate) => isMatcherGroup(candidate) && matcherKey(candidate.matcher) === matcher);
28060
+ const attributes = groupAttributesKey(group);
28061
+ const target = merged.find((candidate) => isMatcherGroup(candidate) && matcherKey(candidate.matcher) === matcher && groupAttributesKey(candidate) === attributes);
28053
28062
  if (target !== void 0 && isMatcherGroup(target)) target.hooks.push(...preserved);
28054
28063
  else merged.push({
28055
28064
  ...omitPrototypePollutionKeys(group),
@@ -28170,6 +28179,11 @@ function stableStringify$1(value) {
28170
28179
  function matcherKey(matcher) {
28171
28180
  return matcher === void 0 ? "" : stableStringify$1(matcher);
28172
28181
  }
28182
+ /** The stable form of a matcher group's keys other than `matcher` and `hooks`. */
28183
+ function groupAttributesKey(group) {
28184
+ const { matcher: _matcher, hooks: _hooks, ...attributes } = group;
28185
+ return stableStringify$1(attributes);
28186
+ }
28173
28187
  function warnSkip({ logger, event, expected }) {
28174
28188
  logger?.warn(`Skipping existing hook entry on ${event}: expected ${expected}`);
28175
28189
  }
@@ -28665,7 +28679,7 @@ const MANAGED_EVENT_NAMES = new Set(Object.values(CANONICAL_TO_CLINE_EVENT_NAMES
28665
28679
  function parseManifest(fileContent) {
28666
28680
  try {
28667
28681
  const parsed = JSON.parse(fileContent);
28668
- if (!isRecord$1(parsed) || !isStringArray$2(parsed.events)) return null;
28682
+ if (!isRecord$1(parsed) || !isStringArray$1(parsed.events)) return null;
28669
28683
  return {
28670
28684
  generatedBy: "rulesync",
28671
28685
  events: parsed.events.filter((event) => MANAGED_EVENT_NAMES.has(event))
@@ -30203,7 +30217,7 @@ function copilotCliHooksToCanonical(rawHooks, logger) {
30203
30217
  * see the CLI config dir reference:
30204
30218
  * https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
30205
30219
  */
30206
- function copilotCliSettingsPaths({ global }) {
30220
+ function copilotCliSettingsPaths$1({ global }) {
30207
30221
  return global ? {
30208
30222
  relativeDirPath: COPILOT_DIR,
30209
30223
  relativeFilePath: COPILOTCLI_SETTINGS_FILE_NAME
@@ -30218,7 +30232,7 @@ function copilotCliSettingsPaths({ global }) {
30218
30232
  * subject, and the permissions feature reports a broken settings file itself.
30219
30233
  */
30220
30234
  async function readDisableAllHooks({ outputRoot, global }) {
30221
- const paths = copilotCliSettingsPaths({ global });
30235
+ const paths = copilotCliSettingsPaths$1({ global });
30222
30236
  const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
30223
30237
  if (fileContent === null) return;
30224
30238
  try {
@@ -30248,7 +30262,7 @@ var CopilotcliHooksSettings = class CopilotcliHooksSettings extends ToolFile {
30248
30262
  return false;
30249
30263
  }
30250
30264
  static async fromDisableAllHooks({ outputRoot = process.cwd(), global = false, disableAllHooks }) {
30251
- const paths = copilotCliSettingsPaths({ global });
30265
+ const paths = copilotCliSettingsPaths$1({ global });
30252
30266
  const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
30253
30267
  const existingContent = await readFileContentOrNull(filePath) ?? "{}";
30254
30268
  return new CopilotcliHooksSettings({
@@ -30278,7 +30292,7 @@ var CopilotcliHooks = class CopilotcliHooks extends ToolHooks {
30278
30292
  this.disableAllHooks = disableAllHooks;
30279
30293
  }
30280
30294
  static getExtraSharedWritePaths({ global = false } = {}) {
30281
- return [copilotCliSettingsPaths({ global })];
30295
+ return [copilotCliSettingsPaths$1({ global })];
30282
30296
  }
30283
30297
  static async getAuxiliaryFiles({ toolHooks } = {}) {
30284
30298
  return toolHooks instanceof CopilotcliHooks && toolHooks.settingsFile ? [toolHooks.settingsFile] : [];
@@ -34722,12 +34736,21 @@ var QwencodeHooks = class QwencodeHooks extends ToolHooks {
34722
34736
  validate
34723
34737
  });
34724
34738
  }
34725
- static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
34739
+ static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, preserveUnowned = false, logger }) {
34726
34740
  const paths = QwencodeHooks.getSettablePaths({ global });
34727
34741
  const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
34728
34742
  const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
34729
34743
  const config = rulesyncHooks.getJson();
34730
- const patch = { hooks: canonicalToQwencodeHooks(config, logger) };
34744
+ const previouslyOwned = preserveUnowned ? parseHooksOwnershipLock(await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, HOOKS_OWNERSHIP_LOCK_FILE_NAME))) : void 0;
34745
+ const merged = mergeGeneratedHookLists({
34746
+ existingContent,
34747
+ generatedHooks: canonicalToQwencodeHooks(config, logger),
34748
+ shape: "matcher-groups",
34749
+ preserveUnowned,
34750
+ previouslyOwned,
34751
+ logger
34752
+ });
34753
+ const patch = { hooks: merged.hooks };
34731
34754
  const disableAllHooks = config.qwencode?.disableAllHooks;
34732
34755
  if (typeof disableAllHooks === "boolean") patch.disableAllHooks = disableAllHooks;
34733
34756
  const fileContent = applySharedConfigPatch({
@@ -34742,9 +34765,16 @@ var QwencodeHooks = class QwencodeHooks extends ToolHooks {
34742
34765
  relativeDirPath: paths.relativeDirPath,
34743
34766
  relativeFilePath: paths.relativeFilePath,
34744
34767
  fileContent,
34768
+ ownedHookRefs: preserveUnowned ? merged.owned : void 0,
34745
34769
  validate
34746
34770
  });
34747
34771
  }
34772
+ static supportsPreserveUnowned() {
34773
+ return true;
34774
+ }
34775
+ static async getAuxiliaryFiles({ toolHooks } = {}) {
34776
+ return toolHooks instanceof QwencodeHooks ? toolHooks.getOwnershipLockFiles() : [];
34777
+ }
34748
34778
  toRulesyncHooks() {
34749
34779
  let settings;
34750
34780
  try {
@@ -41144,7 +41174,7 @@ function isCodexStdioServer(config) {
41144
41174
  * @see https://learn.chatgpt.com/docs/extend/mcp
41145
41175
  */
41146
41176
  function isValidRenamedArray(key, value) {
41147
- return key === "env_vars" || key === "envVars" ? isEnvVarEntryArray(value) : isStringArray$2(value);
41177
+ return key === "env_vars" || key === "envVars" ? isEnvVarEntryArray(value) : isStringArray$1(value);
41148
41178
  }
41149
41179
  /**
41150
41180
  * The keys of Codex's `McpServerOAuthConfig`. Anything else in a server's
@@ -42363,18 +42393,6 @@ function renameEnabledToolsToTools({ server, serverName, logger }) {
42363
42393
  };
42364
42394
  }
42365
42395
  /**
42366
- * Copilot CLI's `tools` accepts more than the canonical `enabledTools` shape:
42367
- * `--tools` documents `*`, a comma-separated list, or `""`, so a hand-written or
42368
- * CLI-written entry can be a bare string. Canonical `enabledTools` is
42369
- * `string[]`, and `RulesyncMcp` throws on a schema failure — carrying a string
42370
- * through would reject the WHOLE imported MCP file rather than one key. Anything
42371
- * that is not a string array is therefore left under `tools` untouched, matching
42372
- * `codexcli-mcp.ts`'s `isValidRenamedArray` guard on the same class of rename.
42373
- */
42374
- function isStringArray(value) {
42375
- return Array.isArray(value) && value.every((entry) => typeof entry === "string");
42376
- }
42377
- /**
42378
42396
  * Resolves and sets the transport type for each MCP server config.
42379
42397
  * GitHub Copilot CLI requires the "type" field for each server. A server it
42380
42398
  * cannot express — one that names no transport, or names one it carries no way
@@ -42416,7 +42434,7 @@ function addTypeField(mcpServers, logger) {
42416
42434
  });
42417
42435
  continue;
42418
42436
  }
42419
- const { httpUrl: _httpUrl, ...rest } = parsed;
42437
+ const { httpUrl: _httpUrl, disabled: _disabled, ...rest } = parsed;
42420
42438
  result[name] = {
42421
42439
  ...renameEnabledToolsToTools({
42422
42440
  server: rest,
@@ -42428,7 +42446,7 @@ function addTypeField(mcpServers, logger) {
42428
42446
  };
42429
42447
  continue;
42430
42448
  }
42431
- const { httpUrl: _localHttpUrl, ...local } = parsed;
42449
+ const { httpUrl: _localHttpUrl, disabled: _localDisabled, ...local } = parsed;
42432
42450
  const [head, ...tail] = resolveLocalMcpCommand(parsed);
42433
42451
  if (head === void 0) {
42434
42452
  warnAndSkipMcpServer({
@@ -42459,7 +42477,7 @@ function removeTypeField(config) {
42459
42477
  const result = {};
42460
42478
  for (const [name, server] of Object.entries(config.mcpServers ?? {})) {
42461
42479
  const { tools, ...withoutTools } = server;
42462
- const restored = isStringArray(tools) ? {
42480
+ const restored = isStringArray$1(tools) ? {
42463
42481
  ...withoutTools,
42464
42482
  enabledTools: tools
42465
42483
  } : {
@@ -42475,6 +42493,99 @@ function removeTypeField(config) {
42475
42493
  }
42476
42494
  return result;
42477
42495
  }
42496
+ /**
42497
+ * The Copilot CLI settings file carrying `disabledMcpServers`:
42498
+ * `.github/copilot/settings.json` (project) / `~/.copilot/settings.json`
42499
+ * (global). Both scopes document the key ("MCP server names to disable. Listed
42500
+ * servers are configured but not started"); at repository scope it is
42501
+ * union-merged with the user value, so a repository can add names but never
42502
+ * remove them.
42503
+ *
42504
+ * @see https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference
42505
+ */
42506
+ function copilotCliSettingsPaths({ global }) {
42507
+ return global ? {
42508
+ relativeDirPath: COPILOT_DIR,
42509
+ relativeFilePath: COPILOTCLI_SETTINGS_FILE_NAME
42510
+ } : {
42511
+ relativeDirPath: COPILOTCLI_PROJECT_SETTINGS_DIR_PATH,
42512
+ relativeFilePath: COPILOTCLI_SETTINGS_FILE_NAME
42513
+ };
42514
+ }
42515
+ async function readCopilotCliSettings({ outputRoot, global }) {
42516
+ const paths = copilotCliSettingsPaths({ global });
42517
+ const existingContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "";
42518
+ try {
42519
+ return {
42520
+ paths,
42521
+ existingContent,
42522
+ document: parseSharedConfig({
42523
+ format: "json",
42524
+ fileContent: existingContent,
42525
+ filePath: join(paths.relativeDirPath, paths.relativeFilePath),
42526
+ invalidRootPolicy: "error"
42527
+ })
42528
+ };
42529
+ } catch (error) {
42530
+ return {
42531
+ paths,
42532
+ existingContent,
42533
+ document: null,
42534
+ error
42535
+ };
42536
+ }
42537
+ }
42538
+ /**
42539
+ * The names of the servers rulesync actually writes for this target: one
42540
+ * `addTypeField` skips (no transport, WebSocket, missing url or command) is
42541
+ * not managed, so its name neither gains a toggle nor strips a same-named
42542
+ * user-authored entry from the settings file.
42543
+ */
42544
+ function managedServerNames(mcpServers) {
42545
+ return Object.keys(addTypeField(mcpServers) ?? {});
42546
+ }
42547
+ /**
42548
+ * Overlays `disabled: true` on the servers the scope's settings file lists in
42549
+ * `disabledMcpServers`, so import round-trips the toggle into the canonical
42550
+ * config. A settings file that exists but cannot be parsed throws
42551
+ * (fail-closed): importing past it would re-enable every disabled server.
42552
+ */
42553
+ async function overlayDisabledServers({ mcpServers, outputRoot, global }) {
42554
+ const settings = await readCopilotCliSettings({
42555
+ outputRoot,
42556
+ global
42557
+ });
42558
+ if (settings.document === null) throw new Error(`Cannot import Copilot CLI MCP servers: ${formatError(settings.error)}. Fix the file so disabledMcpServers can be read.`, { cause: settings.error });
42559
+ const disabled = settings.document.disabledMcpServers;
42560
+ if (!isStringArray$1(disabled) || disabled.length === 0) return mcpServers;
42561
+ const servers = { ...mcpServers };
42562
+ for (const name of disabled) {
42563
+ if (!Object.hasOwn(servers, name)) continue;
42564
+ const server = servers[name];
42565
+ if (isPlainObject$1(server)) servers[name] = {
42566
+ ...server,
42567
+ disabled: true
42568
+ };
42569
+ }
42570
+ return servers;
42571
+ }
42572
+ /**
42573
+ * Auxiliary writer for `disabledMcpServers` in the Copilot CLI settings file.
42574
+ * Only that key is owned (through the shared-config gateway), so `deniedUrls`,
42575
+ * `disableAllHooks`, `model` and every other setting are preserved. It is a
42576
+ * shared, user-owned file and is never deleted.
42577
+ */
42578
+ var CopilotcliMcpSettings = class extends ToolFile {
42579
+ validate() {
42580
+ return {
42581
+ success: true,
42582
+ error: null
42583
+ };
42584
+ }
42585
+ isDeletable() {
42586
+ return false;
42587
+ }
42588
+ };
42478
42589
  var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
42479
42590
  json;
42480
42591
  constructor(params) {
@@ -42517,7 +42628,11 @@ var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
42517
42628
  const json = JSON.parse(fileContent);
42518
42629
  const newJson = {
42519
42630
  ...json,
42520
- mcpServers: json.mcpServers ?? {}
42631
+ mcpServers: await overlayDisabledServers({
42632
+ mcpServers: json.mcpServers ?? {},
42633
+ outputRoot,
42634
+ global
42635
+ })
42521
42636
  };
42522
42637
  return new CopilotcliMcp({
42523
42638
  outputRoot,
@@ -42532,7 +42647,15 @@ var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
42532
42647
  const paths = this.getSettablePaths({ global });
42533
42648
  const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? JSON.stringify({ mcpServers: {} }, null, 2);
42534
42649
  const json = JSON.parse(fileContent);
42535
- const copilotCliMcpServers = addTypeField(rulesyncMcp.getMcpServers(), logger);
42650
+ const settings = await readCopilotCliSettings({
42651
+ outputRoot,
42652
+ global
42653
+ });
42654
+ const copilotCliMcpServers = addTypeField(Object.fromEntries(Object.entries(rulesyncMcp.getMcpServers()).filter(([name, server]) => {
42655
+ if (server.disabled !== true || settings.document !== null) return true;
42656
+ logger?.warn(`[CopilotcliMcp] Skipping disabled MCP server "${name}": ${join(settings.paths.relativeDirPath, settings.paths.relativeFilePath)} cannot be parsed, so disabledMcpServers cannot be written to switch it off.`);
42657
+ return false;
42658
+ })), logger);
42536
42659
  const mcpJson = {
42537
42660
  ...json,
42538
42661
  mcpServers: copilotCliMcpServers
@@ -42546,6 +42669,61 @@ var CopilotcliMcp = class CopilotcliMcp extends ToolMcp {
42546
42669
  global
42547
42670
  });
42548
42671
  }
42672
+ /**
42673
+ * `disabledMcpServers` lives in the Copilot CLI settings file the hooks and
42674
+ * permissions features also write. Declared here so the write-order
42675
+ * derivation sees this feature as one of that file's writers — it is not a
42676
+ * settable path, since the servers themselves live in the MCP file.
42677
+ */
42678
+ static getExtraSharedWritePaths({ global = false } = {}) {
42679
+ return [copilotCliSettingsPaths({ global })];
42680
+ }
42681
+ /**
42682
+ * Routes the canonical `disabled: true` to `disabledMcpServers`, the key
42683
+ * Copilot CLI actually consults: an MCP file entry has no `disabled` field.
42684
+ * rulesync owns the toggle for the servers it writes; names it does not
42685
+ * manage keep their existing state. The file is not created or touched when
42686
+ * there is nothing to disable and nothing to retract.
42687
+ */
42688
+ static async getAuxiliaryFiles({ outputRoot = process.cwd(), global = false, rulesyncMcp, logger }) {
42689
+ const servers = rulesyncMcp.forTarget({
42690
+ toolTarget: "copilotcli",
42691
+ global
42692
+ }).getMcpServers();
42693
+ const managedNames = managedServerNames(servers);
42694
+ const disabledNames = managedNames.filter((name) => servers[name]?.disabled === true);
42695
+ const settings = await readCopilotCliSettings({
42696
+ outputRoot,
42697
+ global
42698
+ });
42699
+ if (settings.document === null) {
42700
+ logger?.warn(`Skipping the Copilot CLI disabledMcpServers update: ${formatError(settings.error)}`);
42701
+ return [];
42702
+ }
42703
+ const existing = settings.document.disabledMcpServers;
42704
+ const existingDisabled = isStringArray$1(existing) ? existing : [];
42705
+ const managedNameSet = new Set(managedNames);
42706
+ const reEnabled = existingDisabled.filter((name) => managedNameSet.has(name) && !disabledNames.includes(name));
42707
+ if (reEnabled.length > 0) logger?.warn(`Copilot CLI MCP: re-enabling ${reEnabled.join(", ")} — the canonical config does not mark ${reEnabled.length === 1 ? "it" : "them"} disabled. Set "disabled": true in .rulesync/mcp.jsonc to keep a managed server off.`);
42708
+ const mergedDisabled = [...existingDisabled.filter((name) => !managedNameSet.has(name)), ...disabledNames].toSorted();
42709
+ if (mergedDisabled.length === 0 && existing === void 0) return [];
42710
+ if (isStringArray$1(existing) && existing.length === mergedDisabled.length && existing.toSorted().every((name, index) => name === mergedDisabled[index])) return [];
42711
+ const { paths } = settings;
42712
+ const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
42713
+ return [new CopilotcliMcpSettings({
42714
+ outputRoot,
42715
+ relativeDirPath: paths.relativeDirPath,
42716
+ relativeFilePath: paths.relativeFilePath,
42717
+ fileContent: applySharedConfigPatch({
42718
+ fileKey: sharedConfigFileKey(paths),
42719
+ feature: "mcp",
42720
+ existingContent: settings.existingContent,
42721
+ patch: { disabledMcpServers: mergedDisabled.length > 0 ? mergedDisabled : void 0 },
42722
+ filePath
42723
+ }),
42724
+ global
42725
+ })];
42726
+ }
42549
42727
  toRulesyncMcp() {
42550
42728
  const mcpServers = removeTypeField(this.json);
42551
42729
  return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers }, null, 2) });
@@ -42977,10 +43155,10 @@ function convertFromCrushFormat(crushServers) {
42977
43155
  if (value === "http" || value === "sse") converted.type = value;
42978
43156
  break;
42979
43157
  case "disabled_tools":
42980
- if (isStringArray$2(value)) converted.disabledTools = value;
43158
+ if (isStringArray$1(value)) converted.disabledTools = value;
42981
43159
  break;
42982
43160
  case "enabled_tools":
42983
- if (isStringArray$2(value)) converted.enabledTools = value;
43161
+ if (isStringArray$1(value)) converted.enabledTools = value;
42984
43162
  break;
42985
43163
  case "oauth_token": break;
42986
43164
  default: {
@@ -43967,7 +44145,7 @@ function convertRowsFromDsh(rows, logger) {
43967
44145
  } else {
43968
44146
  server.type = "stdio";
43969
44147
  if (typeof config.command === "string") server.command = config.command;
43970
- if (isStringArray$2(config.args)) server.args = config.args;
44148
+ if (isStringArray$1(config.args)) server.args = config.args;
43971
44149
  const env = copyStringRecord(config.env);
43972
44150
  if (env !== void 0) server.env = env;
43973
44151
  if (typeof config.cwd === "string") server.cwd = config.cwd;
@@ -44398,11 +44576,11 @@ function applyGooseStdioFields(ext, config) {
44398
44576
  if (Array.isArray(command)) {
44399
44577
  if (typeof command[0] === "string") ext.cmd = command[0];
44400
44578
  const rest = command.slice(1).filter((c) => typeof c === "string");
44401
- const args = isStringArray$2(config.args) ? config.args : [];
44579
+ const args = isStringArray$1(config.args) ? config.args : [];
44402
44580
  if (rest.length > 0 || args.length > 0) ext.args = [...rest, ...args];
44403
44581
  } else if (typeof command === "string") {
44404
44582
  ext.cmd = command;
44405
- if (isStringArray$2(config.args)) ext.args = config.args;
44583
+ if (isStringArray$1(config.args)) ext.args = config.args;
44406
44584
  }
44407
44585
  if (isPlainObject$1(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
44408
44586
  if (typeof config.cwd === "string") ext.cwd = config.cwd;
@@ -44505,7 +44683,7 @@ function convertFromGooseFormat(extensions) {
44505
44683
  else if (type === "streamable_http") server.type = "http";
44506
44684
  else server.type = "stdio";
44507
44685
  if (typeof ext.cmd === "string") server.command = ext.cmd;
44508
- if (isStringArray$2(ext.args)) server.args = ext.args;
44686
+ if (isStringArray$1(ext.args)) server.args = ext.args;
44509
44687
  if (isPlainObject$1(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
44510
44688
  if (typeof ext.cwd === "string") server.cwd = ext.cwd;
44511
44689
  if (typeof ext.uri === "string") server.url = ext.uri;
@@ -44527,11 +44705,11 @@ function buildGoosePluginStdioServer(config) {
44527
44705
  if (Array.isArray(command)) {
44528
44706
  if (typeof command[0] === "string") server.command = command[0];
44529
44707
  const rest = command.slice(1).filter((c) => typeof c === "string");
44530
- const args = isStringArray$2(config.args) ? config.args : [];
44708
+ const args = isStringArray$1(config.args) ? config.args : [];
44531
44709
  if (rest.length > 0 || args.length > 0) server.args = [...rest, ...args];
44532
44710
  } else if (typeof command === "string") {
44533
44711
  server.command = command;
44534
- if (isStringArray$2(config.args)) server.args = config.args;
44712
+ if (isStringArray$1(config.args)) server.args = config.args;
44535
44713
  }
44536
44714
  if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
44537
44715
  if (typeof config.cwd === "string") server.cwd = config.cwd;
@@ -44939,7 +45117,7 @@ function copyHermesOauth(source) {
44939
45117
  if (!isRecord$1(source)) return;
44940
45118
  const oauth = {};
44941
45119
  for (const key of HERMES_OAUTH_STRING_KEYS) if (typeof source[key] === "string") oauth[key] = source[key];
44942
- if (typeof oauth.scope !== "string" && isStringArray$2(source.scopes)) oauth.scope = source.scopes.join(" ");
45120
+ if (typeof oauth.scope !== "string" && isStringArray$1(source.scopes)) oauth.scope = source.scopes.join(" ");
44943
45121
  for (const key of ["redirect_port", "timeout"]) if (typeof source[key] === "number") oauth[key] = source[key];
44944
45122
  if (typeof source.cimd === "boolean") oauth.cimd = source.cimd;
44945
45123
  return Object.keys(oauth).length > 0 ? oauth : void 0;
@@ -44966,7 +45144,7 @@ function copyHermesAdvancedFields(source, target) {
44966
45144
  target.auth = source.auth;
44967
45145
  copied = true;
44968
45146
  }
44969
- if (typeof source.client_cert === "string" || isStringArray$2(source.client_cert)) {
45147
+ if (typeof source.client_cert === "string" || isStringArray$1(source.client_cert)) {
44970
45148
  target.client_cert = source.client_cert;
44971
45149
  copied = true;
44972
45150
  }
@@ -45038,8 +45216,8 @@ function copyHermesAdvancedFields(source, target) {
45038
45216
  */
45039
45217
  function buildHermesToolsBlock(config) {
45040
45218
  const tools = {};
45041
- if (isStringArray$2(config.enabledTools)) tools.include = config.enabledTools;
45042
- if (isStringArray$2(config.disabledTools)) tools.exclude = config.disabledTools;
45219
+ if (isStringArray$1(config.enabledTools)) tools.include = config.enabledTools;
45220
+ if (isStringArray$1(config.disabledTools)) tools.exclude = config.disabledTools;
45043
45221
  if (typeof config.promptsEnabled === "boolean") tools.prompts = config.promptsEnabled;
45044
45222
  if (typeof config.resourcesEnabled === "boolean") tools.resources = config.resourcesEnabled;
45045
45223
  return tools;
@@ -45051,8 +45229,8 @@ function buildHermesToolsBlock(config) {
45051
45229
  * `promptsEnabled`/`resourcesEnabled` top-level toggles.
45052
45230
  */
45053
45231
  function applyHermesToolsBlock(hermesTools, server) {
45054
- if (isStringArray$2(hermesTools.include)) server.enabledTools = hermesTools.include;
45055
- if (isStringArray$2(hermesTools.exclude)) server.disabledTools = hermesTools.exclude;
45232
+ if (isStringArray$1(hermesTools.include)) server.enabledTools = hermesTools.include;
45233
+ if (isStringArray$1(hermesTools.exclude)) server.disabledTools = hermesTools.exclude;
45056
45234
  if (typeof hermesTools.prompts === "boolean") server.promptsEnabled = hermesTools.prompts;
45057
45235
  if (typeof hermesTools.resources === "boolean") server.resourcesEnabled = hermesTools.resources;
45058
45236
  }
@@ -45078,11 +45256,11 @@ function convertServerToHermes(config) {
45078
45256
  if (Array.isArray(command)) {
45079
45257
  if (typeof command[0] === "string") out.command = command[0];
45080
45258
  const rest = command.slice(1).filter((c) => typeof c === "string");
45081
- const args = isStringArray$2(config.args) ? config.args : [];
45259
+ const args = isStringArray$1(config.args) ? config.args : [];
45082
45260
  if (rest.length > 0 || args.length > 0) out.args = [...rest, ...args];
45083
45261
  } else if (typeof command === "string") {
45084
45262
  out.command = command;
45085
- if (isStringArray$2(config.args)) out.args = config.args;
45263
+ if (isStringArray$1(config.args)) out.args = config.args;
45086
45264
  }
45087
45265
  if (isPlainObject$1(config.env)) out.env = omitPrototypePollutionKeys(config.env);
45088
45266
  } else if (url !== void 0) {
@@ -45132,7 +45310,7 @@ function convertFromHermesFormat(mcpServers) {
45132
45310
  if (PROTOTYPE_POLLUTION_KEYS.has(name) || !isRecord$1(config)) continue;
45133
45311
  const server = {};
45134
45312
  if (typeof config.command === "string") server.command = config.command;
45135
- if (isStringArray$2(config.args)) server.args = config.args;
45313
+ if (isStringArray$1(config.args)) server.args = config.args;
45136
45314
  if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
45137
45315
  if (typeof config.url === "string") server.url = config.url;
45138
45316
  if (isPlainObject$1(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
@@ -46324,7 +46502,7 @@ function mergeToolLists(...lists) {
46324
46502
  function toKiroMcpServers(servers) {
46325
46503
  return Object.fromEntries(Object.entries(servers).map(([name, server]) => {
46326
46504
  const { kiroAutoApprove, kiroAutoBlock, disabledTools, ...rest } = server;
46327
- const autoApprove = mergeToolLists(isStringArray$2(rest.autoApprove) ? rest.autoApprove : void 0, kiroAutoApprove);
46505
+ const autoApprove = mergeToolLists(isStringArray$1(rest.autoApprove) ? rest.autoApprove : void 0, kiroAutoApprove);
46328
46506
  const disabled = mergeToolLists(disabledTools, kiroAutoBlock);
46329
46507
  return [name, {
46330
46508
  ...rest,
@@ -46348,7 +46526,7 @@ function fromKiroMcpServers(servers) {
46348
46526
  return Object.fromEntries(Object.entries(servers).map(([name, server]) => {
46349
46527
  if (server === null || typeof server !== "object" || Array.isArray(server)) return [name, server];
46350
46528
  const { autoApprove, ...rest } = server;
46351
- if (!isStringArray$2(autoApprove)) return [name, server];
46529
+ if (!isStringArray$1(autoApprove)) return [name, server];
46352
46530
  return [name, {
46353
46531
  ...rest,
46354
46532
  kiroAutoApprove: autoApprove
@@ -47777,7 +47955,7 @@ function headersRecordToPoolList(headers) {
47777
47955
  return Object.entries(headers).map(([name, value]) => `${name}: ${value}`);
47778
47956
  }
47779
47957
  function poolHeadersListToRecord(serverName, headers) {
47780
- if (!isStringArray$2(headers)) return void 0;
47958
+ if (!isStringArray$1(headers)) return void 0;
47781
47959
  const result = {};
47782
47960
  for (const entry of headers) {
47783
47961
  const separator = entry.indexOf(":");
@@ -47803,7 +47981,7 @@ const GLOB_METACHARACTERS = /[*?[]/;
47803
47981
  /** Patterns named in the glob warning before the rest collapse to `(+N more)`. */
47804
47982
  const MAX_LISTED_GLOB_PATTERNS = 10;
47805
47983
  function importPoolToolList({ serverName, poolKey, value, warnOnGlobs }) {
47806
- if (!isStringArray$2(value)) {
47984
+ if (!isStringArray$1(value)) {
47807
47985
  warnWithFallback(void 0, `Ignored malformed value for ${poolKey} in Pool MCP server ${quoteValueForWarning(serverName)}: expected a list of strings`);
47808
47986
  return;
47809
47987
  }
@@ -48013,7 +48191,7 @@ var PoolMcp = class PoolMcp extends ToolMcp {
48013
48191
  const poolAllow = isRecord$1(rawServer) ? rawServer.poolAllow : void 0;
48014
48192
  return [serverName, {
48015
48193
  ...serverConfig,
48016
- ...isStringArray$2(poolAllow) && { poolAllow }
48194
+ ...isStringArray$1(poolAllow) && { poolAllow }
48017
48195
  }];
48018
48196
  })), logger);
48019
48197
  return new PoolMcp({
@@ -48244,7 +48422,7 @@ function pickServerLists(source) {
48244
48422
  if (!isPlainObject$1(source)) return {};
48245
48423
  return Object.fromEntries(QWENCODE_MCP_SERVER_LIST_KEYS.flatMap((key) => {
48246
48424
  const value = source[key];
48247
- return isStringArray$2(value) ? [[key, value]] : [];
48425
+ return isStringArray$1(value) ? [[key, value]] : [];
48248
48426
  }));
48249
48427
  }
48250
48428
  /**
@@ -48937,7 +49115,7 @@ async function resolveRovodevMcpImportPath({ outputRoot, global, config, logger
48937
49115
  }
48938
49116
  function disabledNamesOf(config) {
48939
49117
  const mcpBlock = config && isRecord$1(config.mcp) ? config.mcp : {};
48940
- return isStringArray$2(mcpBlock.disabledMcpServers) ? mcpBlock.disabledMcpServers : [];
49118
+ return isStringArray$1(mcpBlock.disabledMcpServers) ? mcpBlock.disabledMcpServers : [];
48941
49119
  }
48942
49120
  /**
48943
49121
  * The value `mcp.mcpConfigPath` needs so Rovo Dev reads the `mcp.json` rulesync
@@ -49353,7 +49531,7 @@ var RovodevMcp = class RovodevMcp extends ToolMcp {
49353
49531
  return [];
49354
49532
  }
49355
49533
  const existingMcp = isRecord$1(existingParsed.mcp) ? { ...existingParsed.mcp } : {};
49356
- const existingDisabled = isStringArray$2(existingMcp.disabledMcpServers) ? existingMcp.disabledMcpServers : [];
49534
+ const existingDisabled = isStringArray$1(existingMcp.disabledMcpServers) ? existingMcp.disabledMcpServers : [];
49357
49535
  const managedNameSet = new Set(managedNames);
49358
49536
  const reEnabled = existingDisabled.filter((name) => managedNameSet.has(name) && !disabledNames.includes(name));
49359
49537
  if (reEnabled.length > 0) logger?.warn(`Rovo Dev MCP: re-enabling ${reEnabled.join(", ")} — the canonical config does not mark ${reEnabled.length === 1 ? "it" : "them"} disabled. Set "disabled": true in .rulesync/mcp.jsonc to keep a managed server off.`);
@@ -56512,8 +56690,9 @@ function convertClaudeToRulesyncPermissions(params) {
56512
56690
  //#endregion
56513
56691
  //#region src/features/permissions/cline-permissions.ts
56514
56692
  /**
56515
- * Cline CLI loads command permissions from the `CLINE_COMMAND_PERMISSIONS` environment variable.
56516
- * Cline supports only `allow` and `deny` (no `ask`), and only for shell commands.
56693
+ * The legacy Cline CLI loaded command permissions from the `CLINE_COMMAND_PERMISSIONS`
56694
+ * environment variable. It supported only `allow` and `deny` (no `ask`), and only for
56695
+ * shell commands.
56517
56696
  * Schema:
56518
56697
  * ```json
56519
56698
  * { "allow": ["pattern1", ...], "deny": ["pattern2", ...], "allowRedirects": false }
@@ -56521,7 +56700,14 @@ function convertClaudeToRulesyncPermissions(params) {
56521
56700
  *
56522
56701
  * Rulesync writes the JSON to `.cline/command-permissions.json` so users can do:
56523
56702
  * `export CLINE_COMMAND_PERMISSIONS=$(cat .cline/command-permissions.json)`
56703
+ *
56704
+ * Deprecated: no Cline CLI 3 release reads `CLINE_COMMAND_PERMISSIONS`. Upstream removed
56705
+ * it from the docs in cline/cline@6354d209, and the CLI reference now says the CLI has no
56706
+ * allow or deny list for shell commands and recommends a `PreToolUse` hook instead
56707
+ * (https://docs.cline.bot/cli/cli-reference). The output is still generated for
56708
+ * compatibility, with a deprecation warning on every generation.
56524
56709
  */
56710
+ const CLINE_PERMISSIONS_DEPRECATION_WARNING = "WARNING: The Cline permissions output (.cline/command-permissions.json) is deprecated. No Cline CLI 3 release reads CLINE_COMMAND_PERMISSIONS, so the generated allow/deny lists are not enforced by current Cline releases. To block shell commands in Cline, use a preToolUse hook (rulesync hooks feature, cline target) that cancels the run_commands calls you do not allow. See https://docs.cline.bot/cli/cli-reference.";
56525
56711
  const ClineCommandPermissionsSchema = z.looseObject({
56526
56712
  allow: z.optional(z.array(z.string())),
56527
56713
  deny: z.optional(z.array(z.string())),
@@ -56639,6 +56825,7 @@ var ClinePermissions = class ClinePermissions extends ToolPermissions {
56639
56825
  });
56640
56826
  }
56641
56827
  static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, global = false, logger }) {
56828
+ logger?.warn(CLINE_PERMISSIONS_DEPRECATION_WARNING);
56642
56829
  const paths = ClinePermissions.getSettablePaths({ global });
56643
56830
  const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
56644
56831
  const existingContent = await readFileContentOrNull(filePath) ?? "{}";
@@ -56668,7 +56855,7 @@ var ClinePermissions = class ClinePermissions extends ToolPermissions {
56668
56855
  const mergedDeny = uniq([...existing.deny ?? [], ...dedupedDeny]).toSorted();
56669
56856
  const denySet = new Set(mergedDeny);
56670
56857
  const collisions = dedupedAllow.filter((p) => denySet.has(p));
56671
- if (collisions.length > 0) logger?.warn(`Cline command permissions: pattern(s) ${collisions.map((p) => `'${p}'`).join(", ")} appear in both 'allow' and 'deny'. Cline documents that deny rules always take precedence, so the 'allow' entry has no effect. Consider removing the duplicate rule.`);
56858
+ if (collisions.length > 0) logger?.warn(`Cline command permissions: pattern(s) ${collisions.map((p) => `'${p}'`).join(", ")} appear in both 'allow' and 'deny'. The legacy Cline CLI gave deny rules precedence, so the 'allow' entry has no effect. Consider removing the duplicate rule.`);
56672
56859
  const next = {
56673
56860
  ...existing,
56674
56861
  allow: dedupedAllow,
@@ -59951,9 +60138,9 @@ function managedContinueToolNames(config) {
59951
60138
  */
59952
60139
  function parseContinuePermissionLists(parsed) {
59953
60140
  return {
59954
- exclude: isStringArray$2(parsed.exclude) ? parsed.exclude : [],
59955
- ask: isStringArray$2(parsed.ask) ? parsed.ask : [],
59956
- allow: isStringArray$2(parsed.allow) ? parsed.allow : []
60141
+ exclude: isStringArray$1(parsed.exclude) ? parsed.exclude : [],
60142
+ ask: isStringArray$1(parsed.ask) ? parsed.ask : [],
60143
+ allow: isStringArray$1(parsed.allow) ? parsed.allow : []
59957
60144
  };
59958
60145
  }
59959
60146
  /**
@@ -60480,8 +60667,8 @@ var CrushPermissions = class CrushPermissions extends ToolPermissions {
60480
60667
  logger
60481
60668
  });
60482
60669
  const managedToolNames = managedCrushToolNames(config);
60483
- const preservedAllowed = (isStringArray$2(existingPermissions?.["allowed_tools"]) ? existingPermissions[CRUSH_ALLOWED_TOOLS_KEY] : []).filter((entry) => entry.includes(":") || !managedToolNames.has(entry));
60484
- const preservedDisabled = (isStringArray$2(existingOptions?.["disabled_tools"]) ? existingOptions[CRUSH_DISABLED_TOOLS_KEY] : []).filter((entry) => !managedToolNames.has(entry));
60670
+ const preservedAllowed = (isStringArray$1(existingPermissions?.["allowed_tools"]) ? existingPermissions[CRUSH_ALLOWED_TOOLS_KEY] : []).filter((entry) => entry.includes(":") || !managedToolNames.has(entry));
60671
+ const preservedDisabled = (isStringArray$1(existingOptions?.["disabled_tools"]) ? existingOptions[CRUSH_DISABLED_TOOLS_KEY] : []).filter((entry) => !managedToolNames.has(entry));
60485
60672
  const allowedList = uniq$1([...preservedAllowed, ...generated.allowed]);
60486
60673
  const disabledList = uniq$1([...preservedDisabled, ...generated.disabled]);
60487
60674
  const patch = {};
@@ -60507,8 +60694,8 @@ var CrushPermissions = class CrushPermissions extends ToolPermissions {
60507
60694
  const permissions = isRecord$1(this.json["permissions"]) ? this.json[CRUSH_PERMISSIONS_KEY] : {};
60508
60695
  const options = isRecord$1(this.json["options"]) ? this.json[CRUSH_OPTIONS_KEY] : {};
60509
60696
  const rulesyncConfig = convertCrushListsToRulesync({
60510
- allowed: isStringArray$2(permissions["allowed_tools"]) ? permissions[CRUSH_ALLOWED_TOOLS_KEY] : [],
60511
- disabled: isStringArray$2(options["disabled_tools"]) ? options[CRUSH_DISABLED_TOOLS_KEY] : []
60697
+ allowed: isStringArray$1(permissions["allowed_tools"]) ? permissions[CRUSH_ALLOWED_TOOLS_KEY] : [],
60698
+ disabled: isStringArray$1(options["disabled_tools"]) ? options[CRUSH_DISABLED_TOOLS_KEY] : []
60512
60699
  });
60513
60700
  return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(rulesyncConfig, null, 2) });
60514
60701
  }
@@ -62683,7 +62870,7 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
62683
62870
  function convertGoosePermissionConfigToRulesync(userPermission) {
62684
62871
  const permission = {};
62685
62872
  for (const key of GOOSE_PERMISSION_LIST_KEYS) {
62686
- const toolNames = isStringArray$2(userPermission[key]) ? userPermission[key] : [];
62873
+ const toolNames = isStringArray$1(userPermission[key]) ? userPermission[key] : [];
62687
62874
  const action = GOOSE_LIST_TO_ACTION[key];
62688
62875
  for (const toolName of toolNames) for (const category of GOOSE_TO_RULESYNC_TOOL_NAMES[toolName] ?? [toolName]) {
62689
62876
  permission[category] ??= {};
@@ -63031,7 +63218,7 @@ const ACTION_RANK$3 = {
63031
63218
  * (mirrors the Cursor adapter's preservation of unmanaged types).
63032
63219
  */
63033
63220
  function unmanagedEntries(existingPermission, key) {
63034
- return (isStringArray$2(existingPermission[key]) ? existingPermission[key] : []).filter((entry) => parseGrokEntry(entry) === null);
63221
+ return (isStringArray$1(existingPermission[key]) ? existingPermission[key] : []).filter((entry) => parseGrokEntry(entry) === null);
63035
63222
  }
63036
63223
  /**
63037
63224
  * Bucket canonical rules into Grok's `[permission]` allow/deny/ask arrays of
@@ -63088,7 +63275,7 @@ function warnRemovedAllToolsRestrictions({ existingPermission, ranked, logger })
63088
63275
  const generated = ranked.get(GROK_ALL_TOOLS_ENTRY);
63089
63276
  for (const action of ["deny", "ask"]) {
63090
63277
  const entries = existingPermission[action];
63091
- if (!isStringArray$2(entries)) continue;
63278
+ if (!isStringArray$1(entries)) continue;
63092
63279
  if (!entries.some((entry) => parseGrokEntry(entry)?.category === "*")) continue;
63093
63280
  if (generated !== void 0 && ACTION_RANK$3[generated] >= ACTION_RANK$3[action]) continue;
63094
63281
  logger.warn(`Grok CLI permissions: removing the existing bare '*' entry from the '${action}' array — Rulesync now manages it through the all-tools '*' category, and the canonical config has no '*' '${action}' rule. Add "*": { "*": "${action}" } to keep restricting every tool.`);
@@ -63119,9 +63306,9 @@ function warnEntriesShadowedByAllTools({ ranked, logger }) {
63119
63306
  * described more than once resolves to the strictest action.
63120
63307
  */
63121
63308
  function parseGrokPermissionArrays(permission) {
63122
- const allow = isStringArray$2(permission.allow) ? permission.allow : void 0;
63123
- const deny = isStringArray$2(permission.deny) ? permission.deny : void 0;
63124
- const ask = isStringArray$2(permission.ask) ? permission.ask : void 0;
63309
+ const allow = isStringArray$1(permission.allow) ? permission.allow : void 0;
63310
+ const deny = isStringArray$1(permission.deny) ? permission.deny : void 0;
63311
+ const ask = isStringArray$1(permission.ask) ? permission.ask : void 0;
63125
63312
  const rules = Array.isArray(permission[GROKCLI_PERMISSION_RULES_KEY]) ? permission[GROKCLI_PERMISSION_RULES_KEY] : void 0;
63126
63313
  if ((allow?.length ?? 0) + (deny?.length ?? 0) + (ask?.length ?? 0) + (rules?.length ?? 0) === 0) return null;
63127
63314
  const result = {};
@@ -63329,20 +63516,20 @@ var HermesagentPermissions = class HermesagentPermissions extends ToolPermission
63329
63516
  const permission = clonePermissionBlock(provenance.permission);
63330
63517
  reconcileCommandAllowlist({
63331
63518
  permission,
63332
- commandAllowlist: isStringArray$2(config.command_allowlist) ? config.command_allowlist : []
63519
+ commandAllowlist: isStringArray$1(config.command_allowlist) ? config.command_allowlist : []
63333
63520
  });
63334
63521
  const approvals = isRecord$1(config.approvals) ? config.approvals : {};
63335
63522
  reconcileNativeDenies({
63336
63523
  permission,
63337
63524
  category: SHELL_PERMISSION_CATEGORY,
63338
- patterns: isStringArray$2(approvals.deny) ? approvals.deny : []
63525
+ patterns: isStringArray$1(approvals.deny) ? approvals.deny : []
63339
63526
  });
63340
63527
  const security = isRecord$1(config.security) ? config.security : {};
63341
63528
  const websiteBlocklist = isRecord$1(security.website_blocklist) ? security.website_blocklist : {};
63342
63529
  reconcileNativeDenies({
63343
63530
  permission,
63344
63531
  category: WEBFETCH_PERMISSION_CATEGORY,
63345
- patterns: websiteBlocklist.enabled === true && isStringArray$2(websiteBlocklist.domains) ? websiteBlocklist.domains : []
63532
+ patterns: websiteBlocklist.enabled === true && isStringArray$1(websiteBlocklist.domains) ? websiteBlocklist.domains : []
63346
63533
  });
63347
63534
  removeEmptyCategories(permission);
63348
63535
  const { permission: _permission, hermes: _hermes, ...otherProvenance } = provenance;
@@ -64736,13 +64923,13 @@ function classifyExistingEntry({ emitted, capability, pattern, effect }) {
64736
64923
  * how many entries rulesync does not generate.
64737
64924
  */
64738
64925
  function mergeExistingRule({ rule, emitted, logger }) {
64739
- if (!isRecord$1(rule) || typeof rule.capability !== "string" || rule.exclude !== void 0 || !MANAGED_CAPABILITIES.has(rule.capability) || rule.match !== void 0 && !isStringArray$2(rule.match)) return {
64926
+ if (!isRecord$1(rule) || typeof rule.capability !== "string" || rule.exclude !== void 0 || !MANAGED_CAPABILITIES.has(rule.capability) || rule.match !== void 0 && !isStringArray$1(rule.match)) return {
64740
64927
  rule,
64741
64928
  unrelatedEntries: 0
64742
64929
  };
64743
64930
  const capability = rule.capability;
64744
64931
  const isCatchAll = rule.match === void 0;
64745
- const patterns = isStringArray$2(rule.match) ? rule.match : [CATCH_ALL_PATTERN$6];
64932
+ const patterns = isStringArray$1(rule.match) ? rule.match : [CATCH_ALL_PATTERN$6];
64746
64933
  if (patterns.length === 0) return {
64747
64934
  rule,
64748
64935
  unrelatedEntries: 0
@@ -64926,7 +65113,7 @@ var KiroCapabilityPermissions = class KiroCapabilityPermissions extends ToolPerm
64926
65113
  for (const rule of rules) {
64927
65114
  if (!isImportableRule(rule) || !isRecord$1(rule) || !isPermissionAction$1(rule.effect)) continue;
64928
65115
  const category = KIRO_CAPABILITY_TO_CANONICAL[rule.capability];
64929
- const patterns = rule.match === void 0 ? [CATCH_ALL_PATTERN$6] : isStringArray$2(rule.match) ? rule.match : [];
65116
+ const patterns = rule.match === void 0 ? [CATCH_ALL_PATTERN$6] : isStringArray$1(rule.match) ? rule.match : [];
64930
65117
  const categoryRules = permission[category] ??= {};
64931
65118
  for (const pattern of patterns) {
64932
65119
  const previous = categoryRules[pattern];
@@ -66644,8 +66831,8 @@ function importPoolTool({ toolConfig, bucket }) {
66644
66831
  bucket[CATCH_ALL_PATTERN$3] = "deny";
66645
66832
  return;
66646
66833
  }
66647
- const denied = isStringArray$2(toolConfig[POOL_DENY_KEY]) ? toolConfig[POOL_DENY_KEY] : [];
66648
- const allowed = isStringArray$2(toolConfig[POOL_ALLOW_KEY]) ? toolConfig[POOL_ALLOW_KEY] : [];
66834
+ const denied = isStringArray$1(toolConfig[POOL_DENY_KEY]) ? toolConfig[POOL_DENY_KEY] : [];
66835
+ const allowed = isStringArray$1(toolConfig[POOL_ALLOW_KEY]) ? toolConfig[POOL_ALLOW_KEY] : [];
66649
66836
  for (const pattern of denied.filter(isUsablePattern)) bucket[pattern] = "deny";
66650
66837
  for (const pattern of allowed.filter(isUsablePattern)) bucket[pattern] ??= "allow";
66651
66838
  }
@@ -68850,7 +69037,7 @@ function convertRovodevToolPermissionsToRulesync(toolPermissions) {
68850
69037
  permission[category] ??= {};
68851
69038
  permission[category][CATCH_ALL_PATTERN$1] = levels.reduce((strictest, level) => strictestAction(strictest, level ?? implicitLevel), permission[category][CATCH_ALL_PATTERN$1]);
68852
69039
  }
68853
- if (isStringArray$2(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
69040
+ if (isStringArray$1(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
68854
69041
  permission.read ??= {};
68855
69042
  permission.read[path] ??= "allow";
68856
69043
  }
@@ -69106,7 +69293,7 @@ function stripRefusedOverridePaths(override) {
69106
69293
  */
69107
69294
  function collectTrustAffectingOverrideEntries({ tools, general }) {
69108
69295
  const entries = [];
69109
- const allowed = isStringArray$2(tools[ALLOWED_KEY]) ? tools[ALLOWED_KEY] : [];
69296
+ const allowed = isStringArray$1(tools[ALLOWED_KEY]) ? tools[ALLOWED_KEY] : [];
69110
69297
  if (allowed.length > 0) entries.push({
69111
69298
  label: `tools.allowed (${allowed.map(quoteValueForWarning).join(", ")})`,
69112
69299
  reason: "auto-approves what it names as the override spells it; a non-shell entry is only checked against a whole-tool deny of its tool"
@@ -69257,7 +69444,7 @@ function withholdDeniedOverrideAllowed({ overrideVerbatimAllowed, exclude, logge
69257
69444
  * hand-written entry went.
69258
69445
  */
69259
69446
  function reportDroppedManagedEntries({ existingTools, allowedList, excludeList, logger }) {
69260
- const dropped = (key, list) => uniq(isStringArray$2(existingTools?.[key]) ? existingTools[key] : []).filter((entry) => {
69447
+ const dropped = (key, list) => uniq(isStringArray$1(existingTools?.[key]) ? existingTools[key] : []).filter((entry) => {
69261
69448
  if (list.includes(entry)) return false;
69262
69449
  const toolName = parseTabnineEntry(entry)?.toolName;
69263
69450
  return toolName === void 0 || !list.includes(toolName);
@@ -69339,7 +69526,7 @@ var TabninePermissions = class TabninePermissions extends ToolPermissions {
69339
69526
  }
69340
69527
  const overrideList = (key) => {
69341
69528
  const value = overrideTools[key];
69342
- if (value === void 0 || isStringArray$2(value)) return value ?? [];
69529
+ if (value === void 0 || isStringArray$1(value)) return value ?? [];
69343
69530
  warnWithFallback(logger, `Tabnine CLI permissions: ignored tools.${key} of the tabnine override (${quoteValueForWarning(value)}); it must be a list of tool names.`);
69344
69531
  return [];
69345
69532
  };
@@ -69377,7 +69564,7 @@ var TabninePermissions = class TabninePermissions extends ToolPermissions {
69377
69564
  if (ignoredOverrideKeys.length > 0) warnWithFallback(logger, `Tabnine CLI permissions: ignored ${ignoredOverrideKeys.length} key(s) of the tabnine override (${ignoredOverrideKeys.map(quoteValueForWarning).join(", ")}); only 'tools' and 'general' are written through the permissions feature.`);
69378
69565
  const existingTools = this.existingToolsGroup(existingContent);
69379
69566
  const managedToolNames = new Set(Object.keys(config.permission).map((category) => category === "bash" || category === "*" ? SHELL_TOOL_NAME : toTabnineToolName(category)));
69380
- const preservedEntries = (key) => (isStringArray$2(existingTools?.[key]) ? existingTools[key] : []).filter((entry) => {
69567
+ const preservedEntries = (key) => (isStringArray$1(existingTools?.[key]) ? existingTools[key] : []).filter((entry) => {
69381
69568
  const toolName = parseTabnineEntry(entry)?.toolName;
69382
69569
  return toolName === void 0 || !managedToolNames.has(toolName);
69383
69570
  });
@@ -69455,7 +69642,7 @@ var TabninePermissions = class TabninePermissions extends ToolPermissions {
69455
69642
  if (rules !== void 0 && !Object.hasOwn(rules, pattern)) rules[pattern] = action;
69456
69643
  };
69457
69644
  for (const [key, action] of [[EXCLUDE_KEY, "deny"], [ALLOWED_KEY, "allow"]]) {
69458
- const entries = isStringArray$2(tools[key]) ? tools[key] : [];
69645
+ const entries = isStringArray$1(tools[key]) ? tools[key] : [];
69459
69646
  for (const entry of entries) {
69460
69647
  const parsed = parseTabnineEntry(entry);
69461
69648
  if (parsed === void 0) {
@@ -70323,12 +70510,12 @@ const WIDE_CHARACTERS_PATTERN = /\p{Emoji_Presentation}|\p{Emoji_Modifier_Base}|
70323
70510
  * The ranges are the `East_Asian_Width=A` property of Unicode 17.0, taken from
70324
70511
  * the table `get-east-asian-width` 1.6.0 carries and merged where two are
70325
70512
  * adjacent. The table is written out here rather than read from the package so
70326
- * that the one lookup adds no dependency of its own, and the drift test in
70327
- * `display-width.test.ts` is what keeps it honest: it loads the package through
70328
- * the prompt renderer that already depends on it, walks every code point, and
70329
- * fails when this table and the property disagree in either direction. When
70330
- * the renderer's copy moves to a newer Unicode, that test is what fails, and
70331
- * the table is regenerated from the new property — its ranges, marks aside,
70513
+ * that the one lookup adds no runtime dependency of its own, and the drift test
70514
+ * in `display-width.test.ts` is what keeps it honest: it loads the package,
70515
+ * pinned as a dev dependency, walks every code point, and fails when this
70516
+ * table and the property disagree in either direction. When that pin moves to
70517
+ * a newer Unicode, that test is what fails, and the table is regenerated from
70518
+ * the new property — its ranges, marks aside,
70332
70519
  * merged where adjacent and written as escapes — with the version named here.
70333
70520
  *
70334
70521
  * Three stretches of the property are left out because they never reach this
@@ -72077,8 +72264,8 @@ var WarpPermissions = class WarpPermissions extends ToolPermissions {
72077
72264
  const executionProfiles = isRecord$1(agents["execution_profiles"]) ? agents[EXECUTION_PROFILES_KEY] : void 0;
72078
72265
  const defaultProfile = executionProfiles && isRecord$1(executionProfiles["default"]) ? executionProfiles[DEFAULT_PROFILE_KEY] : void 0;
72079
72266
  const config = convertWarpToRulesyncPermissions({
72080
- allow: defaultProfile ? isStringArray$2(defaultProfile["command_allowlist"]) ? defaultProfile[PROFILE_ALLOWLIST_KEY] : [] : isStringArray$2(profiles[ALLOWLIST_KEY]) ? profiles[ALLOWLIST_KEY] : [],
72081
- deny: defaultProfile ? isStringArray$2(defaultProfile["command_denylist"]) ? defaultProfile[PROFILE_DENYLIST_KEY] : [] : isStringArray$2(profiles[DENYLIST_KEY]) ? profiles[DENYLIST_KEY] : []
72267
+ allow: defaultProfile ? isStringArray$1(defaultProfile["command_allowlist"]) ? defaultProfile[PROFILE_ALLOWLIST_KEY] : [] : isStringArray$1(profiles[ALLOWLIST_KEY]) ? profiles[ALLOWLIST_KEY] : [],
72268
+ deny: defaultProfile ? isStringArray$1(defaultProfile["command_denylist"]) ? defaultProfile[PROFILE_DENYLIST_KEY] : [] : isStringArray$1(profiles[DENYLIST_KEY]) ? profiles[DENYLIST_KEY] : []
72082
72269
  });
72083
72270
  const warpOverride = {};
72084
72271
  for (const key of WARP_OVERRIDE_KEYS) if (profiles[key] !== void 0) warpOverride[key] = profiles[key];
@@ -72608,8 +72795,8 @@ var WarpcliPermissions = class WarpcliPermissions extends ToolPermissions {
72608
72795
  const allowlist = defaultProfile[PROFILE_ALLOWLIST_KEY];
72609
72796
  const denylist = defaultProfile[PROFILE_DENYLIST_KEY];
72610
72797
  const config = convertWarpToRulesyncPermissions({
72611
- allow: isStringArray$2(allowlist) ? allowlist : [],
72612
- deny: isStringArray$2(denylist) ? denylist : []
72798
+ allow: isStringArray$1(allowlist) ? allowlist : [],
72799
+ deny: isStringArray$1(denylist) ? denylist : []
72613
72800
  });
72614
72801
  const executionProfileOverride = liftExecutionProfileOverride(defaultProfile);
72615
72802
  const warpcliOverride = {};
@@ -84624,7 +84811,7 @@ var BobSubagent = class extends RooSubagent {
84624
84811
  const mode = super.toRooMode(rulesyncSubagent);
84625
84812
  const frontmatter = rulesyncSubagent.getFrontmatter();
84626
84813
  const bobSection = isRecord$1(frontmatter.bob) ? frontmatter.bob : {};
84627
- if (isStringArray$2(bobSection.allowedSubagents)) mode.allowedSubagents = bobSection.allowedSubagents;
84814
+ if (isStringArray$1(bobSection.allowedSubagents)) mode.allowedSubagents = bobSection.allowedSubagents;
84628
84815
  return mode;
84629
84816
  }
84630
84817
  };
@@ -90079,7 +90266,7 @@ var ZoocodeSubagent = class extends RooSubagent {
90079
90266
  const mode = super.toRooMode(rulesyncSubagent);
90080
90267
  const frontmatter = rulesyncSubagent.getFrontmatter();
90081
90268
  const zoocodeSection = isRecord$1(frontmatter.zoocode) ? frontmatter.zoocode : {};
90082
- if (isStringArray$2(zoocodeSection.allowedMcpServers)) mode.allowedMcpServers = zoocodeSection.allowedMcpServers;
90269
+ if (isStringArray$1(zoocodeSection.allowedMcpServers)) mode.allowedMcpServers = zoocodeSection.allowedMcpServers;
90083
90270
  return mode;
90084
90271
  }
90085
90272
  toRulesyncSubagents() {
@@ -90091,7 +90278,7 @@ var ZoocodeSubagent = class extends RooSubagent {
90091
90278
  targets: ["zoocode"],
90092
90279
  roo: restRooSection
90093
90280
  };
90094
- if (isStringArray$2(allowedMcpServers)) rebuilt.zoocode = { allowedMcpServers };
90281
+ if (isStringArray$1(allowedMcpServers)) rebuilt.zoocode = { allowedMcpServers };
90095
90282
  return new RulesyncSubagent({
90096
90283
  outputRoot: ".",
90097
90284
  frontmatter: rebuilt,
@@ -94032,7 +94219,8 @@ var NestedAgentsmdRule = class extends ToolRule {
94032
94219
  * `<subprojectPath>/AGENTS.md` (project scope only); every other non-root rule
94033
94220
  * folds into the root `AGENTS.md`, since Codex has no modular rules directory.
94034
94221
  * The project files on the root-to-cwd chain share one `project_doc_max_bytes`
94035
- * budget (32 KiB by default), which the RulesProcessor warns about.
94222
+ * budget (32 KiB by default, or the value set in the project's
94223
+ * `.codex/config.toml`), which the RulesProcessor warns about.
94036
94224
  *
94037
94225
  * @see https://learn.chatgpt.com/docs/agent-configuration/agents-md
94038
94226
  */
@@ -94044,6 +94232,29 @@ var CodexcliRule = class extends NestedAgentsmdRule {
94044
94232
  };
94045
94233
  }
94046
94234
  };
94235
+ /**
94236
+ * Read the `project_doc_max_bytes` value set in the project's
94237
+ * `.codex/config.toml`, which Codex loads for trusted projects and which
94238
+ * overrides the 32 KiB default budget for project `AGENTS.md` files.
94239
+ *
94240
+ * Answers `undefined` when the file is missing, is not valid TOML, or does not
94241
+ * set the key to a positive integer, so the caller falls back to the default.
94242
+ *
94243
+ * @see https://learn.chatgpt.com/docs/config-file/config-reference
94244
+ */
94245
+ async function readCodexcliProjectDocMaxBytes(outputRoot) {
94246
+ const content = await readFileContentOrNull(join(outputRoot, CODEXCLI_DIR, CODEXCLI_MCP_FILE_NAME));
94247
+ if (content === null) return;
94248
+ let parsed;
94249
+ try {
94250
+ parsed = smolToml.parse(content);
94251
+ } catch {
94252
+ return;
94253
+ }
94254
+ if (!isPlainObject$1(parsed)) return;
94255
+ const value = parsed.project_doc_max_bytes;
94256
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
94257
+ }
94047
94258
  //#endregion
94048
94259
  //#region src/features/rules/commandcode-rule.ts
94049
94260
  var CommandcodeRule = class CommandcodeRule extends ToolRule {
@@ -99418,7 +99629,9 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
99418
99629
  collisionPolicy: "fold",
99419
99630
  projectInstructionBudget: {
99420
99631
  bytes: CODEXCLI_PROJECT_DOC_MAX_BYTES,
99421
- remedy: "Codex CLI stops reading project `AGENTS.md` files once their combined size on the root-to-working-directory chain reaches `project_doc_max_bytes` (32 KiB by default) and silently drops the rest. Move directory-scoped rules into nested `AGENTS.md` files with `agentsmd.subprojectPath` (or `deriveSubprojectPathFromGlobs: true` in rulesync.jsonc), trim the rules, or raise `project_doc_max_bytes` in `.codex/config.toml` (ignore this warning if you already have)."
99632
+ remedy: "Codex CLI stops reading project `AGENTS.md` files once their combined size on the root-to-working-directory chain reaches `project_doc_max_bytes` (32 KiB by default) and silently drops the rest. Move directory-scoped rules into nested `AGENTS.md` files with `agentsmd.subprojectPath` (or `deriveSubprojectPathFromGlobs: true` in rulesync.jsonc), trim the rules, or raise `project_doc_max_bytes` in `.codex/config.toml`.",
99633
+ readConfiguredBytes: readCodexcliProjectDocMaxBytes,
99634
+ configuredSource: "`project_doc_max_bytes` in `.codex/config.toml`"
99422
99635
  }
99423
99636
  }
99424
99637
  }],
@@ -99963,7 +100176,7 @@ var RulesProcessor = class extends FeatureProcessor {
99963
100176
  factory
99964
100177
  });
99965
100178
  await this.warnForWarpLegacyRootFile(toolRules);
99966
- this.warnForInstructionBudget({
100179
+ await this.warnForInstructionBudget({
99967
100180
  toolRules,
99968
100181
  meta
99969
100182
  });
@@ -99984,14 +100197,19 @@ var RulesProcessor = class extends FeatureProcessor {
99984
100197
  * once, at its shallowest directory. With several output roots the warning
99985
100198
  * names the root it refers to. An output root below the git root is measured
99986
100199
  * without the parent directories' files Codex would also load, so the
99987
- * estimate can come out low there. The budget checked is the tool's default: a value
99988
- * raised by hand in the tool's own config is not read. Global scope is not
100200
+ * estimate can come out low there. The budget checked is the value set in the
100201
+ * output root's tool config when the tool supports one (Codex's
100202
+ * `project_doc_max_bytes` in `.codex/config.toml`), else the tool's default;
100203
+ * the warning names which one it used. Global scope is not
99989
100204
  * checked because Codex loads the personal `~/.codex/AGENTS.md` outside the
99990
100205
  * project budget.
99991
100206
  */
99992
- warnForInstructionBudget({ toolRules, meta }) {
100207
+ async warnForInstructionBudget({ toolRules, meta }) {
99993
100208
  const budget = meta.projectInstructionBudget;
99994
100209
  if (budget === void 0 || this.global) return;
100210
+ const configuredBytes = await budget.readConfiguredBytes?.(this.outputRoot);
100211
+ const limitBytes = configuredBytes ?? budget.bytes;
100212
+ const limitLabel = budget.readConfiguredBytes === void 0 ? `${limitBytes}-byte limit` : configuredBytes === void 0 ? `${limitBytes}-byte default limit` : `${limitBytes}-byte limit set by ${budget.configuredSource ?? "the tool config"}`;
99995
100213
  const files = toolRules.map((rule) => ({
99996
100214
  dir: normalizeRelativeDir(rule.getRelativeDirPath()),
99997
100215
  path: toPosixPath(join(rule.getRelativeDirPath(), rule.getRelativeFilePath())),
@@ -100001,8 +100219,8 @@ var RulesProcessor = class extends FeatureProcessor {
100001
100219
  const location = this.outputRoot === process.cwd() ? "" : ` under '${this.outputRoot}'`;
100002
100220
  const rootFiles = files.filter(({ dir }) => dir === ".");
100003
100221
  const rootBytes = rootFiles.reduce((sum, { bytes }) => sum + bytes, 0);
100004
- if (rootBytes > budget.bytes) {
100005
- this.logger.warn(`The generated ${this.toolTarget} root instruction file${location} (${rootFiles.map(({ path }) => path).join(", ")}) is ${rootBytes} bytes, over the ${budget.bytes}-byte limit. ${budget.remedy}`);
100222
+ if (rootBytes > limitBytes) {
100223
+ this.logger.warn(`The generated ${this.toolTarget} root instruction file${location} (${rootFiles.map(({ path }) => path).join(", ")}) is ${rootBytes} bytes, over the ${limitLabel}. ${budget.remedy}`);
100006
100224
  return;
100007
100225
  }
100008
100226
  const reportedDirs = [];
@@ -100011,9 +100229,9 @@ var RulesProcessor = class extends FeatureProcessor {
100011
100229
  if (reportedDirs.some((dir) => isAncestorOrSelf(dir, file.dir))) continue;
100012
100230
  const chain = files.filter(({ dir }) => isAncestorOrSelf(dir, file.dir));
100013
100231
  const chainBytes = chain.reduce((sum, { bytes }) => sum + bytes, 0);
100014
- if (chainBytes > budget.bytes) {
100232
+ if (chainBytes > limitBytes) {
100015
100233
  reportedDirs.push(file.dir);
100016
- this.logger.warn(`The generated ${this.toolTarget} instruction files${location} loaded while working in '${file.dir}' (${chain.map(({ path }) => path).join(", ")}) total ${chainBytes} bytes, over the ${budget.bytes}-byte limit (as is every directory below it). ${budget.remedy}`);
100234
+ this.logger.warn(`The generated ${this.toolTarget} instruction files${location} loaded while working in '${file.dir}' (${chain.map(({ path }) => path).join(", ")}) total ${chainBytes} bytes, over the ${limitLabel} (as is every directory below it). ${budget.remedy}`);
100017
100235
  }
100018
100236
  }
100019
100237
  }
@@ -103918,4 +104136,4 @@ async function importChecksCore(params) {
103918
104136
  //#endregion
103919
104137
  export { RulesyncHooks as $, removeDirectoryStrict as $t, groupSpellingsByCaseFoldedIdentity as A, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as An, applyFileMode as At, QWENCODE_LOCAL_RULE_FILE_NAME as B, parseCommaSeparatedList as Bn, getFileSizeAndExecutableBit as Bt, COMMANDCODE_SETTINGS_LOCAL_FILE_NAME as C, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as Cn, fallbackLogger as Ct, FACTORYDROID_DIR as D, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as Dn, withWarnOnceScope as Dt, ChecksProcessor as E, RULESYNC_IGNORE_RELATIVE_FILE_PATH as En, resetRunWarningState as Et, CLAUDECODE_LOCAL_RULE_FILE_NAME as F, RULESYNC_SKILLS_RELATIVE_DIR_PATH as Fn, createTempDirectory as Ft, RulesyncSubagentFrontmatterSchema as G, truncateText as Gn, listDirectoryEntryNames as Gt, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as H, ALL_FEATURES_WITH_WILDCARD as Hn, isFileNotFoundError as Ht, CLAUDECODE_MEMORIES_DIR_NAME as I, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as In, directoryExists as It, RulesyncRule as J, quoteForLog as Jn, pathEscapesRoot as Jt, RulesyncSkill as K, hasDeceptiveHiddenCharacters as Kn, listFilePathsRecursively as Kt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as L, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as Ln, ensureDir as Lt, CODEXCLI_DIR as M, RULESYNC_PERMISSIONS_SCHEMA_URL as Mn, assertTreeContainsNoSymlinks as Mt, CLAUDECODE_AGENT_MEMORY_LOCAL_DIR_NAME as N, RULESYNC_RELATIVE_DIR_PATH as Nn, assertWritablePathInsideRoot as Nt, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as O, RULESYNC_MCP_RELATIVE_FILE_PATH as On, CLIError as Ot, CLAUDECODE_DIR as P, RULESYNC_RULES_RELATIVE_DIR_PATH as Pn, checkPathTraversal as Pt, RulesyncIgnore as Q, removeDirectory as Qt, CLAUDECODE_SKILLS_DIR_PATH as R, RULESYNC_USER_CONFIG_DIR_NAME as Rn, fileExists as Rt, COMMANDCODE_DIR as S, RULESYNC_CONFIG_SCHEMA_URL as Sn, WarningCollectingLogger as St, CODEBUDDY_LOCAL_RULE_FILE_NAME as T, RULESYNC_HOOKS_RELATIVE_FILE_PATH as Tn, withFallbackLoggerTarget as Tt, getLocalSkillDirNames as U, DEPRECATED_FEATURE_REPLACEMENTS as Un, isFileSystemError as Ut, AUGMENTCODE_DIR as V, ALL_FEATURES as Vn, getHomeDirectory as Vt, RulesyncSubagent as W, formatError as Wn, isSymlink as Wt, RulesyncPermissions as X, stripControlCharactersKeepingLineFeeds as Xn, readFileContent as Xt, RulesyncRuleFrontmatterSchema as Y, stripControlCharacters as Yn, posixRelativePathEscapesRoot as Yt, RulesyncMcp as Z, stripHiddenCharacters as Zn, readFileContentOrNull as Zt, LETTACODE_DIR as _, MAX_FILE_SIZE as _n, SourceEntrySchema as _t, getProcessorRegistryEntry as a, runWithDirectoryRollback as an, RulesyncCheck as at, CommandsProcessor as b, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as bn, ConsoleLogger as bt, RulesProcessor as c, writeFileBuffer as cn, loadYaml as ct, ELLIPSIS_WIDTH as d, ALL_TOOL_TARGETS as dn, ConfigResolver as dt, removeFile as en, getRulesyncSourceCandidates as et, displayWidthOf as f, ALL_TOOL_TARGETS_WITH_WILDCARD as fn, mergeInputRootConfigs as ft, HooksProcessor as g, FEATURE_SOURCE_TREE_ENTRIES as gn, GITIGNORE_DESTINATION_KEY as gt, IgnoreProcessor as h, CURATED_RULES_FEATURE_SUBDIR as hn, ConfigFileSchema as ht, inspectInputRoots as i, resolvedRelativePath as in, RulesyncCommandFrontmatterSchema as it, CODEXCLI_BASH_RULES_FILE_NAME as j, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as jn, assertDirectoryIfExists as jt, caseFoldIdentity as k, RULESYNC_MCP_SCHEMA_URL as kn, ErrorCodes as kt, SubagentsProcessor as l, writeFileContent as ln, SHARED_USER_MANAGED_CONFIG_PATHS as lt, McpProcessor as m, ToolTargetSchema as mn, CONFLICTING_TARGET_PAIRS as mt, formatSourceLoadFailure as n, removeTempDirectory as nn, parseJsonc as nt, convertFromTool as o, splitPathSegments as on, RulesyncCheckFrontmatterSchema as ot, shortenToWidth as p, PACKAGING_TOOL_TARGETS as pn, resolveEffectiveInputRoots as pt, RulesyncSkillFrontmatterSchema as q, hasEnclosingMarkOutsideKeycap as qn, listSubdirectoryNames as qt, generate as r, resolvePath as rn, RulesyncCommand as rt, isPackagingToolTarget as s, toPosixPath as sn, stringifyFrontmatter as st, importFromTool as t, removeFileStrict as tn, resolveRulesyncSourceWritePath as tt, SkillsProcessor as u, mapWithConcurrency as un, SKILL_FILE_NAME as ut, LETTACODE_SETTINGS_LOCAL_FILE_NAME as v, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as vn, assertTargetsFeaturesExclusive as vt, CODEBUDDY_DIR as w, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as wn, warnOnConflictingFlags as wt, REASONIX_LOCAL_RULE_FILE_NAME as x, RULESYNC_CONFIG_RELATIVE_FILE_PATH as xn, JsonLogger as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, RULESYNC_CHECKS_RELATIVE_DIR_PATH as yn, findControlCharacter as yt, QWENCODE_DIR as z, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as zn, findFilesByGlobs as zt };
103920
104138
 
103921
- //# sourceMappingURL=import-Tya9qZRK.js.map
104139
+ //# sourceMappingURL=import-BPSwSahy.js.map