rulesync 19.0.0 → 21.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.
@@ -5254,8 +5254,8 @@ const CONTINUE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
5254
5254
  /**
5255
5255
  * Hook events supported by Hermes Agent's native Shell Hooks system.
5256
5256
  *
5257
- * Hermes validates hook events against a fixed `VALID_HOOKS` set — 39 entries as
5258
- * of v0.21.3 (`v2026.9.14`); see {@link HERMESAGENT_NATIVE_HOOK_EVENTS} for the
5257
+ * Hermes validates hook events against a fixed `VALID_HOOKS` set — 41 entries as
5258
+ * of v0.21.5 (`v2026.9.24`); see {@link HERMESAGENT_NATIVE_HOOK_EVENTS} for the
5259
5259
  * full list. Only the events with a clean 1:1 canonical equivalent are mapped
5260
5260
  * here. All other native events round-trip through `hermesagent.hooks`.
5261
5261
  * @see https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/hooks.md
@@ -5279,13 +5279,14 @@ const HERMESAGENT_HOOK_EVENTS = [
5279
5279
  * observers, `gateway_platform_event` and `transform_api_error_classification`.
5280
5280
  * Shell hooks are gated on the same set.
5281
5281
  *
5282
- * Verified against the tag `v2026.9.14` (v0.21.3): `VALID_HOOKS` holds 39
5282
+ * Verified against the tag `v2026.9.24` (v0.21.5): `VALID_HOOKS` holds 41
5283
5283
  * entries, grown from 23 at `v2026.8.3` (v0.20.0) and 37 at `v2026.8.16`
5284
- * (v0.20.2) — v0.21 added `agent_loop_stopped` and `on_room_member_activity`.
5285
- * This list holds 38 of them — see
5284
+ * (v0.20.2) — v0.21 added `agent_loop_stopped` and `on_room_member_activity`,
5285
+ * and v0.21.5 added `pre_auxiliary_call` and `post_auxiliary_call`.
5286
+ * This list holds 40 of them — see
5286
5287
  * {@link HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS} for the exclusion.
5287
5288
  *
5288
- * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/hermes_cli/plugins.py
5289
+ * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.24/hermes_cli/plugins.py
5289
5290
  */
5290
5291
  const HERMESAGENT_NATIVE_HOOK_EVENTS = [
5291
5292
  "pre_tool_call",
@@ -5303,6 +5304,8 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
5303
5304
  "pre_api_request",
5304
5305
  "post_api_request",
5305
5306
  "api_request_error",
5307
+ "pre_auxiliary_call",
5308
+ "post_auxiliary_call",
5306
5309
  "on_session_start",
5307
5310
  "on_session_end",
5308
5311
  "on_session_finalize",
@@ -5337,8 +5340,8 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
5337
5340
  * rulesync only ever writes shell hooks, so these are excluded from
5338
5341
  * {@link HERMESAGENT_NATIVE_HOOK_EVENTS} and warned about with their own message.
5339
5342
  *
5340
- * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/hermes_cli/plugins.py — `SHELL_UNSUPPORTED_HOOKS`
5341
- * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/agent/shell_hooks.py — `_parse_hooks_block`
5343
+ * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.24/hermes_cli/plugins.py — `SHELL_UNSUPPORTED_HOOKS`
5344
+ * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.24/agent/shell_hooks.py — `_parse_hooks_block`
5342
5345
  */
5343
5346
  const HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS = ["transform_api_error_classification"];
5344
5347
  /**
@@ -26053,7 +26056,7 @@ function canonicalToHermesHooks({ config, toolOverrideHooks, logger }) {
26053
26056
  for (const [nativeEvent, definitions] of Object.entries(toolOverrideHooks ?? {})) {
26054
26057
  if (HERMESAGENT_CANONICAL_EVENTS.has(nativeEvent)) continue;
26055
26058
  if (HERMESAGENT_SHELL_UNSUPPORTED_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is Python-plugin-only; Hermes Agent refuses it for shell hooks, so the generated entry will never run.`);
26056
- else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.21.3; preserving it for forward compatibility.`);
26059
+ else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.21.5; preserving it for forward compatibility.`);
26057
26060
  setHermesHookEntries({
26058
26061
  result,
26059
26062
  event: nativeEvent,
@@ -44329,7 +44332,8 @@ function convertRulesyncToCodexProfile({ config, logger }) {
44329
44332
  applyDefaultGitWriteRules({
44330
44333
  config,
44331
44334
  filesystem,
44332
- workspaceRootFilesystem
44335
+ workspaceRootFilesystem,
44336
+ logger
44333
44337
  });
44334
44338
  if (Object.keys(workspaceRootFilesystem).length > 0) {
44335
44339
  const directWorkspaceRootsAccess = filesystem[CODEX_WORKSPACE_ROOTS_KEY];
@@ -44383,11 +44387,32 @@ function addCodexFilesystemRules({ categoryRules, filesystem, workspaceRootFiles
44383
44387
  });
44384
44388
  }
44385
44389
  }
44386
- function applyDefaultGitWriteRules({ config, filesystem, workspaceRootFilesystem }) {
44390
+ function applyDefaultGitWriteRules({ config, filesystem, workspaceRootFilesystem, logger }) {
44387
44391
  if (config.codexcli?.git_write_rules === false) return;
44388
44392
  if (config.codexcli?.base_permission_profile === CODEX_READ_ONLY_BASELINE) return;
44389
44393
  if (typeof filesystem[CODEX_WORKSPACE_ROOTS_KEY] === "string") return;
44390
- for (const [pattern, access] of Object.entries(CODEX_GIT_WRITE_RULES)) workspaceRootFilesystem[pattern] ??= access;
44394
+ for (const [pattern, access] of Object.entries(CODEX_GIT_WRITE_RULES)) {
44395
+ if (workspaceRootFilesystem[pattern] !== void 0) continue;
44396
+ if (isRestrictedInWorkspaceRootTable({
44397
+ pattern,
44398
+ table: workspaceRootFilesystem
44399
+ })) {
44400
+ logger?.warn(`Skipping the default Codex CLI "${pattern}" = "${access}" carve-out: ".git" is covered by a broader workspace-relative deny or read-only rule, which the carve-out would reopen. To keep it, author read and edit allows for "${pattern}"; to silence this warning, set codexcli.git_write_rules to false.`);
44401
+ continue;
44402
+ }
44403
+ workspaceRootFilesystem[pattern] = access;
44404
+ }
44405
+ }
44406
+ function isRestrictedInWorkspaceRootTable({ pattern, table }) {
44407
+ const readRules = {};
44408
+ for (const [tablePattern, access] of Object.entries(table)) {
44409
+ if (tablePattern === pattern) continue;
44410
+ readRules[tablePattern] = access === "write" ? "allow" : "deny";
44411
+ }
44412
+ return isCoveredByReadRestriction({
44413
+ pattern,
44414
+ readRules
44415
+ });
44391
44416
  }
44392
44417
  function convertCodexProfileToRulesync({ profile, domainsHadUnknown }) {
44393
44418
  const permission = {};
@@ -44401,7 +44426,10 @@ function convertCodexProfileToRulesync({ profile, domainsHadUnknown }) {
44401
44426
  continue;
44402
44427
  }
44403
44428
  if (isCodexFilesystemRuleTable(access)) for (const [nestedPattern, nestedAccess] of Object.entries(access)) {
44404
- if (pattern === CODEX_WORKSPACE_ROOTS_KEY && CODEX_GIT_WRITE_RULES[nestedPattern] === nestedAccess) continue;
44429
+ if (pattern === CODEX_WORKSPACE_ROOTS_KEY && CODEX_GIT_WRITE_RULES[nestedPattern] === nestedAccess && !isRestrictedInWorkspaceRootTable({
44430
+ pattern: nestedPattern,
44431
+ table: access
44432
+ })) continue;
44405
44433
  addRulesyncFilesystemRule(permission, nestedPattern, nestedAccess);
44406
44434
  }
44407
44435
  }
@@ -44983,14 +45011,14 @@ const COMMANDCODE_TOOL_TO_CATEGORY = {
44983
45011
  web_fetch: "webfetch",
44984
45012
  web_search: "websearch"
44985
45013
  };
44986
- const ACTION_RANK$1 = {
45014
+ const ACTION_RANK$2 = {
44987
45015
  allow: 0,
44988
45016
  ask: 1,
44989
45017
  deny: 2
44990
45018
  };
44991
45019
  /** Whether `action` outranks `existing` (deny > ask > allow). */
44992
45020
  function isStricterAction({ action, existing }) {
44993
- return existing === void 0 || ACTION_RANK$1[action] > ACTION_RANK$1[existing];
45021
+ return existing === void 0 || ACTION_RANK$2[action] > ACTION_RANK$2[existing];
44994
45022
  }
44995
45023
  /**
44996
45024
  * Why a padded `Shell( * )` cannot stand in for the bare `Shell` in `allow`:
@@ -48338,20 +48366,22 @@ function convertFactorydroidToRulesyncPermissions({ allow, deny }) {
48338
48366
  const GOOSE_GLOBAL_ONLY_MESSAGE = "Goose permissions are global-only; use --global to sync ~/.config/goose/permission.yaml";
48339
48367
  const GOOSE_USER_KEY = "user";
48340
48368
  const CATCH_ALL_PATTERN$4 = "*";
48341
- const RULESYNC_TO_GOOSE_TOOL_NAME = {
48342
- bash: "developer__shell",
48343
- edit: "developer__text_editor",
48344
- write: "developer__text_editor"
48345
- };
48346
- const GOOSE_TO_RULESYNC_TOOL_NAME = {
48347
- developer__shell: "bash",
48348
- developer__text_editor: "edit"
48369
+ const RULESYNC_TO_GOOSE_TOOL_NAME = { bash: "shell" };
48370
+ const GOOSE_TO_RULESYNC_TOOL_NAMES = {
48371
+ shell: ["bash"],
48372
+ developer__shell: ["bash"],
48373
+ developer__text_editor: ["edit", "write"]
48349
48374
  };
48350
48375
  const ACTION_TO_GOOSE_LIST = {
48351
48376
  allow: "always_allow",
48352
48377
  ask: "ask_before",
48353
48378
  deny: "never_allow"
48354
48379
  };
48380
+ const ACTION_RANK$1 = {
48381
+ allow: 0,
48382
+ ask: 1,
48383
+ deny: 2
48384
+ };
48355
48385
  const GOOSE_LIST_TO_ACTION = {
48356
48386
  always_allow: "allow",
48357
48387
  ask_before: "ask",
@@ -48376,10 +48406,11 @@ const GOOSE_PERMISSION_LIST_KEYS = [
48376
48406
  *
48377
48407
  * Mapping (rulesync canonical -> Goose):
48378
48408
  * - Action: `allow` -> `always_allow`, `ask` -> `ask_before`, `deny` -> `never_allow`.
48379
- * - Tool name: `bash` -> `developer__shell`, `edit` -> `developer__text_editor`;
48380
- * any other category passes through verbatim as the Goose tool name (mirrors
48381
- * the Gemini CLI adapter). `write` collapses onto `developer__text_editor`
48382
- * too, so a conflicting `edit`/`write` catch-all is reported and `edit` wins.
48409
+ * - Tool name: `bash` -> `shell`; any other category (including `edit` and
48410
+ * `write`) passes through verbatim as the Goose tool name (mirrors the
48411
+ * Gemini CLI adapter). The legacy `developer__shell` /
48412
+ * `developer__text_editor` names are still accepted on import, the latter
48413
+ * as both `edit` and `write`.
48383
48414
  * - Granularity: Goose lists hold whole tool names, so only a category's
48384
48415
  * catch-all `*` pattern is representable. Non-catch-all patterns cannot be
48385
48416
  * expressed per-tool and are reported via `logger.warn` and skipped.
@@ -48495,8 +48526,7 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
48495
48526
  never_allow: []
48496
48527
  };
48497
48528
  const assigned = /* @__PURE__ */ new Map();
48498
- const orderedEntries = Object.entries(honorAllToolsOnBash(config.permission)).toSorted(([a], [b]) => (a === "edit" ? 1 : 0) - (b === "edit" ? 1 : 0));
48499
- for (const [category, rules] of orderedEntries) {
48529
+ for (const [category, rules] of Object.entries(honorAllToolsOnBash(config.permission))) {
48500
48530
  const toolName = RULESYNC_TO_GOOSE_TOOL_NAME[category] ?? category;
48501
48531
  for (const [pattern, action] of Object.entries(rules)) {
48502
48532
  if (pattern !== CATCH_ALL_PATTERN$4) {
@@ -48504,12 +48534,15 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
48504
48534
  continue;
48505
48535
  }
48506
48536
  const previous = assigned.get(toolName);
48507
- if (previous !== void 0 && previous !== action) {
48508
- logger?.warn(`Goose maps "${category}" onto the "${toolName}" tool, which already has a conflicting permission ("${previous}"). The "${action}" value takes precedence.`);
48537
+ if (previous === action) continue;
48538
+ if (previous !== void 0) {
48539
+ const stricter = ACTION_RANK$1[action] > ACTION_RANK$1[previous] ? action : previous;
48540
+ logger?.warn(`Goose maps "${category}" onto the "${toolName}" tool, which already has a conflicting permission ("${previous}"). Keeping the stricter "${stricter}" (deny > ask > allow).`);
48541
+ if (stricter === previous) continue;
48509
48542
  const previousList = lists[ACTION_TO_GOOSE_LIST[previous]];
48510
48543
  const index = previousList.indexOf(toolName);
48511
48544
  if (index !== -1) previousList.splice(index, 1);
48512
- } else if (previous === action) continue;
48545
+ }
48513
48546
  lists[ACTION_TO_GOOSE_LIST[action]].push(toolName);
48514
48547
  assigned.set(toolName, action);
48515
48548
  }
@@ -48525,8 +48558,7 @@ function convertGoosePermissionConfigToRulesync(userPermission) {
48525
48558
  for (const key of GOOSE_PERMISSION_LIST_KEYS) {
48526
48559
  const toolNames = isStringArray$2(userPermission[key]) ? userPermission[key] : [];
48527
48560
  const action = GOOSE_LIST_TO_ACTION[key];
48528
- for (const toolName of toolNames) {
48529
- const category = GOOSE_TO_RULESYNC_TOOL_NAME[toolName] ?? toolName;
48561
+ for (const toolName of toolNames) for (const category of GOOSE_TO_RULESYNC_TOOL_NAMES[toolName] ?? [toolName]) {
48530
48562
  permission[category] ??= {};
48531
48563
  permission[category][CATCH_ALL_PATTERN$4] = action;
48532
48564
  }
@@ -5229,8 +5229,8 @@ const CONTINUE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
5229
5229
  /**
5230
5230
  * Hook events supported by Hermes Agent's native Shell Hooks system.
5231
5231
  *
5232
- * Hermes validates hook events against a fixed `VALID_HOOKS` set — 39 entries as
5233
- * of v0.21.3 (`v2026.9.14`); see {@link HERMESAGENT_NATIVE_HOOK_EVENTS} for the
5232
+ * Hermes validates hook events against a fixed `VALID_HOOKS` set — 41 entries as
5233
+ * of v0.21.5 (`v2026.9.24`); see {@link HERMESAGENT_NATIVE_HOOK_EVENTS} for the
5234
5234
  * full list. Only the events with a clean 1:1 canonical equivalent are mapped
5235
5235
  * here. All other native events round-trip through `hermesagent.hooks`.
5236
5236
  * @see https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/hooks.md
@@ -5254,13 +5254,14 @@ const HERMESAGENT_HOOK_EVENTS = [
5254
5254
  * observers, `gateway_platform_event` and `transform_api_error_classification`.
5255
5255
  * Shell hooks are gated on the same set.
5256
5256
  *
5257
- * Verified against the tag `v2026.9.14` (v0.21.3): `VALID_HOOKS` holds 39
5257
+ * Verified against the tag `v2026.9.24` (v0.21.5): `VALID_HOOKS` holds 41
5258
5258
  * entries, grown from 23 at `v2026.8.3` (v0.20.0) and 37 at `v2026.8.16`
5259
- * (v0.20.2) — v0.21 added `agent_loop_stopped` and `on_room_member_activity`.
5260
- * This list holds 38 of them — see
5259
+ * (v0.20.2) — v0.21 added `agent_loop_stopped` and `on_room_member_activity`,
5260
+ * and v0.21.5 added `pre_auxiliary_call` and `post_auxiliary_call`.
5261
+ * This list holds 40 of them — see
5261
5262
  * {@link HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS} for the exclusion.
5262
5263
  *
5263
- * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/hermes_cli/plugins.py
5264
+ * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.24/hermes_cli/plugins.py
5264
5265
  */
5265
5266
  const HERMESAGENT_NATIVE_HOOK_EVENTS = [
5266
5267
  "pre_tool_call",
@@ -5278,6 +5279,8 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
5278
5279
  "pre_api_request",
5279
5280
  "post_api_request",
5280
5281
  "api_request_error",
5282
+ "pre_auxiliary_call",
5283
+ "post_auxiliary_call",
5281
5284
  "on_session_start",
5282
5285
  "on_session_end",
5283
5286
  "on_session_finalize",
@@ -5312,8 +5315,8 @@ const HERMESAGENT_NATIVE_HOOK_EVENTS = [
5312
5315
  * rulesync only ever writes shell hooks, so these are excluded from
5313
5316
  * {@link HERMESAGENT_NATIVE_HOOK_EVENTS} and warned about with their own message.
5314
5317
  *
5315
- * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/hermes_cli/plugins.py — `SHELL_UNSUPPORTED_HOOKS`
5316
- * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.14/agent/shell_hooks.py — `_parse_hooks_block`
5318
+ * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.24/hermes_cli/plugins.py — `SHELL_UNSUPPORTED_HOOKS`
5319
+ * @see https://github.com/NousResearch/hermes-agent/blob/v2026.9.24/agent/shell_hooks.py — `_parse_hooks_block`
5317
5320
  */
5318
5321
  const HERMESAGENT_SHELL_UNSUPPORTED_HOOK_EVENTS = ["transform_api_error_classification"];
5319
5322
  /**
@@ -26028,7 +26031,7 @@ function canonicalToHermesHooks({ config, toolOverrideHooks, logger }) {
26028
26031
  for (const [nativeEvent, definitions] of Object.entries(toolOverrideHooks ?? {})) {
26029
26032
  if (HERMESAGENT_CANONICAL_EVENTS.has(nativeEvent)) continue;
26030
26033
  if (HERMESAGENT_SHELL_UNSUPPORTED_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is Python-plugin-only; Hermes Agent refuses it for shell hooks, so the generated entry will never run.`);
26031
- else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.21.3; preserving it for forward compatibility.`);
26034
+ else if (!HERMESAGENT_NATIVE_EVENTS.has(nativeEvent)) logger?.warn(`Hermes hook event "${nativeEvent}" is not documented by Hermes Agent v0.21.5; preserving it for forward compatibility.`);
26032
26035
  setHermesHookEntries({
26033
26036
  result,
26034
26037
  event: nativeEvent,
@@ -44304,7 +44307,8 @@ function convertRulesyncToCodexProfile({ config, logger }) {
44304
44307
  applyDefaultGitWriteRules({
44305
44308
  config,
44306
44309
  filesystem,
44307
- workspaceRootFilesystem
44310
+ workspaceRootFilesystem,
44311
+ logger
44308
44312
  });
44309
44313
  if (Object.keys(workspaceRootFilesystem).length > 0) {
44310
44314
  const directWorkspaceRootsAccess = filesystem[CODEX_WORKSPACE_ROOTS_KEY];
@@ -44358,11 +44362,32 @@ function addCodexFilesystemRules({ categoryRules, filesystem, workspaceRootFiles
44358
44362
  });
44359
44363
  }
44360
44364
  }
44361
- function applyDefaultGitWriteRules({ config, filesystem, workspaceRootFilesystem }) {
44365
+ function applyDefaultGitWriteRules({ config, filesystem, workspaceRootFilesystem, logger }) {
44362
44366
  if (config.codexcli?.git_write_rules === false) return;
44363
44367
  if (config.codexcli?.base_permission_profile === CODEX_READ_ONLY_BASELINE) return;
44364
44368
  if (typeof filesystem[CODEX_WORKSPACE_ROOTS_KEY] === "string") return;
44365
- for (const [pattern, access] of Object.entries(CODEX_GIT_WRITE_RULES)) workspaceRootFilesystem[pattern] ??= access;
44369
+ for (const [pattern, access] of Object.entries(CODEX_GIT_WRITE_RULES)) {
44370
+ if (workspaceRootFilesystem[pattern] !== void 0) continue;
44371
+ if (isRestrictedInWorkspaceRootTable({
44372
+ pattern,
44373
+ table: workspaceRootFilesystem
44374
+ })) {
44375
+ logger?.warn(`Skipping the default Codex CLI "${pattern}" = "${access}" carve-out: ".git" is covered by a broader workspace-relative deny or read-only rule, which the carve-out would reopen. To keep it, author read and edit allows for "${pattern}"; to silence this warning, set codexcli.git_write_rules to false.`);
44376
+ continue;
44377
+ }
44378
+ workspaceRootFilesystem[pattern] = access;
44379
+ }
44380
+ }
44381
+ function isRestrictedInWorkspaceRootTable({ pattern, table }) {
44382
+ const readRules = {};
44383
+ for (const [tablePattern, access] of Object.entries(table)) {
44384
+ if (tablePattern === pattern) continue;
44385
+ readRules[tablePattern] = access === "write" ? "allow" : "deny";
44386
+ }
44387
+ return isCoveredByReadRestriction({
44388
+ pattern,
44389
+ readRules
44390
+ });
44366
44391
  }
44367
44392
  function convertCodexProfileToRulesync({ profile, domainsHadUnknown }) {
44368
44393
  const permission = {};
@@ -44376,7 +44401,10 @@ function convertCodexProfileToRulesync({ profile, domainsHadUnknown }) {
44376
44401
  continue;
44377
44402
  }
44378
44403
  if (isCodexFilesystemRuleTable(access)) for (const [nestedPattern, nestedAccess] of Object.entries(access)) {
44379
- if (pattern === CODEX_WORKSPACE_ROOTS_KEY && CODEX_GIT_WRITE_RULES[nestedPattern] === nestedAccess) continue;
44404
+ if (pattern === CODEX_WORKSPACE_ROOTS_KEY && CODEX_GIT_WRITE_RULES[nestedPattern] === nestedAccess && !isRestrictedInWorkspaceRootTable({
44405
+ pattern: nestedPattern,
44406
+ table: access
44407
+ })) continue;
44380
44408
  addRulesyncFilesystemRule(permission, nestedPattern, nestedAccess);
44381
44409
  }
44382
44410
  }
@@ -44958,14 +44986,14 @@ const COMMANDCODE_TOOL_TO_CATEGORY = {
44958
44986
  web_fetch: "webfetch",
44959
44987
  web_search: "websearch"
44960
44988
  };
44961
- const ACTION_RANK$1 = {
44989
+ const ACTION_RANK$2 = {
44962
44990
  allow: 0,
44963
44991
  ask: 1,
44964
44992
  deny: 2
44965
44993
  };
44966
44994
  /** Whether `action` outranks `existing` (deny > ask > allow). */
44967
44995
  function isStricterAction({ action, existing }) {
44968
- return existing === void 0 || ACTION_RANK$1[action] > ACTION_RANK$1[existing];
44996
+ return existing === void 0 || ACTION_RANK$2[action] > ACTION_RANK$2[existing];
44969
44997
  }
44970
44998
  /**
44971
44999
  * Why a padded `Shell( * )` cannot stand in for the bare `Shell` in `allow`:
@@ -48313,20 +48341,22 @@ function convertFactorydroidToRulesyncPermissions({ allow, deny }) {
48313
48341
  const GOOSE_GLOBAL_ONLY_MESSAGE = "Goose permissions are global-only; use --global to sync ~/.config/goose/permission.yaml";
48314
48342
  const GOOSE_USER_KEY = "user";
48315
48343
  const CATCH_ALL_PATTERN$4 = "*";
48316
- const RULESYNC_TO_GOOSE_TOOL_NAME = {
48317
- bash: "developer__shell",
48318
- edit: "developer__text_editor",
48319
- write: "developer__text_editor"
48320
- };
48321
- const GOOSE_TO_RULESYNC_TOOL_NAME = {
48322
- developer__shell: "bash",
48323
- developer__text_editor: "edit"
48344
+ const RULESYNC_TO_GOOSE_TOOL_NAME = { bash: "shell" };
48345
+ const GOOSE_TO_RULESYNC_TOOL_NAMES = {
48346
+ shell: ["bash"],
48347
+ developer__shell: ["bash"],
48348
+ developer__text_editor: ["edit", "write"]
48324
48349
  };
48325
48350
  const ACTION_TO_GOOSE_LIST = {
48326
48351
  allow: "always_allow",
48327
48352
  ask: "ask_before",
48328
48353
  deny: "never_allow"
48329
48354
  };
48355
+ const ACTION_RANK$1 = {
48356
+ allow: 0,
48357
+ ask: 1,
48358
+ deny: 2
48359
+ };
48330
48360
  const GOOSE_LIST_TO_ACTION = {
48331
48361
  always_allow: "allow",
48332
48362
  ask_before: "ask",
@@ -48351,10 +48381,11 @@ const GOOSE_PERMISSION_LIST_KEYS = [
48351
48381
  *
48352
48382
  * Mapping (rulesync canonical -> Goose):
48353
48383
  * - Action: `allow` -> `always_allow`, `ask` -> `ask_before`, `deny` -> `never_allow`.
48354
- * - Tool name: `bash` -> `developer__shell`, `edit` -> `developer__text_editor`;
48355
- * any other category passes through verbatim as the Goose tool name (mirrors
48356
- * the Gemini CLI adapter). `write` collapses onto `developer__text_editor`
48357
- * too, so a conflicting `edit`/`write` catch-all is reported and `edit` wins.
48384
+ * - Tool name: `bash` -> `shell`; any other category (including `edit` and
48385
+ * `write`) passes through verbatim as the Goose tool name (mirrors the
48386
+ * Gemini CLI adapter). The legacy `developer__shell` /
48387
+ * `developer__text_editor` names are still accepted on import, the latter
48388
+ * as both `edit` and `write`.
48358
48389
  * - Granularity: Goose lists hold whole tool names, so only a category's
48359
48390
  * catch-all `*` pattern is representable. Non-catch-all patterns cannot be
48360
48391
  * expressed per-tool and are reported via `logger.warn` and skipped.
@@ -48470,8 +48501,7 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
48470
48501
  never_allow: []
48471
48502
  };
48472
48503
  const assigned = /* @__PURE__ */ new Map();
48473
- const orderedEntries = Object.entries(honorAllToolsOnBash(config.permission)).toSorted(([a], [b]) => (a === "edit" ? 1 : 0) - (b === "edit" ? 1 : 0));
48474
- for (const [category, rules] of orderedEntries) {
48504
+ for (const [category, rules] of Object.entries(honorAllToolsOnBash(config.permission))) {
48475
48505
  const toolName = RULESYNC_TO_GOOSE_TOOL_NAME[category] ?? category;
48476
48506
  for (const [pattern, action] of Object.entries(rules)) {
48477
48507
  if (pattern !== CATCH_ALL_PATTERN$4) {
@@ -48479,12 +48509,15 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
48479
48509
  continue;
48480
48510
  }
48481
48511
  const previous = assigned.get(toolName);
48482
- if (previous !== void 0 && previous !== action) {
48483
- logger?.warn(`Goose maps "${category}" onto the "${toolName}" tool, which already has a conflicting permission ("${previous}"). The "${action}" value takes precedence.`);
48512
+ if (previous === action) continue;
48513
+ if (previous !== void 0) {
48514
+ const stricter = ACTION_RANK$1[action] > ACTION_RANK$1[previous] ? action : previous;
48515
+ logger?.warn(`Goose maps "${category}" onto the "${toolName}" tool, which already has a conflicting permission ("${previous}"). Keeping the stricter "${stricter}" (deny > ask > allow).`);
48516
+ if (stricter === previous) continue;
48484
48517
  const previousList = lists[ACTION_TO_GOOSE_LIST[previous]];
48485
48518
  const index = previousList.indexOf(toolName);
48486
48519
  if (index !== -1) previousList.splice(index, 1);
48487
- } else if (previous === action) continue;
48520
+ }
48488
48521
  lists[ACTION_TO_GOOSE_LIST[action]].push(toolName);
48489
48522
  assigned.set(toolName, action);
48490
48523
  }
@@ -48500,8 +48533,7 @@ function convertGoosePermissionConfigToRulesync(userPermission) {
48500
48533
  for (const key of GOOSE_PERMISSION_LIST_KEYS) {
48501
48534
  const toolNames = isStringArray$2(userPermission[key]) ? userPermission[key] : [];
48502
48535
  const action = GOOSE_LIST_TO_ACTION[key];
48503
- for (const toolName of toolNames) {
48504
- const category = GOOSE_TO_RULESYNC_TOOL_NAME[toolName] ?? toolName;
48536
+ for (const toolName of toolNames) for (const category of GOOSE_TO_RULESYNC_TOOL_NAMES[toolName] ?? [toolName]) {
48505
48537
  permission[category] ??= {};
48506
48538
  permission[category][CATCH_ALL_PATTERN$4] = action;
48507
48539
  }
@@ -82584,4 +82616,4 @@ async function importChecksCore(params) {
82584
82616
  //#endregion
82585
82617
  export { RulesyncCommand as $, toPosixPath as $t, CLAUDECODE_MEMORIES_DIR_NAME as A, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as An, checkPathTraversal as At, RulesyncSubagent as B, stripControlCharacters as Bn, listDirectoryEntryNames as Bt, COMMANDCODE_DIR as C, RULESYNC_PERMISSIONS_SCHEMA_URL as Cn, withWarnOnceScope as Ct, CODEXCLI_DIR as D, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Dn, assertDirectoryIfExists as Dt, CODEXCLI_BASH_RULES_FILE_NAME as E, RULESYNC_SKILLS_RELATIVE_DIR_PATH as En, applyFileMode as Et, caseFoldIdentity as F, formatError as Fn, getFileSize as Ft, RulesyncRuleFrontmatterSchema as G, readFileContentOrNull as Gt, RulesyncSkill as H, stripHiddenCharacters as Hn, listSubdirectoryNames as Ht, groupSpellingsByCaseFoldedIdentity as I, truncateText as In, getHomeDirectory as It, RulesyncIgnore as J, removeFile as Jt, RulesyncPermissions as K, removeDirectory as Kt, AUGMENTCODE_DIR as L, hasDeceptiveHiddenCharacters as Ln, isFileNotFoundError as Lt, CLAUDECODE_SKILLS_DIR_PATH as M, ALL_FEATURES as Mn, directoryExists as Mt, FACTORYDROID_DIR as N, ALL_FEATURES_WITH_WILDCARD as Nn, ensureDir as Nt, CLAUDECODE_DIR as O, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as On, assertTreeContainsNoSymlinks as Ot, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as P, DEPRECATED_FEATURE_REPLACEMENTS as Pn, fileExists as Pt, parseJsonc as Q, runWithDirectoryRollback as Qt, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as R, hasEnclosingMarkOutsideKeycap as Rn, isFileSystemError as Rt, QWENCODE_LOCAL_RULE_FILE_NAME as S, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as Sn, resetRunWarningState as St, ChecksProcessor as T, RULESYNC_RULES_RELATIVE_DIR_PATH as Tn, ErrorCodes as Tt, RulesyncSkillFrontmatterSchema as U, pathEscapesRoot as Ut, RulesyncSubagentFrontmatterSchema as V, stripControlCharactersKeepingLineFeeds as Vn, listFilePathsRecursively as Vt, RulesyncRule as W, readFileContent as Wt, getRulesyncSourceCandidates as X, removeTempDirectory as Xt, RulesyncHooks as Y, removeFileStrict as Yt, resolveRulesyncSourceWritePath as Z, resolvePath as Zt, IgnoreProcessor as _, RULESYNC_IGNORE_RELATIVE_FILE_PATH as _n, JsonLogger as _t, getProcessorRegistryEntry as a, ToolTargetSchema as an, SHARED_USER_MANAGED_CONFIG_PATHS as at, CommandsProcessor as b, RULESYNC_MCP_SCHEMA_URL as bn, warnOnConflictingFlags as bt, RulesProcessor as c, MAX_FILE_SIZE as cn, mergeInputRootConfigs as ct, CODEBUDDY_DIR as d, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as dn, ConfigFileSchema as dt, writeFileBuffer as en, RulesyncCommandFrontmatterSchema as et, CODEBUDDY_LOCAL_RULE_FILE_NAME as f, RULESYNC_CONFIG_RELATIVE_FILE_PATH as fn, GITIGNORE_DESTINATION_KEY as ft, McpProcessor as g, RULESYNC_HOOKS_RELATIVE_FILE_PATH as gn, ConsoleLogger as gt, shortenToWidth as h, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as hn, findControlCharacter as ht, inspectInputRoots as i, PACKAGING_TOOL_TARGETS as in, loadYaml as it, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as j, parseCommaSeparatedList as jn, createTempDirectory as jt, CLAUDECODE_LOCAL_RULE_FILE_NAME as k, RULESYNC_USER_CONFIG_DIR_NAME as kn, assertWritablePathInsideRoot as kt, SubagentsProcessor as l, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as ln, resolveEffectiveInputRoots as lt, displayWidthOf as m, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as mn, assertTargetsFeaturesExclusive as mt, formatSourceLoadFailure as n, ALL_TOOL_TARGETS as nn, RulesyncCheckFrontmatterSchema as nt, convertFromTool as o, CURATED_RULES_FEATURE_SUBDIR as on, SKILL_FILE_NAME as ot, ELLIPSIS_WIDTH as p, RULESYNC_CONFIG_SCHEMA_URL as pn, SourceEntrySchema as pt, RulesyncMcp as q, removeDirectoryStrict as qt, generate as r, ALL_TOOL_TARGETS_WITH_WILDCARD as rn, stringifyFrontmatter as rt, isPackagingToolTarget as s, FEATURE_SOURCE_TREE_ENTRIES as sn, ConfigResolver as st, importFromTool as t, writeFileContent as tn, RulesyncCheck as tt, SkillsProcessor as u, RULESYNC_CHECKS_RELATIVE_DIR_PATH as un, CONFLICTING_TARGET_PAIRS as ut, HooksProcessor as v, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as vn, WarningCollectingLogger as vt, COMMANDCODE_SETTINGS_LOCAL_FILE_NAME as w, RULESYNC_RELATIVE_DIR_PATH as wn, CLIError as wt, QWENCODE_DIR as x, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as xn, withFallbackLoggerTarget as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, RULESYNC_MCP_RELATIVE_FILE_PATH as yn, fallbackLogger as yt, getLocalSkillDirNames as z, quoteForLog as zn, isSymlink as zt };
82586
82618
 
82587
- //# sourceMappingURL=import-C-Jsxedt.js.map
82619
+ //# sourceMappingURL=import-CSKYRGa5.js.map