axconfig 3.5.0 → 3.5.1

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.
@@ -47,10 +47,25 @@ export declare function collectTools(rules: PermissionRule[]): Set<CanonicalTool
47
47
  export declare function collectBashPatterns(rules: PermissionRule[]): string[];
48
48
  /**
49
49
  * Build bash permission config from patterns and tool permissions.
50
+ *
51
+ * IMPORTANT: The wildcard "*" rule must be added FIRST in the config object.
52
+ * OpenCode's `disabled()` function uses `findLast()` to find any rule matching
53
+ * the permission name, then checks if that rule has `pattern === "*"` and
54
+ * `action === "deny"`. If the "*" rule is last, bash gets disabled entirely
55
+ * even when specific patterns are allowed. By adding "*" first, `findLast()`
56
+ * returns a specific pattern rule instead, keeping bash available to the model.
57
+ *
58
+ * At execution time, `evaluate()` correctly handles rule precedence because it
59
+ * uses `findLast()` with BOTH permission AND pattern matching - so specific
60
+ * patterns still take priority over the wildcard when the command matches.
50
61
  */
51
62
  export declare function buildBashPermission(allowPatterns: string[], denyPatterns: string[], bashAllowed: boolean, bashDenied: boolean): OpenCodePermission | Record<string, OpenCodePermission>;
52
63
  /**
53
64
  * Build tool permission config with optional path patterns.
65
+ *
66
+ * IMPORTANT: Like buildBashPermission, the wildcard "*" rule must be added
67
+ * FIRST to prevent OpenCode's disabled() from hiding the tool entirely.
68
+ * See buildBashPermission comment for detailed explanation.
54
69
  */
55
70
  export declare function buildToolPermission(canonicalName: CanonicalTool, opencodeName: string, allowedTools: Set<CanonicalTool>, deniedTools: Set<CanonicalTool>, allowPathPatterns: Map<string, string[]>, denyPathPatterns: Map<string, string[]>): OpenCodePermission | Record<string, OpenCodePermission>;
56
71
  /**
@@ -78,26 +78,44 @@ export function collectBashPatterns(rules) {
78
78
  }
79
79
  /**
80
80
  * Build bash permission config from patterns and tool permissions.
81
+ *
82
+ * IMPORTANT: The wildcard "*" rule must be added FIRST in the config object.
83
+ * OpenCode's `disabled()` function uses `findLast()` to find any rule matching
84
+ * the permission name, then checks if that rule has `pattern === "*"` and
85
+ * `action === "deny"`. If the "*" rule is last, bash gets disabled entirely
86
+ * even when specific patterns are allowed. By adding "*" first, `findLast()`
87
+ * returns a specific pattern rule instead, keeping bash available to the model.
88
+ *
89
+ * At execution time, `evaluate()` correctly handles rule precedence because it
90
+ * uses `findLast()` with BOTH permission AND pattern matching - so specific
91
+ * patterns still take priority over the wildcard when the command matches.
81
92
  */
82
93
  export function buildBashPermission(allowPatterns, denyPatterns, bashAllowed, bashDenied) {
83
94
  if (bashDenied && allowPatterns.length === 0) {
84
95
  return "deny";
85
96
  }
86
97
  if (allowPatterns.length > 0 || denyPatterns.length > 0) {
87
- const bashConfig = {};
98
+ // Add wildcard FIRST so specific patterns come after in ruleset order.
99
+ // This prevents OpenCode's disabled() from hiding bash from the model.
100
+ const bashConfig = {
101
+ "*": bashAllowed ? "allow" : "deny",
102
+ };
88
103
  for (const pattern of allowPatterns) {
89
104
  bashConfig[pattern] = "allow";
90
105
  }
91
106
  for (const pattern of denyPatterns) {
92
107
  bashConfig[pattern] = "deny";
93
108
  }
94
- bashConfig["*"] = bashAllowed ? "allow" : "deny";
95
109
  return bashConfig;
96
110
  }
97
111
  return bashAllowed ? "allow" : "deny";
98
112
  }
99
113
  /**
100
114
  * Build tool permission config with optional path patterns.
115
+ *
116
+ * IMPORTANT: Like buildBashPermission, the wildcard "*" rule must be added
117
+ * FIRST to prevent OpenCode's disabled() from hiding the tool entirely.
118
+ * See buildBashPermission comment for detailed explanation.
101
119
  */
102
120
  export function buildToolPermission(canonicalName, opencodeName, allowedTools, deniedTools, allowPathPatterns, denyPathPatterns) {
103
121
  const hasAllowPatterns = allowPathPatterns.has(opencodeName);
@@ -114,14 +132,17 @@ export function buildToolPermission(canonicalName, opencodeName, allowedTools, d
114
132
  return "deny";
115
133
  }
116
134
  if (hasAllowPatterns || hasDenyPatterns) {
117
- const patternConfig = {};
135
+ // Add wildcard FIRST so specific patterns come after in ruleset order.
136
+ // This prevents OpenCode's disabled() from hiding the tool from the model.
137
+ const patternConfig = {
138
+ "*": effectiveAllowed ? "allow" : "deny",
139
+ };
118
140
  for (const pattern of allowPathPatterns.get(opencodeName) ?? []) {
119
141
  patternConfig[pattern] = "allow";
120
142
  }
121
143
  for (const pattern of denyPathPatterns.get(opencodeName) ?? []) {
122
144
  patternConfig[pattern] = "deny";
123
145
  }
124
- patternConfig["*"] = effectiveAllowed ? "allow" : "deny";
125
146
  return patternConfig;
126
147
  }
127
148
  return effectiveAllowed ? "allow" : "deny";
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "axconfig",
3
3
  "author": "Łukasz Jerciński",
4
4
  "license": "MIT",
5
- "version": "3.5.0",
5
+ "version": "3.5.1",
6
6
  "description": "Unified configuration management for AI coding agents - common API for permissions, settings, and config across Claude Code, Codex, Gemini CLI, and OpenCode",
7
7
  "repository": {
8
8
  "type": "git",