axconfig 2.0.0 → 3.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.
package/README.md CHANGED
@@ -41,23 +41,23 @@ write:.env
41
41
 
42
42
  Translates unified permissions to agent-specific formats:
43
43
 
44
- | Agent | Output Format |
45
- | ----------- | --------------------------------------------------------- |
46
- | claude-code | JSON `settings.json` with `permissions.allow/deny` arrays |
47
- | codex | TOML `config.toml` + Starlark `.rules` files |
48
- | gemini | TOML policy files with `[[rule]]` entries |
49
- | opencode | JSON with `permission.{edit,bash,webfetch}` |
44
+ | Agent | Output Format |
45
+ | -------- | --------------------------------------------------------- |
46
+ | claude | JSON `settings.json` with `permissions.allow/deny` arrays |
47
+ | codex | TOML `config.toml` + Starlark `.rules` files |
48
+ | gemini | TOML policy files with `[[rule]]` entries |
49
+ | opencode | JSON with `permission.{edit,bash,webfetch}` |
50
50
 
51
51
  ### Capability Validation
52
52
 
53
53
  Each agent has different capabilities:
54
54
 
55
- | Agent | Tool Perms | Bash Patterns | Path Restrictions | Can Deny Read |
56
- | ----------- | ----------- | ------------- | ----------------- | ------------- |
57
- | claude-code | ✓ | ✓ | ✓ | ✓ |
58
- | codex | ✗ (sandbox) | ✓ | ✗ | ✗ |
59
- | gemini | ✓ | ✓ | ✗ | ✓ |
60
- | opencode | ✓ | ✓ | ✗ | ✓ |
55
+ | Agent | Tool Perms | Bash Patterns | Path Restrictions | Can Deny Read |
56
+ | -------- | ----------- | ------------- | ----------------- | ------------- |
57
+ | claude | ✓ | ✓ | ✓ | ✓ |
58
+ | codex | ✗ (sandbox) | ✓ | ✗ | ✗ |
59
+ | gemini | ✓ | ✓ | ✗ | ✓ |
60
+ | opencode | ✓ | ✓ | ✗ | ✓ |
61
61
 
62
62
  axconfig validates permissions against agent capabilities:
63
63
 
@@ -82,7 +82,7 @@ const permissions = parsePermissions(
82
82
 
83
83
  // Build agent-specific config
84
84
  const result = buildAgentConfig({
85
- agentId: "claude-code",
85
+ agentId: "claude",
86
86
  allow: "read,glob,bash:git *",
87
87
  deny: "bash:rm *",
88
88
  output: "/tmp/my-config", // directory for config files
@@ -94,8 +94,8 @@ if (result.ok) {
94
94
  }
95
95
 
96
96
  // Read existing config
97
- const reader = getConfigReader("claude-code");
98
- const configDir = resolveConfigPath("claude-code"); // ~/.claude/
97
+ const reader = getConfigReader("claude");
98
+ const configDir = resolveConfigPath("claude"); // ~/.claude/
99
99
 
100
100
  const perms = reader.readPermissions(configDir);
101
101
  if (perms.ok && perms.value) {
@@ -113,58 +113,58 @@ reader.writeRaw(configDir, "customSetting", { foo: "bar" });
113
113
 
114
114
  ```bash
115
115
  # Create config with permissions (outputs env vars)
116
- axconfig create --agent claude-code --output /tmp/config \
116
+ axconfig create --agent claude --output /tmp/config \
117
117
  --allow "read,glob,bash:git *" \
118
118
  --deny "bash:rm *"
119
119
 
120
120
  # Export for shell usage
121
- eval $(axconfig create --agent claude-code --output /tmp/config --allow read)
121
+ eval $(axconfig create --agent claude --output /tmp/config --allow read)
122
122
 
123
123
  # Export as JSON for parsing
124
- axconfig create --agent claude-code --output /tmp/cfg --allow read --format json | jq '.env'
124
+ axconfig create --agent claude --output /tmp/cfg --allow read --format json | jq '.env'
125
125
  ```
126
126
 
127
127
  ### Get/Set Unified Settings
128
128
 
129
129
  ```bash
130
130
  # Get current settings (uses agent's default config location)
131
- axconfig get --agent claude-code allow
131
+ axconfig get --agent claude allow
132
132
  # Output: read,glob,bash:git *
133
133
 
134
- axconfig get --agent claude-code deny
134
+ axconfig get --agent claude deny
135
135
  # Output: bash:rm *
136
136
 
137
- axconfig get --agent claude-code model
137
+ axconfig get --agent claude model
138
138
  # Output: sonnet
139
139
 
140
140
  # Get settings as JSON
141
- axconfig get --agent claude-code allow --format json
141
+ axconfig get --agent claude allow --format json
142
142
  # Output: ["Read", "Glob", {"Bash": {"command": "git", "args": "*"}}]
143
143
 
144
144
  # Set settings (merge with existing by default)
145
- axconfig set --agent claude-code allow "read,glob"
146
- axconfig set --agent claude-code deny "bash:rm *"
147
- axconfig set --agent claude-code model "claude-sonnet-4-20250514"
145
+ axconfig set --agent claude allow "read,glob"
146
+ axconfig set --agent claude deny "bash:rm *"
147
+ axconfig set --agent claude model "claude-sonnet-4-20250514"
148
148
 
149
149
  # Replace instead of merge (for allow/deny)
150
- axconfig set --agent claude-code allow "read,glob" --replace
150
+ axconfig set --agent claude allow "read,glob" --replace
151
151
 
152
152
  # Set with custom config path
153
- axconfig set --agent claude-code --path /tmp/cfg allow "read,glob"
153
+ axconfig set --agent claude --path /tmp/cfg allow "read,glob"
154
154
  ```
155
155
 
156
156
  ### Get/Set Raw Config Values
157
157
 
158
158
  ```bash
159
159
  # Get raw config value by dotted path
160
- axconfig get-raw --agent claude-code permissions.allow
160
+ axconfig get-raw --agent claude permissions.allow
161
161
  # Output: ["Read", "Glob"]
162
162
 
163
163
  # Get raw value as JSON
164
- axconfig get-raw --agent claude-code permissions --format json
164
+ axconfig get-raw --agent claude permissions --format json
165
165
 
166
166
  # Set raw config value (JSON or string)
167
- axconfig set-raw --agent claude-code permissions.allow '["Read", "Glob"]'
167
+ axconfig set-raw --agent claude permissions.allow '["Read", "Glob"]'
168
168
  axconfig set-raw --agent opencode permission.edit allow
169
169
  ```
170
170
 
@@ -172,29 +172,29 @@ axconfig set-raw --agent opencode permission.edit allow
172
172
 
173
173
  ```bash
174
174
  # Capture settings in shell variables
175
- ALLOW=$(axconfig get --agent claude-code allow)
176
- MODEL=$(axconfig get --agent claude-code model)
175
+ ALLOW=$(axconfig get --agent claude allow)
176
+ MODEL=$(axconfig get --agent claude model)
177
177
 
178
178
  # Extract allow rules as JSON and filter with jq
179
- axconfig get --agent claude-code allow --format json | jq '.'
179
+ axconfig get --agent claude allow --format json | jq '.'
180
180
 
181
181
  # List all bash permissions, one per line
182
- axconfig get --agent claude-code allow | tr ',' '\n' | grep 'bash:'
182
+ axconfig get --agent claude allow | tr ',' '\n' | grep 'bash:'
183
183
 
184
184
  # Count unique bash command prefixes
185
- axconfig get --agent claude-code allow \
185
+ axconfig get --agent claude allow \
186
186
  | tr ',' '\n' | grep 'bash:' \
187
187
  | cut -d: -f2 | cut -d' ' -f1 | sort | uniq -c | sort -rn
188
188
 
189
189
  # Compare permissions between agents
190
- diff <(axconfig get -a claude-code allow) <(axconfig get -a gemini allow)
190
+ diff <(axconfig get -a claude allow) <(axconfig get -a gemini allow)
191
191
 
192
192
  # Check if a specific permission exists
193
- axconfig get --agent claude-code allow | grep -q 'bash:git' && echo "git allowed"
193
+ axconfig get --agent claude allow | grep -q 'bash:git' && echo "git allowed"
194
194
 
195
195
  # Add a new permission to existing allow list
196
- CURRENT=$(axconfig get --agent claude-code allow)
197
- axconfig set --agent claude-code allow "$CURRENT,write" --replace
196
+ CURRENT=$(axconfig get --agent claude allow)
197
+ axconfig set --agent claude allow "$CURRENT,write" --replace
198
198
  ```
199
199
 
200
200
  ## Module Structure
@@ -215,7 +215,7 @@ src/
215
215
  │ ├── get-raw.ts # Get raw config values
216
216
  │ └── set-raw.ts # Set raw config values
217
217
  └── agents/
218
- ├── claude-code.ts # Claude Code config builder + reader
218
+ ├── claude.ts # Claude Code config builder + reader
219
219
  ├── codex.ts # Codex config builder + reader
220
220
  ├── gemini.ts # Gemini CLI config builder + reader
221
221
  └── opencode.ts # OpenCode config builder + reader
@@ -4,8 +4,8 @@
4
4
  * Reads and writes Claude Code configuration from settings.json.
5
5
  */
6
6
  import { mkdirSync } from "node:fs";
7
- import { homedir } from "node:os";
8
7
  import path from "node:path";
8
+ import { getAgentPathInfo } from "axshared";
9
9
  import { atomicWriteFileSync } from "../atomic-write.js";
10
10
  import { registerConfigReader } from "../reader.js";
11
11
  import { createJsonConfigOperations, readJsonConfig, } from "../read-write-json-config.js";
@@ -143,11 +143,15 @@ function writeModel(configDirectory, model) {
143
143
  };
144
144
  }
145
145
  }
146
+ // Get path info from axshared (single source of truth)
147
+ const pathInfo = getAgentPathInfo("claude");
146
148
  /** Claude Code ConfigReader */
147
149
  const claudeCodeConfigReader = {
148
150
  agentId: "claude",
149
- defaultConfigDir: () => path.join(homedir(), ".claude"),
150
- envVar: "CLAUDE_CONFIG_DIR",
151
+ defaultConfigDir: pathInfo.defaultConfigDir,
152
+ envVar: pathInfo.envVar,
153
+ subdirectory: pathInfo.subdirectory,
154
+ buildRuntimeEnvironment: pathInfo.buildRuntimeEnvironment,
151
155
  readPermissions,
152
156
  readModel,
153
157
  writeModel,
@@ -9,7 +9,7 @@
9
9
  * - Path patterns like "Read(src/**)"
10
10
  */
11
11
  import type { ConfigBuilder } from "../types.js";
12
- export { claudeCodeConfigReader } from "./claude-code-reader.js";
12
+ export { claudeCodeConfigReader } from "./claude-reader.js";
13
13
  /** Claude Code ConfigBuilder */
14
14
  declare const claudeCodeConfigBuilder: ConfigBuilder;
15
15
  export { claudeCodeConfigBuilder };
@@ -13,7 +13,7 @@ import path from "node:path";
13
13
  import { atomicWriteFileSync } from "../atomic-write.js";
14
14
  import { registerConfigBuilder } from "../builder.js";
15
15
  // Re-export reader
16
- export { claudeCodeConfigReader } from "./claude-code-reader.js";
16
+ export { claudeCodeConfigReader } from "./claude-reader.js";
17
17
  /** Claude Code tool name mapping */
18
18
  const TOOL_MAP = {
19
19
  read: "Read",
@@ -4,9 +4,9 @@
4
4
  * Reads and writes Codex configuration from config.toml and .rules files.
5
5
  */
6
6
  import { existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
7
- import { homedir } from "node:os";
8
7
  import path from "node:path";
9
8
  import TOML from "@iarna/toml";
9
+ import { getAgentPathInfo } from "axshared";
10
10
  import { atomicWriteFileSync } from "../atomic-write.js";
11
11
  import { registerConfigReader } from "../reader.js";
12
12
  import { createTomlConfigOperations, readTomlConfig, } from "../read-write-toml-config.js";
@@ -145,11 +145,15 @@ function writeModel(configDirectory, model) {
145
145
  return { ok: false, error: `Failed to write Codex model: ${message}` };
146
146
  }
147
147
  }
148
+ // Get path info from axshared (single source of truth)
149
+ const pathInfo = getAgentPathInfo("codex");
148
150
  /** Codex ConfigReader */
149
151
  const codexConfigReader = {
150
152
  agentId: "codex",
151
- defaultConfigDir: () => path.join(homedir(), ".codex"),
152
- envVar: "CODEX_HOME",
153
+ defaultConfigDir: pathInfo.defaultConfigDir,
154
+ envVar: pathInfo.envVar,
155
+ subdirectory: pathInfo.subdirectory,
156
+ buildRuntimeEnvironment: pathInfo.buildRuntimeEnvironment,
153
157
  readPermissions,
154
158
  readModel,
155
159
  writeModel,
@@ -68,7 +68,7 @@ function build(config, output) {
68
68
  rule,
69
69
  reason: "Codex sandbox mode always permits file reads",
70
70
  suggestions: [
71
- "Use a different agent that supports denying reads (e.g., claude-code)",
71
+ "Use a different agent that supports denying reads (e.g., claude)",
72
72
  'Remove the --deny "read" rule',
73
73
  ],
74
74
  });
@@ -80,7 +80,7 @@ function build(config, output) {
80
80
  reason: "Codex does not support path restrictions",
81
81
  suggestions: [
82
82
  `Use "${rule.tool}" to deny all ${rule.tool} operations`,
83
- "Use a different agent that supports path restrictions (e.g., claude-code)",
83
+ "Use a different agent that supports path restrictions (e.g., claude)",
84
84
  ],
85
85
  });
86
86
  }
@@ -8,33 +8,12 @@
8
8
  * - Does NOT support tool permissions in the axconfig sense
9
9
  */
10
10
  import { mkdirSync } from "node:fs";
11
- import { homedir } from "node:os";
12
11
  import path from "node:path";
12
+ import { getAgentPathInfo } from "axshared";
13
13
  import { atomicWriteFileSync } from "../atomic-write.js";
14
14
  import { deleteByPath, getByPath } from "../object-path.js";
15
15
  import { registerConfigReader } from "../reader.js";
16
16
  import { createJsonConfigOperations, readJsonConfig, } from "../read-write-json-config.js";
17
- /**
18
- * Get the platform-specific config directory for Copilot CLI.
19
- *
20
- * Copilot CLI uses an unconventional XDG pattern: `$XDG_CONFIG_HOME/.copilot`
21
- * (with the dot prefix inside XDG_CONFIG_HOME, unlike typical XDG conventions
22
- * which would use `$XDG_CONFIG_HOME/copilot` without the dot).
23
- *
24
- * Verified against decompiled Copilot CLI source (sdk/index.js:67574):
25
- * ```js
26
- * path.join(process.env.XDG_CONFIG_HOME, ".copilot")
27
- * ```
28
- *
29
- * Falls back to ~/.copilot when XDG_CONFIG_HOME is not set.
30
- */
31
- function getPlatformConfigDirectory() {
32
- const xdgConfig = process.env.XDG_CONFIG_HOME;
33
- if (xdgConfig) {
34
- return path.join(xdgConfig, ".copilot");
35
- }
36
- return path.join(homedir(), ".copilot");
37
- }
38
17
  /**
39
18
  * Get the config.json path for a config directory.
40
19
  */
@@ -103,11 +82,15 @@ function writeModel(configDirectory, model) {
103
82
  };
104
83
  }
105
84
  }
85
+ // Get path info from axshared (single source of truth)
86
+ const pathInfo = getAgentPathInfo("copilot");
106
87
  /** Copilot CLI ConfigReader */
107
88
  const copilotConfigReader = {
108
89
  agentId: "copilot",
109
- defaultConfigDir: getPlatformConfigDirectory,
110
- envVar: "GITHUB_COPILOT_CONFIG_DIR",
90
+ defaultConfigDir: pathInfo.defaultConfigDir,
91
+ envVar: pathInfo.envVar,
92
+ subdirectory: pathInfo.subdirectory,
93
+ buildRuntimeEnvironment: pathInfo.buildRuntimeEnvironment,
111
94
  readPermissions,
112
95
  readModel,
113
96
  writeModel,
@@ -43,7 +43,7 @@ function build(config, output) {
43
43
  reason: "Copilot CLI uses runtime approval prompts, not pre-configured allow lists",
44
44
  suggestions: [
45
45
  "Copilot CLI will prompt for approval when tools are used",
46
- "Use a different agent for pre-configured permissions (e.g., claude-code)",
46
+ "Use a different agent for pre-configured permissions (e.g., claude)",
47
47
  ],
48
48
  });
49
49
  }
@@ -53,7 +53,7 @@ function build(config, output) {
53
53
  rule,
54
54
  reason: "Copilot CLI does not support denying tools via configuration",
55
55
  suggestions: [
56
- "Use a different agent that supports deny rules (e.g., claude-code)",
56
+ "Use a different agent that supports deny rules (e.g., claude)",
57
57
  "Remove the deny rule",
58
58
  ],
59
59
  });
@@ -4,9 +4,9 @@
4
4
  * Reads and writes Gemini CLI configuration from settings.json and policy TOML files.
5
5
  */
6
6
  import { existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
7
- import { homedir } from "node:os";
8
7
  import path from "node:path";
9
8
  import TOML from "@iarna/toml";
9
+ import { getAgentPathInfo } from "axshared";
10
10
  import { atomicWriteFileSync } from "../atomic-write.js";
11
11
  import { registerConfigReader } from "../reader.js";
12
12
  import { createJsonConfigOperations, readJsonConfig, } from "../read-write-json-config.js";
@@ -141,11 +141,15 @@ function writeModel(configDirectory, model) {
141
141
  return { ok: false, error: `Failed to write Gemini CLI model: ${message}` };
142
142
  }
143
143
  }
144
+ // Get path info from axshared (single source of truth)
145
+ const pathInfo = getAgentPathInfo("gemini");
144
146
  /** Gemini CLI ConfigReader */
145
147
  const geminiConfigReader = {
146
148
  agentId: "gemini",
147
- defaultConfigDir: () => path.join(homedir(), ".gemini"),
148
- envVar: "GEMINI_DIR",
149
+ defaultConfigDir: pathInfo.defaultConfigDir,
150
+ envVar: pathInfo.envVar,
151
+ subdirectory: pathInfo.subdirectory,
152
+ buildRuntimeEnvironment: pathInfo.buildRuntimeEnvironment,
149
153
  readPermissions,
150
154
  readModel,
151
155
  writeModel,
@@ -106,7 +106,7 @@ function build(config, output) {
106
106
  reason: "Gemini CLI does not support path restrictions",
107
107
  suggestions: [
108
108
  `Use "${rule.tool}" to deny all ${rule.tool} operations`,
109
- "Use a different agent that supports path restrictions (e.g., claude-code)",
109
+ "Use a different agent that supports path restrictions (e.g., claude)",
110
110
  ],
111
111
  });
112
112
  }
@@ -4,8 +4,8 @@
4
4
  * Reads and writes OpenCode configuration from opencode.json.
5
5
  */
6
6
  import { mkdirSync } from "node:fs";
7
- import { homedir } from "node:os";
8
7
  import path from "node:path";
8
+ import { getAgentPathInfo } from "axshared";
9
9
  import { atomicWriteFileSync } from "../atomic-write.js";
10
10
  import { registerConfigReader } from "../reader.js";
11
11
  import { createJsonConfigOperations, readJsonConfig, } from "../read-write-json-config.js";
@@ -118,11 +118,15 @@ function writeModel(configDirectory, model) {
118
118
  return { ok: false, error: `Failed to write OpenCode model: ${message}` };
119
119
  }
120
120
  }
121
+ // Get path info from axshared (single source of truth)
122
+ const pathInfo = getAgentPathInfo("opencode");
121
123
  /** OpenCode ConfigReader */
122
124
  const openCodeConfigReader = {
123
125
  agentId: "opencode",
124
- defaultConfigDir: () => path.join(homedir(), ".opencode"),
125
- envVar: "OPENCODE_CONFIG_DIR",
126
+ defaultConfigDir: pathInfo.defaultConfigDir,
127
+ envVar: pathInfo.envVar,
128
+ subdirectory: pathInfo.subdirectory,
129
+ buildRuntimeEnvironment: pathInfo.buildRuntimeEnvironment,
126
130
  readPermissions,
127
131
  readModel,
128
132
  writeModel,
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import { existsSync, mkdirSync, readFileSync } from "node:fs";
12
12
  import path from "node:path";
13
+ import { getAgentConfigSubdirectory } from "axshared";
13
14
  import { atomicWriteFileSync } from "../atomic-write.js";
14
15
  import { registerConfigBuilder } from "../builder.js";
15
16
  // Re-export reader
@@ -51,7 +52,7 @@ function build(config, output) {
51
52
  reason: "OpenCode does not support path restrictions",
52
53
  suggestions: [
53
54
  `Use "${rule.tool}" to deny all ${rule.tool} operations`,
54
- "Use a different agent that supports path restrictions (e.g., claude-code)",
55
+ "Use a different agent that supports path restrictions (e.g., claude)",
55
56
  ],
56
57
  });
57
58
  }
@@ -61,7 +62,11 @@ function build(config, output) {
61
62
  if (errors.length > 0) {
62
63
  return { ok: false, errors };
63
64
  }
64
- const configPath = path.join(output, "opencode.json");
65
+ // OpenCode expects config at XDG_CONFIG_HOME/opencode/opencode.json
66
+ const subdirectory = getAgentConfigSubdirectory("opencode") ?? "opencode";
67
+ const openCodeDirectory = path.join(output, subdirectory);
68
+ mkdirSync(openCodeDirectory, { recursive: true });
69
+ const configPath = path.join(openCodeDirectory, "opencode.json");
65
70
  // Build permission config
66
71
  const permissionConfig = {};
67
72
  if (permissions) {
@@ -153,7 +158,10 @@ function build(config, output) {
153
158
  atomicWriteFileSync(configPath, JSON.stringify(openCodeConfig, undefined, 2));
154
159
  return {
155
160
  ok: true,
156
- env: { OPENCODE_CONFIG_DIR: output },
161
+ env: {
162
+ XDG_CONFIG_HOME: output,
163
+ XDG_DATA_HOME: output,
164
+ },
157
165
  warnings,
158
166
  };
159
167
  }
package/dist/cli.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Manages configurations for AI coding agents.
6
6
  */
7
- import "./agents/claude-code.js";
7
+ import "./agents/claude.js";
8
8
  import "./agents/codex.js";
9
9
  import "./agents/copilot.js";
10
10
  import "./agents/gemini.js";
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import { handleGetRaw } from "./commands/get-raw.js";
13
13
  import { handleSet } from "./commands/set.js";
14
14
  import { handleSetRaw } from "./commands/set-raw.js";
15
15
  // Import to trigger self-registration
16
- import "./agents/claude-code.js";
16
+ import "./agents/claude.js";
17
17
  import "./agents/codex.js";
18
18
  import "./agents/copilot.js";
19
19
  import "./agents/gemini.js";
@@ -33,29 +33,29 @@ const program = new Command()
33
33
  .addHelpText("after", String.raw `
34
34
  Examples:
35
35
  # Create config and source it in current shell
36
- eval $(axconfig create --agent claude-code --output /tmp/cfg --allow read)
36
+ eval $(axconfig create --agent claude --output /tmp/cfg --allow read)
37
37
 
38
38
  # Get current settings
39
- axconfig get --agent claude-code allow
40
- axconfig get --agent claude-code deny
41
- axconfig get --agent claude-code model
39
+ axconfig get --agent claude allow
40
+ axconfig get --agent claude deny
41
+ axconfig get --agent claude model
42
42
 
43
43
  # Set settings (merge with existing by default)
44
- axconfig set --agent claude-code allow "read,glob"
45
- axconfig set --agent claude-code deny "bash:rm *"
46
- axconfig set --agent claude-code model "claude-sonnet-4-20250514"
44
+ axconfig set --agent claude allow "read,glob"
45
+ axconfig set --agent claude deny "bash:rm *"
46
+ axconfig set --agent claude model "claude-sonnet-4-20250514"
47
47
 
48
48
  # Replace instead of merge
49
- axconfig set --agent claude-code allow "read,glob" --replace
49
+ axconfig set --agent claude allow "read,glob" --replace
50
50
 
51
51
  # Get raw config value
52
- axconfig get-raw --agent claude-code permissions.allow
52
+ axconfig get-raw --agent claude permissions.allow
53
53
 
54
54
  # Extract allow rules as JSON and filter with jq
55
- axconfig get --agent claude-code allow --format json | jq '.'
55
+ axconfig get --agent claude allow --format json | jq '.'
56
56
 
57
57
  # Capture in shell variable
58
- MODEL=$(axconfig get --agent claude-code model)`);
58
+ MODEL=$(axconfig get --agent claude model)`);
59
59
  program
60
60
  .command("create")
61
61
  .description("Create agent-specific configuration")
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Agent runtime environment utilities.
3
+ *
4
+ * Provides functions to compute environment variables for running agents
5
+ * with custom config directories. Delegates to axshared for path info.
6
+ */
7
+ import type { AgentCli } from "axshared";
8
+ /** Result type for getAgentConfigEnvironment */
9
+ type GetAgentConfigEnvironmentResult = {
10
+ ok: true;
11
+ env: Record<string, string>;
12
+ } | {
13
+ ok: false;
14
+ error: string;
15
+ };
16
+ /**
17
+ * Get agent-specific environment variables for a custom config directory.
18
+ *
19
+ * Computes the environment variables needed to run an agent with a specific
20
+ * config directory. This handles agents that require the env var to point to
21
+ * a parent directory (e.g., HOME for gemini, XDG_DATA_HOME for opencode).
22
+ *
23
+ * @param agent - The agent CLI identifier
24
+ * @param configDirectory - The config directory path (must end with expected suffix for some agents)
25
+ * @returns Result with agent-specific env vars, or error if configDirectory is invalid
26
+ *
27
+ * @example
28
+ * // Claude: env var points directly to config dir
29
+ * getAgentConfigEnvironment("claude", "/tmp/test/.claude")
30
+ * // Returns: { ok: true, env: { CLAUDE_CONFIG_DIR: "/tmp/test/.claude" } }
31
+ *
32
+ * // Gemini: env var points to parent (HOME), config is in .gemini subdirectory
33
+ * getAgentConfigEnvironment("gemini", "/tmp/test/.gemini")
34
+ * // Returns: { ok: true, env: { HOME: "/tmp/test", GEMINI_FORCE_FILE_STORAGE: "true" } }
35
+ */
36
+ declare function getAgentConfigEnvironment(agent: AgentCli, configDirectory: string): GetAgentConfigEnvironmentResult;
37
+ export { getAgentConfigEnvironment, type GetAgentConfigEnvironmentResult };
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Agent runtime environment utilities.
3
+ *
4
+ * Provides functions to compute environment variables for running agents
5
+ * with custom config directories. Delegates to axshared for path info.
6
+ */
7
+ import path from "node:path";
8
+ import { buildAgentRuntimeEnvironment, getAgentConfigSubdirectory, } from "axshared";
9
+ /**
10
+ * Get agent-specific environment variables for a custom config directory.
11
+ *
12
+ * Computes the environment variables needed to run an agent with a specific
13
+ * config directory. This handles agents that require the env var to point to
14
+ * a parent directory (e.g., HOME for gemini, XDG_DATA_HOME for opencode).
15
+ *
16
+ * @param agent - The agent CLI identifier
17
+ * @param configDirectory - The config directory path (must end with expected suffix for some agents)
18
+ * @returns Result with agent-specific env vars, or error if configDirectory is invalid
19
+ *
20
+ * @example
21
+ * // Claude: env var points directly to config dir
22
+ * getAgentConfigEnvironment("claude", "/tmp/test/.claude")
23
+ * // Returns: { ok: true, env: { CLAUDE_CONFIG_DIR: "/tmp/test/.claude" } }
24
+ *
25
+ * // Gemini: env var points to parent (HOME), config is in .gemini subdirectory
26
+ * getAgentConfigEnvironment("gemini", "/tmp/test/.gemini")
27
+ * // Returns: { ok: true, env: { HOME: "/tmp/test", GEMINI_FORCE_FILE_STORAGE: "true" } }
28
+ */
29
+ function getAgentConfigEnvironment(agent, configDirectory) {
30
+ const subdirectory = getAgentConfigSubdirectory(agent);
31
+ const resolvedConfigDirectory = path.resolve(configDirectory);
32
+ // Validate subdirectory suffix for agents that require it
33
+ if (subdirectory !== undefined) {
34
+ const actualSuffix = path.basename(resolvedConfigDirectory);
35
+ if (actualSuffix !== subdirectory) {
36
+ return {
37
+ ok: false,
38
+ error: `Config directory for ${agent} must end with '${subdirectory}', got '${actualSuffix}'.\n` +
39
+ `Expected path like: /some/path/${subdirectory}`,
40
+ };
41
+ }
42
+ }
43
+ return {
44
+ ok: true,
45
+ env: buildAgentRuntimeEnvironment(agent, resolvedConfigDirectory),
46
+ };
47
+ }
48
+ export { getAgentConfigEnvironment };
package/dist/index.d.ts CHANGED
@@ -21,13 +21,14 @@ export { formatRule, parsePermissions, parseRule, parseRuleList, } from "./parse
21
21
  export { getConfigBuilder, registerConfigBuilder } from "./builder.js";
22
22
  export { getConfigReader, registerConfigReader } from "./reader.js";
23
23
  export { resolveConfigPath } from "./resolve-config-path.js";
24
+ export { getAgentConfigEnvironment, type GetAgentConfigEnvironmentResult, } from "./get-agent-runtime-environment.js";
24
25
  export { buildAgentConfig, formatPermissionErrors, formatPermissionWarnings, } from "./build-agent-config.js";
25
- import "./agents/claude-code.js";
26
+ import "./agents/claude.js";
26
27
  import "./agents/codex.js";
27
28
  import "./agents/copilot.js";
28
29
  import "./agents/gemini.js";
29
30
  import "./agents/opencode.js";
30
- export { claudeCodeConfigBuilder, claudeCodeConfigReader, } from "./agents/claude-code.js";
31
+ export { claudeCodeConfigBuilder, claudeCodeConfigReader, } from "./agents/claude.js";
31
32
  export { codexConfigBuilder, codexConfigReader } from "./agents/codex.js";
32
33
  export { copilotConfigBuilder, copilotConfigReader } from "./agents/copilot.js";
33
34
  export { geminiConfigBuilder, geminiConfigReader } from "./agents/gemini.js";
package/dist/index.js CHANGED
@@ -24,16 +24,18 @@ export { getConfigBuilder, registerConfigBuilder } from "./builder.js";
24
24
  export { getConfigReader, registerConfigReader } from "./reader.js";
25
25
  // Path resolution
26
26
  export { resolveConfigPath } from "./resolve-config-path.js";
27
+ // Runtime environment
28
+ export { getAgentConfigEnvironment, } from "./get-agent-runtime-environment.js";
27
29
  // High-level API
28
30
  export { buildAgentConfig, formatPermissionErrors, formatPermissionWarnings, } from "./build-agent-config.js";
29
31
  // Import agent modules to trigger self-registration
30
- import "./agents/claude-code.js";
32
+ import "./agents/claude.js";
31
33
  import "./agents/codex.js";
32
34
  import "./agents/copilot.js";
33
35
  import "./agents/gemini.js";
34
36
  import "./agents/opencode.js";
35
37
  // Export agent builders for direct access
36
- export { claudeCodeConfigBuilder, claudeCodeConfigReader, } from "./agents/claude-code.js";
38
+ export { claudeCodeConfigBuilder, claudeCodeConfigReader, } from "./agents/claude.js";
37
39
  export { codexConfigBuilder, codexConfigReader } from "./agents/codex.js";
38
40
  export { copilotConfigBuilder, copilotConfigReader } from "./agents/copilot.js";
39
41
  export { geminiConfigBuilder, geminiConfigReader } from "./agents/gemini.js";
@@ -10,10 +10,12 @@ import type { AgentCli } from "axshared";
10
10
  * 2. Agent's environment variable
11
11
  * 3. Agent's default config directory
12
12
  *
13
+ * Delegates to axshared's resolveAgentConfigDirectory, which is the canonical
14
+ * source of truth for agent path resolution.
15
+ *
13
16
  * @param agentId - The agent to resolve config for
14
17
  * @param providedPath - Optional path from --path option
15
18
  * @returns The resolved config directory path
16
- * @throws Error if no reader is registered for the agent
17
19
  */
18
20
  declare function resolveConfigPath(agentId: AgentCli, providedPath?: string): string;
19
21
  export { resolveConfigPath };
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Resolve agent config directory from --path option, env vars, or defaults.
3
3
  */
4
- import { getConfigReader } from "./reader.js";
4
+ import { resolveAgentConfigDirectory } from "axshared";
5
5
  /**
6
6
  * Resolve the config directory for an agent.
7
7
  *
@@ -10,23 +10,14 @@ import { getConfigReader } from "./reader.js";
10
10
  * 2. Agent's environment variable
11
11
  * 3. Agent's default config directory
12
12
  *
13
+ * Delegates to axshared's resolveAgentConfigDirectory, which is the canonical
14
+ * source of truth for agent path resolution.
15
+ *
13
16
  * @param agentId - The agent to resolve config for
14
17
  * @param providedPath - Optional path from --path option
15
18
  * @returns The resolved config directory path
16
- * @throws Error if no reader is registered for the agent
17
19
  */
18
20
  function resolveConfigPath(agentId, providedPath) {
19
- if (providedPath) {
20
- return providedPath;
21
- }
22
- const reader = getConfigReader(agentId);
23
- if (!reader) {
24
- throw new Error(`No config reader registered for agent "${agentId}"`);
25
- }
26
- const environmentValue = process.env[reader.envVar];
27
- if (environmentValue) {
28
- return environmentValue;
29
- }
30
- return reader.defaultConfigDir();
21
+ return resolveAgentConfigDirectory(agentId, providedPath);
31
22
  }
32
23
  export { resolveConfigPath };
package/dist/types.d.ts CHANGED
@@ -133,6 +133,18 @@ interface ConfigReader {
133
133
  defaultConfigDir: () => string;
134
134
  /** Environment variable that overrides the config directory */
135
135
  envVar: string;
136
+ /**
137
+ * Subdirectory name within the base path for agents where the env var
138
+ * points to a parent directory (e.g., ".gemini" for HOME, "opencode" for XDG_DATA_HOME).
139
+ * Undefined if env var points directly to the config directory.
140
+ */
141
+ subdirectory: string | undefined;
142
+ /**
143
+ * Build environment variables for running this agent with a custom config directory.
144
+ * @param basePath - The base path (parent of subdirectory, or config dir if no subdirectory)
145
+ * @returns Environment variables to set for the agent
146
+ */
147
+ buildRuntimeEnvironment: (basePath: string) => Record<string, string>;
136
148
  /** Read permissions from agent config and translate to unified format */
137
149
  readPermissions: (configDirectory: string) => ReadPermissionsResult;
138
150
  /** Read model from agent config */
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "axconfig",
3
3
  "author": "Łukasz Jerciński",
4
4
  "license": "MIT",
5
- "version": "2.0.0",
5
+ "version": "3.0.0",
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",
@@ -68,7 +68,7 @@
68
68
  "dependencies": {
69
69
  "@commander-js/extra-typings": "^14.0.0",
70
70
  "@iarna/toml": "^2.2.5",
71
- "axshared": "^1.1.0",
71
+ "axshared": "^1.2.0",
72
72
  "commander": "^14.0.2"
73
73
  },
74
74
  "devDependencies": {