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.
@@ -10489,6 +10489,66 @@ var AugmentcodeIgnore = class AugmentcodeIgnore extends ToolIgnore {
10489
10489
  }
10490
10490
  };
10491
10491
  //#endregion
10492
+ //#region src/features/claudecode-settings-gateway.ts
10493
+ /**
10494
+ * Single owner of the `.claude/settings.json` `permissions` block, which both
10495
+ * `ignore` (writes `Read(...)` into `permissions.deny`) and `permissions`
10496
+ * (writes the whole `allow`/`ask`/`deny`) read-modify-write. The entry format,
10497
+ * the merge, and the cross-feature ownership rule (permissions' explicit `Read`
10498
+ * rules win over ignore-derived `Read` denies) used to be duplicated across both
10499
+ * feature files; they live here once so each feature just states its intent and
10500
+ * never reasons about the other's existence.
10501
+ */
10502
+ const READ_TOOL_NAME = "Read";
10503
+ const isReadDenyEntry = (entry) => entry.startsWith(`${READ_TOOL_NAME}(`) && entry.endsWith(")");
10504
+ const buildReadDenyEntry = (pattern) => `${READ_TOOL_NAME}(${pattern})`;
10505
+ const parsePermissionsBlock = (settings) => {
10506
+ const permissions = settings.permissions ?? {};
10507
+ return {
10508
+ allow: permissions.allow ?? [],
10509
+ ask: permissions.ask ?? [],
10510
+ deny: permissions.deny ?? []
10511
+ };
10512
+ };
10513
+ const withPermissions = (settings, next) => {
10514
+ const permissions = { ...settings.permissions };
10515
+ const assign = (key, values) => {
10516
+ if (values.length > 0) permissions[key] = values;
10517
+ else delete permissions[key];
10518
+ };
10519
+ assign("allow", next.allow);
10520
+ assign("ask", next.ask);
10521
+ assign("deny", next.deny);
10522
+ return {
10523
+ ...settings,
10524
+ permissions
10525
+ };
10526
+ };
10527
+ const applyIgnoreReadDenies = (params) => {
10528
+ const { settings, readDenies } = params;
10529
+ const current = parsePermissionsBlock(settings);
10530
+ const preservedDeny = current.deny.filter((entry) => !isReadDenyEntry(entry) || readDenies.includes(entry));
10531
+ return withPermissions(settings, {
10532
+ allow: current.allow,
10533
+ ask: current.ask,
10534
+ deny: (0, es_toolkit.uniq)([...preservedDeny, ...readDenies].toSorted())
10535
+ });
10536
+ };
10537
+ const applyPermissions = (params) => {
10538
+ const { settings, managedToolNames, toolNameOf, allow, ask, deny, logger } = params;
10539
+ const current = parsePermissionsBlock(settings);
10540
+ const keepUnmanaged = (entries) => entries.filter((entry) => !managedToolNames.has(toolNameOf(entry)));
10541
+ if (logger && managedToolNames.has(READ_TOOL_NAME)) {
10542
+ const overwrittenReadDenies = current.deny.filter((entry) => toolNameOf(entry) === READ_TOOL_NAME);
10543
+ 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.`);
10544
+ }
10545
+ return withPermissions(settings, {
10546
+ allow: (0, es_toolkit.uniq)([...keepUnmanaged(current.allow), ...allow].toSorted()),
10547
+ ask: (0, es_toolkit.uniq)([...keepUnmanaged(current.ask), ...ask].toSorted()),
10548
+ deny: (0, es_toolkit.uniq)([...keepUnmanaged(current.deny), ...deny].toSorted())
10549
+ });
10550
+ };
10551
+ //#endregion
10492
10552
  //#region src/features/ignore/claudecode-ignore.ts
10493
10553
  const DEFAULT_FILE_MODE = "shared";
10494
10554
  /**
@@ -10535,7 +10595,7 @@ var ClaudecodeIgnore = class ClaudecodeIgnore extends ToolIgnore {
10535
10595
  }
10536
10596
  toRulesyncIgnore() {
10537
10597
  const fileContent = this.patterns.map((pattern) => {
10538
- if (pattern.startsWith("Read(") && pattern.endsWith(")")) return pattern.slice(5, -1);
10598
+ if (isReadDenyEntry(pattern)) return pattern.slice(5, -1);
10539
10599
  return pattern;
10540
10600
  }).filter((pattern) => pattern.length > 0).join("\n");
10541
10601
  return new RulesyncIgnore({
@@ -10546,22 +10606,20 @@ var ClaudecodeIgnore = class ClaudecodeIgnore extends ToolIgnore {
10546
10606
  });
10547
10607
  }
10548
10608
  static async fromRulesyncIgnore({ outputRoot = process.cwd(), rulesyncIgnore, options }) {
10549
- const deniedValues = rulesyncIgnore.getFileContent().split(/\r?\n|\r/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).map((pattern) => `Read(${pattern})`);
10609
+ const deniedValues = rulesyncIgnore.getFileContent().split(/\r?\n|\r/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).map((pattern) => buildReadDenyEntry(pattern));
10550
10610
  const paths = this.getSettablePaths({ options });
10551
10611
  const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
10552
10612
  const existingFileContent = await fileExists(filePath) ? await readFileContent(filePath) : "{}";
10553
- const existingJsonValue = JSON.parse(existingFileContent);
10554
- const preservedDenies = (existingJsonValue.permissions?.deny ?? []).filter((deny) => {
10555
- if (deny.startsWith("Read(") && deny.endsWith(")")) return deniedValues.includes(deny);
10556
- return true;
10613
+ let existingJsonValue;
10614
+ try {
10615
+ existingJsonValue = JSON.parse(existingFileContent);
10616
+ } catch (error) {
10617
+ throw new Error(`Failed to parse existing Claude settings at ${filePath}: ${formatError(error)}`, { cause: error });
10618
+ }
10619
+ const jsonValue = applyIgnoreReadDenies({
10620
+ settings: existingJsonValue,
10621
+ readDenies: deniedValues
10557
10622
  });
10558
- const jsonValue = {
10559
- ...existingJsonValue,
10560
- permissions: {
10561
- ...existingJsonValue.permissions,
10562
- deny: (0, es_toolkit.uniq)([...preservedDenies, ...deniedValues].toSorted())
10563
- }
10564
- };
10565
10623
  return new ClaudecodeIgnore({
10566
10624
  outputRoot,
10567
10625
  relativeDirPath: paths.relativeDirPath,
@@ -17098,31 +17156,15 @@ var ClaudecodePermissions = class ClaudecodePermissions extends ToolPermissions
17098
17156
  const config = rulesyncPermissions.getJson();
17099
17157
  const { allow, ask, deny } = convertRulesyncToClaudePermissions(config);
17100
17158
  const managedToolNames = new Set(Object.keys(config.permission).map((category) => toClaudeToolName(category)));
17101
- const existingPermissions = settings.permissions ?? {};
17102
- const preservedAllow = (existingPermissions.allow ?? []).filter((entry) => !managedToolNames.has(parseClaudePermissionEntry(entry).toolName));
17103
- const preservedAsk = (existingPermissions.ask ?? []).filter((entry) => !managedToolNames.has(parseClaudePermissionEntry(entry).toolName));
17104
- const preservedDeny = (existingPermissions.deny ?? []).filter((entry) => !managedToolNames.has(parseClaudePermissionEntry(entry).toolName));
17105
- if (logger && managedToolNames.has("Read")) {
17106
- const droppedReadDenyEntries = (existingPermissions.deny ?? []).filter((entry) => {
17107
- const { toolName } = parseClaudePermissionEntry(entry);
17108
- return toolName === "Read";
17109
- });
17110
- 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.`);
17111
- }
17112
- const mergedPermissions = { ...existingPermissions };
17113
- const mergedAllow = (0, es_toolkit.uniq)([...preservedAllow, ...allow].toSorted());
17114
- const mergedAsk = (0, es_toolkit.uniq)([...preservedAsk, ...ask].toSorted());
17115
- const mergedDeny = (0, es_toolkit.uniq)([...preservedDeny, ...deny].toSorted());
17116
- if (mergedAllow.length > 0) mergedPermissions.allow = mergedAllow;
17117
- else delete mergedPermissions.allow;
17118
- if (mergedAsk.length > 0) mergedPermissions.ask = mergedAsk;
17119
- else delete mergedPermissions.ask;
17120
- if (mergedDeny.length > 0) mergedPermissions.deny = mergedDeny;
17121
- else delete mergedPermissions.deny;
17122
- const merged = {
17123
- ...settings,
17124
- permissions: mergedPermissions
17125
- };
17159
+ const merged = applyPermissions({
17160
+ settings,
17161
+ managedToolNames,
17162
+ toolNameOf: (entry) => parseClaudePermissionEntry(entry).toolName,
17163
+ allow,
17164
+ ask,
17165
+ deny,
17166
+ logger
17167
+ });
17126
17168
  const fileContent = JSON.stringify(merged, null, 2);
17127
17169
  return new ClaudecodePermissions({
17128
17170
  outputRoot,
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_import = require("./import-cNqo0ZxT.cjs");
2
+ const require_import = require("./import-H0MKtu9x.cjs");
3
3
  //#region src/index.ts
4
4
  async function generate(options = {}) {
5
5
  const { silent = true, verbose = false, ...rest } = options;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Ut as ALL_FEATURES, Y as ConfigResolver, Z as ConsoleLogger, _t as ALL_TOOL_TARGETS, i as convertFromTool$1, n as checkRulesyncDirExists, r as generate$1, t as importFromTool$1 } from "./import-DywcWsgJ.js";
1
+ import { Ut as ALL_FEATURES, Y as ConfigResolver, Z as ConsoleLogger, _t as ALL_TOOL_TARGETS, i as convertFromTool$1, n as checkRulesyncDirExists, r as generate$1, t as importFromTool$1 } from "./import-BkxwFCDM.js";
2
2
  //#region src/index.ts
3
3
  async function generate(options = {}) {
4
4
  const { silent = true, verbose = false, ...rest } = options;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rulesync",
3
- "version": "9.1.1",
3
+ "version": "9.2.0",
4
4
  "description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
5
5
  "keywords": [
6
6
  "ai",
@@ -54,33 +54,33 @@
54
54
  "@toon-format/toon": "2.3.0",
55
55
  "@valibot/to-json-schema": "1.7.1",
56
56
  "commander": "15.0.0",
57
- "effect": "3.21.3",
58
- "es-toolkit": "1.47.1",
59
- "fastmcp": "4.3.0",
57
+ "effect": "3.21.4",
58
+ "es-toolkit": "1.49.0",
59
+ "fastmcp": "4.3.2",
60
60
  "globby": "16.2.0",
61
61
  "gray-matter": "4.0.3",
62
62
  "js-yaml": "4.2.0",
63
63
  "jsonc-parser": "3.3.1",
64
- "smol-toml": "1.6.1",
64
+ "smol-toml": "1.7.0",
65
65
  "sury": "10.0.4",
66
66
  "zod": "4.4.3"
67
67
  },
68
68
  "devDependencies": {
69
- "@anthropic-ai/claude-agent-sdk": "0.3.181",
70
- "@openrouter/sdk": "0.13.7",
69
+ "@anthropic-ai/claude-agent-sdk": "0.3.198",
70
+ "@openrouter/sdk": "0.13.22",
71
71
  "@secretlint/secretlint-rule-preset-recommend": "13.0.2",
72
72
  "@tsconfig/node24": "24.0.4",
73
73
  "@types/js-yaml": "4.0.9",
74
- "@types/node": "25.9.3",
75
- "@typescript/native-preview": "7.0.0-dev.20260617.2",
74
+ "@types/node": "26.1.0",
75
+ "@typescript/native-preview": "7.0.0-dev.20260701.1",
76
76
  "@vitest/coverage-v8": "4.1.9",
77
77
  "cspell": "10.0.1",
78
- "knip": "6.17.1",
79
- "lint-staged": "17.0.7",
80
- "oxfmt": "0.55.0",
81
- "oxlint": "1.70.0",
82
- "repomix": "1.14.1",
83
- "resend": "6.14.0",
78
+ "knip": "6.23.0",
79
+ "lint-staged": "17.0.8",
80
+ "oxfmt": "0.57.0",
81
+ "oxlint": "1.72.0",
82
+ "repomix": "1.16.0",
83
+ "resend": "6.16.0",
84
84
  "secretlint": "13.0.2",
85
85
  "simple-git": "3.36.0",
86
86
  "simple-git-hooks": "2.13.1",
@@ -88,7 +88,7 @@
88
88
  "tsdown": "0.22.3",
89
89
  "tsx": "4.22.4",
90
90
  "typescript": "6.0.3",
91
- "vite": "8.0.16",
91
+ "vite": "8.1.2",
92
92
  "vitepress": "1.6.4",
93
93
  "vitest": "4.1.9"
94
94
  },