rulesync 9.1.1 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10462,6 +10462,66 @@ var AugmentcodeIgnore = class AugmentcodeIgnore extends ToolIgnore {
10462
10462
  }
10463
10463
  };
10464
10464
  //#endregion
10465
+ //#region src/features/claudecode-settings-gateway.ts
10466
+ /**
10467
+ * Single owner of the `.claude/settings.json` `permissions` block, which both
10468
+ * `ignore` (writes `Read(...)` into `permissions.deny`) and `permissions`
10469
+ * (writes the whole `allow`/`ask`/`deny`) read-modify-write. The entry format,
10470
+ * the merge, and the cross-feature ownership rule (permissions' explicit `Read`
10471
+ * rules win over ignore-derived `Read` denies) used to be duplicated across both
10472
+ * feature files; they live here once so each feature just states its intent and
10473
+ * never reasons about the other's existence.
10474
+ */
10475
+ const READ_TOOL_NAME = "Read";
10476
+ const isReadDenyEntry = (entry) => entry.startsWith(`${READ_TOOL_NAME}(`) && entry.endsWith(")");
10477
+ const buildReadDenyEntry = (pattern) => `${READ_TOOL_NAME}(${pattern})`;
10478
+ const parsePermissionsBlock = (settings) => {
10479
+ const permissions = settings.permissions ?? {};
10480
+ return {
10481
+ allow: permissions.allow ?? [],
10482
+ ask: permissions.ask ?? [],
10483
+ deny: permissions.deny ?? []
10484
+ };
10485
+ };
10486
+ const withPermissions = (settings, next) => {
10487
+ const permissions = { ...settings.permissions };
10488
+ const assign = (key, values) => {
10489
+ if (values.length > 0) permissions[key] = values;
10490
+ else delete permissions[key];
10491
+ };
10492
+ assign("allow", next.allow);
10493
+ assign("ask", next.ask);
10494
+ assign("deny", next.deny);
10495
+ return {
10496
+ ...settings,
10497
+ permissions
10498
+ };
10499
+ };
10500
+ const applyIgnoreReadDenies = (params) => {
10501
+ const { settings, readDenies } = params;
10502
+ const current = parsePermissionsBlock(settings);
10503
+ const preservedDeny = current.deny.filter((entry) => !isReadDenyEntry(entry) || readDenies.includes(entry));
10504
+ return withPermissions(settings, {
10505
+ allow: current.allow,
10506
+ ask: current.ask,
10507
+ deny: uniq([...preservedDeny, ...readDenies].toSorted())
10508
+ });
10509
+ };
10510
+ const applyPermissions = (params) => {
10511
+ const { settings, managedToolNames, toolNameOf, allow, ask, deny, logger } = params;
10512
+ const current = parsePermissionsBlock(settings);
10513
+ const keepUnmanaged = (entries) => entries.filter((entry) => !managedToolNames.has(toolNameOf(entry)));
10514
+ if (logger && managedToolNames.has(READ_TOOL_NAME)) {
10515
+ const overwrittenReadDenies = current.deny.filter((entry) => toolNameOf(entry) === READ_TOOL_NAME);
10516
+ if (overwrittenReadDenies.length > 0) logger.warn(`Permissions feature manages '${READ_TOOL_NAME}' tool and will overwrite ${overwrittenReadDenies.length} existing ${READ_TOOL_NAME} deny entries. Permissions take precedence.`);
10517
+ }
10518
+ return withPermissions(settings, {
10519
+ allow: uniq([...keepUnmanaged(current.allow), ...allow].toSorted()),
10520
+ ask: uniq([...keepUnmanaged(current.ask), ...ask].toSorted()),
10521
+ deny: uniq([...keepUnmanaged(current.deny), ...deny].toSorted())
10522
+ });
10523
+ };
10524
+ //#endregion
10465
10525
  //#region src/features/ignore/claudecode-ignore.ts
10466
10526
  const DEFAULT_FILE_MODE = "shared";
10467
10527
  /**
@@ -10508,7 +10568,7 @@ var ClaudecodeIgnore = class ClaudecodeIgnore extends ToolIgnore {
10508
10568
  }
10509
10569
  toRulesyncIgnore() {
10510
10570
  const fileContent = this.patterns.map((pattern) => {
10511
- if (pattern.startsWith("Read(") && pattern.endsWith(")")) return pattern.slice(5, -1);
10571
+ if (isReadDenyEntry(pattern)) return pattern.slice(5, -1);
10512
10572
  return pattern;
10513
10573
  }).filter((pattern) => pattern.length > 0).join("\n");
10514
10574
  return new RulesyncIgnore({
@@ -10519,22 +10579,20 @@ var ClaudecodeIgnore = class ClaudecodeIgnore extends ToolIgnore {
10519
10579
  });
10520
10580
  }
10521
10581
  static async fromRulesyncIgnore({ outputRoot = process.cwd(), rulesyncIgnore, options }) {
10522
- const deniedValues = rulesyncIgnore.getFileContent().split(/\r?\n|\r/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).map((pattern) => `Read(${pattern})`);
10582
+ const deniedValues = rulesyncIgnore.getFileContent().split(/\r?\n|\r/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).map((pattern) => buildReadDenyEntry(pattern));
10523
10583
  const paths = this.getSettablePaths({ options });
10524
10584
  const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
10525
10585
  const existingFileContent = await fileExists(filePath) ? await readFileContent(filePath) : "{}";
10526
- const existingJsonValue = JSON.parse(existingFileContent);
10527
- const preservedDenies = (existingJsonValue.permissions?.deny ?? []).filter((deny) => {
10528
- if (deny.startsWith("Read(") && deny.endsWith(")")) return deniedValues.includes(deny);
10529
- return true;
10586
+ let existingJsonValue;
10587
+ try {
10588
+ existingJsonValue = JSON.parse(existingFileContent);
10589
+ } catch (error) {
10590
+ throw new Error(`Failed to parse existing Claude settings at ${filePath}: ${formatError(error)}`, { cause: error });
10591
+ }
10592
+ const jsonValue = applyIgnoreReadDenies({
10593
+ settings: existingJsonValue,
10594
+ readDenies: deniedValues
10530
10595
  });
10531
- const jsonValue = {
10532
- ...existingJsonValue,
10533
- permissions: {
10534
- ...existingJsonValue.permissions,
10535
- deny: uniq([...preservedDenies, ...deniedValues].toSorted())
10536
- }
10537
- };
10538
10596
  return new ClaudecodeIgnore({
10539
10597
  outputRoot,
10540
10598
  relativeDirPath: paths.relativeDirPath,
@@ -17071,31 +17129,15 @@ var ClaudecodePermissions = class ClaudecodePermissions extends ToolPermissions
17071
17129
  const config = rulesyncPermissions.getJson();
17072
17130
  const { allow, ask, deny } = convertRulesyncToClaudePermissions(config);
17073
17131
  const managedToolNames = new Set(Object.keys(config.permission).map((category) => toClaudeToolName(category)));
17074
- const existingPermissions = settings.permissions ?? {};
17075
- const preservedAllow = (existingPermissions.allow ?? []).filter((entry) => !managedToolNames.has(parseClaudePermissionEntry(entry).toolName));
17076
- const preservedAsk = (existingPermissions.ask ?? []).filter((entry) => !managedToolNames.has(parseClaudePermissionEntry(entry).toolName));
17077
- const preservedDeny = (existingPermissions.deny ?? []).filter((entry) => !managedToolNames.has(parseClaudePermissionEntry(entry).toolName));
17078
- if (logger && managedToolNames.has("Read")) {
17079
- const droppedReadDenyEntries = (existingPermissions.deny ?? []).filter((entry) => {
17080
- const { toolName } = parseClaudePermissionEntry(entry);
17081
- return toolName === "Read";
17082
- });
17083
- if (droppedReadDenyEntries.length > 0) logger.warn(`Permissions feature manages 'Read' tool and will overwrite ${droppedReadDenyEntries.length} existing Read deny entries (possibly from ignore feature). Permissions take precedence.`);
17084
- }
17085
- const mergedPermissions = { ...existingPermissions };
17086
- const mergedAllow = uniq([...preservedAllow, ...allow].toSorted());
17087
- const mergedAsk = uniq([...preservedAsk, ...ask].toSorted());
17088
- const mergedDeny = uniq([...preservedDeny, ...deny].toSorted());
17089
- if (mergedAllow.length > 0) mergedPermissions.allow = mergedAllow;
17090
- else delete mergedPermissions.allow;
17091
- if (mergedAsk.length > 0) mergedPermissions.ask = mergedAsk;
17092
- else delete mergedPermissions.ask;
17093
- if (mergedDeny.length > 0) mergedPermissions.deny = mergedDeny;
17094
- else delete mergedPermissions.deny;
17095
- const merged = {
17096
- ...settings,
17097
- permissions: mergedPermissions
17098
- };
17132
+ const merged = applyPermissions({
17133
+ settings,
17134
+ managedToolNames,
17135
+ toolNameOf: (entry) => parseClaudePermissionEntry(entry).toolName,
17136
+ allow,
17137
+ ask,
17138
+ deny,
17139
+ logger
17140
+ });
17099
17141
  const fileContent = JSON.stringify(merged, null, 2);
17100
17142
  return new ClaudecodePermissions({
17101
17143
  outputRoot,
@@ -37065,4 +37107,4 @@ async function importPermissionsCore(params) {
37065
37107
  //#endregion
37066
37108
  export { CLIError as $, IgnoreProcessor as A, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as At, toolCommandFactories as B, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Bt, PermissionsProcessorToolTargetSchema as C, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as Ct, McpProcessorToolTargetSchema as D, RULESYNC_HOOKS_FILE_NAME as Dt, McpProcessor as E, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as Et, HooksProcessorToolTargetSchema as F, RULESYNC_PERMISSIONS_FILE_NAME as Ft, CLAUDECODE_SKILLS_DIR_PATH as G, CLAUDECODE_LOCAL_RULE_FILE_NAME as H, formatError as Ht, toolHooksFactories as I, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as It, stringifyFrontmatter as J, RulesyncCommand as K, RulesyncHooks as L, RULESYNC_RELATIVE_DIR_PATH as Lt, toolIgnoreFactories as M, RULESYNC_MCP_RELATIVE_FILE_PATH as Mt, RulesyncIgnore as N, RULESYNC_MCP_SCHEMA_URL as Nt, toolMcpFactories as O, RULESYNC_HOOKS_RELATIVE_FILE_PATH as Ot, HooksProcessor as P, RULESYNC_OVERVIEW_FILE_NAME as Pt, JsonLogger as Q, CommandsProcessor as R, RULESYNC_RULES_RELATIVE_DIR_PATH as Rt, PermissionsProcessor as S, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as St, RulesyncPermissions as T, RULESYNC_CONFIG_SCHEMA_URL as Tt, CLAUDECODE_MEMORIES_DIR_NAME as U, ALL_FEATURES as Ut, CLAUDECODE_DIR as V, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as Vt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as W, ALL_FEATURES_WITH_WILDCARD as Wt, findControlCharacter as X, ConfigResolver as Y, ConsoleLogger as Z, toolSkillFactories as _, ALL_TOOL_TARGETS as _t, RulesProcessor as a, fileExists as at, RulesyncSkillFrontmatterSchema as b, MAX_FILE_SIZE as bt, RulesyncRule as c, getHomeDirectory as ct, SubagentsProcessorToolTargetSchema as d, readFileContent as dt, ErrorCodes as et, toolSubagentFactories as f, removeDirectory as ft, SkillsProcessorToolTargetSchema as g, writeFileContent as gt, SkillsProcessor as h, toPosixPath as ht, convertFromTool as i, ensureDir as it, IgnoreProcessorToolTargetSchema as j, RULESYNC_MCP_FILE_NAME as jt, RulesyncMcp as k, RULESYNC_IGNORE_RELATIVE_FILE_PATH as kt, RulesyncRuleFrontmatterSchema as l, isSymlink as lt, RulesyncSubagentFrontmatterSchema as m, removeTempDirectory as mt, checkRulesyncDirExists as n, createTempDirectory as nt, RulesProcessorToolTargetSchema as o, findFilesByGlobs as ot, RulesyncSubagent as p, removeFile as pt, RulesyncCommandFrontmatterSchema as q, generate as r, directoryExists as rt, toolRuleFactories as s, getFileSize as st, importFromTool as t, checkPathTraversal as tt, SubagentsProcessor as u, listDirectoryFiles as ut, getLocalSkillDirNames as v, ALL_TOOL_TARGETS_WITH_WILDCARD as vt, toolPermissionsFactories as w, RULESYNC_CONFIG_RELATIVE_FILE_PATH as wt, SKILL_FILE_NAME as x, RULESYNC_AIIGNORE_FILE_NAME as xt, RulesyncSkill as y, ToolTargetSchema as yt, CommandsProcessorToolTargetSchema as z, RULESYNC_SKILLS_RELATIVE_DIR_PATH as zt };
37067
37109
 
37068
- //# sourceMappingURL=import-DywcWsgJ.js.map
37110
+ //# sourceMappingURL=import-BkxwFCDM.js.map