axconfig 1.1.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 +124 -141
- package/dist/agents/claude-reader.d.ts +9 -0
- package/dist/agents/claude-reader.js +164 -0
- package/dist/agents/{claude-code.d.ts → claude.d.ts} +1 -0
- package/dist/agents/{claude-code.js → claude.js} +30 -4
- package/dist/agents/codex-reader.d.ts +9 -0
- package/dist/agents/codex-reader.js +166 -0
- package/dist/agents/codex.d.ts +1 -0
- package/dist/agents/codex.js +28 -10
- package/dist/agents/copilot-reader.d.ts +13 -0
- package/dist/agents/copilot-reader.js +103 -0
- package/dist/agents/copilot.d.ts +16 -0
- package/dist/agents/copilot.js +85 -0
- package/dist/agents/gemini-reader.d.ts +9 -0
- package/dist/agents/gemini-reader.js +162 -0
- package/dist/agents/gemini.d.ts +1 -0
- package/dist/agents/gemini.js +29 -6
- package/dist/agents/opencode-reader.d.ts +9 -0
- package/dist/agents/opencode-reader.js +139 -0
- package/dist/agents/opencode.d.ts +1 -0
- package/dist/agents/opencode.js +30 -6
- package/dist/atomic-write.d.ts +19 -0
- package/dist/atomic-write.js +46 -0
- package/dist/build-agent-config.d.ts +3 -2
- package/dist/builder.d.ts +3 -2
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +58 -55
- package/dist/commands/create.d.ts +1 -2
- package/dist/commands/create.js +6 -60
- package/dist/commands/get-raw.d.ts +11 -0
- package/dist/commands/get-raw.js +66 -0
- package/dist/commands/get.d.ts +11 -0
- package/dist/commands/get.js +136 -0
- package/dist/commands/set-permissions.d.ts +18 -0
- package/dist/commands/set-permissions.js +99 -0
- package/dist/commands/set-raw.d.ts +10 -0
- package/dist/commands/set-raw.js +58 -0
- package/dist/commands/set.d.ts +11 -0
- package/dist/commands/set.js +72 -0
- package/dist/get-agent-runtime-environment.d.ts +37 -0
- package/dist/get-agent-runtime-environment.js +48 -0
- package/dist/index.d.ts +12 -28
- package/dist/index.js +14 -25
- package/dist/object-path.d.ts +38 -0
- package/dist/object-path.js +104 -0
- package/dist/read-write-json-config.d.ts +26 -0
- package/dist/read-write-json-config.js +99 -0
- package/dist/read-write-toml-config.d.ts +26 -0
- package/dist/read-write-toml-config.js +100 -0
- package/dist/reader.d.ts +20 -0
- package/dist/reader.js +27 -0
- package/dist/resolve-config-path.d.ts +21 -0
- package/dist/resolve-config-path.js +23 -0
- package/dist/types.d.ts +82 -6
- package/dist/types.js +1 -3
- package/package.json +5 -3
- package/dist/auth/agents/claude-code.d.ts +0 -15
- package/dist/auth/agents/claude-code.js +0 -160
- package/dist/auth/agents/codex-config.d.ts +0 -12
- package/dist/auth/agents/codex-config.js +0 -79
- package/dist/auth/agents/codex-storage.d.ts +0 -25
- package/dist/auth/agents/codex-storage.js +0 -43
- package/dist/auth/agents/codex.d.ts +0 -15
- package/dist/auth/agents/codex.js +0 -139
- package/dist/auth/agents/gemini.d.ts +0 -15
- package/dist/auth/agents/gemini.js +0 -149
- package/dist/auth/agents/opencode.d.ts +0 -15
- package/dist/auth/agents/opencode.js +0 -102
- package/dist/auth/check-auth.d.ts +0 -13
- package/dist/auth/check-auth.js +0 -24
- package/dist/auth/extract-credentials.d.ts +0 -11
- package/dist/auth/extract-credentials.js +0 -20
- package/dist/auth/file-storage.d.ts +0 -14
- package/dist/auth/file-storage.js +0 -63
- package/dist/auth/get-access-token.d.ts +0 -35
- package/dist/auth/get-access-token.js +0 -124
- package/dist/auth/install-credentials.d.ts +0 -21
- package/dist/auth/install-credentials.js +0 -21
- package/dist/auth/keychain.d.ts +0 -12
- package/dist/auth/keychain.js +0 -56
- package/dist/auth/remove-credentials.d.ts +0 -15
- package/dist/auth/remove-credentials.js +0 -20
- package/dist/auth/types.d.ts +0 -19
- package/dist/auth/types.js +0 -4
- package/dist/commands/auth.d.ts +0 -32
- package/dist/commands/auth.js +0 -159
- package/dist/crypto.d.ts +0 -39
- package/dist/crypto.js +0 -78
package/README.md
CHANGED
|
@@ -1,38 +1,24 @@
|
|
|
1
1
|
# axconfig
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Unified configuration management for AI coding agents — common API for permissions, settings, and config across Claude Code, Codex, Gemini CLI, and OpenCode.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Different AI coding agents each have their own config formats, permission syntaxes, and settings locations. If you want to control what an agent can do or configure its behavior, you'd need to learn each agent's specific configuration format and file locations.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
2. **As a CLI** - standalone tool for managing, inspecting, and creating agent configs
|
|
9
|
+
axconfig solves this by providing:
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
1. **Unified permission syntax** — One way to express permissions (`--allow read,bash:git *`) that works across all agents
|
|
12
|
+
2. **Common config access** — Get and set configuration properties using a consistent API, regardless of agent
|
|
13
|
+
3. **Translation to agent-specific formats** — Converts unified config into whatever format each agent expects
|
|
14
|
+
4. **Capability validation** — Knows what each agent supports and warns/errors appropriately
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
│ │
|
|
19
|
-
│ axrun -a claude-code --allow "read,bash:git *" "prompt" │
|
|
20
|
-
│ │ │
|
|
21
|
-
│ ▼ │
|
|
22
|
-
│ ┌────────────────────────┐ │
|
|
23
|
-
│ │ axconfig │ │
|
|
24
|
-
│ │ (Config translation) │ │
|
|
25
|
-
│ └────────────────────────┘ │
|
|
26
|
-
│ │ │
|
|
27
|
-
│ ▼ │
|
|
28
|
-
│ { env, settings } │
|
|
29
|
-
│ │ │
|
|
30
|
-
│ ▼ │
|
|
31
|
-
│ spawn(claude, { env }) │
|
|
32
|
-
└─────────────────────────────────────────────────────────────┘
|
|
33
|
-
```
|
|
16
|
+
It can be used:
|
|
17
|
+
|
|
18
|
+
- **As a library** — integrate into your own tools and scripts
|
|
19
|
+
- **As a CLI** — standalone tool for managing agent configs
|
|
34
20
|
|
|
35
|
-
## Core
|
|
21
|
+
## Core Functionality
|
|
36
22
|
|
|
37
23
|
### Permission Parsing
|
|
38
24
|
|
|
@@ -55,23 +41,23 @@ write:.env
|
|
|
55
41
|
|
|
56
42
|
Translates unified permissions to agent-specific formats:
|
|
57
43
|
|
|
58
|
-
| Agent
|
|
59
|
-
|
|
|
60
|
-
| claude
|
|
61
|
-
| codex
|
|
62
|
-
| gemini
|
|
63
|
-
| opencode
|
|
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}` |
|
|
64
50
|
|
|
65
51
|
### Capability Validation
|
|
66
52
|
|
|
67
53
|
Each agent has different capabilities:
|
|
68
54
|
|
|
69
|
-
| Agent
|
|
70
|
-
|
|
|
71
|
-
| claude
|
|
72
|
-
| codex
|
|
73
|
-
| gemini
|
|
74
|
-
| opencode
|
|
55
|
+
| Agent | Tool Perms | Bash Patterns | Path Restrictions | Can Deny Read |
|
|
56
|
+
| -------- | ----------- | ------------- | ----------------- | ------------- |
|
|
57
|
+
| claude | ✓ | ✓ | ✓ | ✓ |
|
|
58
|
+
| codex | ✗ (sandbox) | ✓ | ✗ | ✗ |
|
|
59
|
+
| gemini | ✓ | ✓ | ✗ | ✓ |
|
|
60
|
+
| opencode | ✓ | ✓ | ✗ | ✓ |
|
|
75
61
|
|
|
76
62
|
axconfig validates permissions against agent capabilities:
|
|
77
63
|
|
|
@@ -81,7 +67,12 @@ axconfig validates permissions against agent capabilities:
|
|
|
81
67
|
## Library API
|
|
82
68
|
|
|
83
69
|
```typescript
|
|
84
|
-
import {
|
|
70
|
+
import {
|
|
71
|
+
buildAgentConfig,
|
|
72
|
+
getConfigReader,
|
|
73
|
+
parsePermissions,
|
|
74
|
+
resolveConfigPath,
|
|
75
|
+
} from "axconfig";
|
|
85
76
|
|
|
86
77
|
// Parse CLI-style permission strings
|
|
87
78
|
const permissions = parsePermissions(
|
|
@@ -91,7 +82,7 @@ const permissions = parsePermissions(
|
|
|
91
82
|
|
|
92
83
|
// Build agent-specific config
|
|
93
84
|
const result = buildAgentConfig({
|
|
94
|
-
agentId: "claude
|
|
85
|
+
agentId: "claude",
|
|
95
86
|
allow: "read,glob,bash:git *",
|
|
96
87
|
deny: "bash:rm *",
|
|
97
88
|
output: "/tmp/my-config", // directory for config files
|
|
@@ -101,136 +92,134 @@ if (result.ok) {
|
|
|
101
92
|
// result.env = { CLAUDE_CONFIG_DIR: "/tmp/my-config" }
|
|
102
93
|
// result.warnings = [...]
|
|
103
94
|
}
|
|
104
|
-
```
|
|
105
95
|
|
|
106
|
-
|
|
96
|
+
// Read existing config
|
|
97
|
+
const reader = getConfigReader("claude");
|
|
98
|
+
const configDir = resolveConfigPath("claude"); // ~/.claude/
|
|
107
99
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
100
|
+
const perms = reader.readPermissions(configDir);
|
|
101
|
+
if (perms.ok && perms.value) {
|
|
102
|
+
console.log(perms.value.allow); // PermissionRule[]
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Read/write raw config values
|
|
106
|
+
const raw = reader.readRaw(configDir, "permissions.allow");
|
|
107
|
+
reader.writeRaw(configDir, "customSetting", { foo: "bar" });
|
|
108
|
+
```
|
|
112
109
|
|
|
113
|
-
|
|
114
|
-
axconfig auth token --agent claude-code
|
|
110
|
+
## CLI Commands
|
|
115
111
|
|
|
116
|
-
|
|
117
|
-
axconfig auth export --agent claude-code --output creds.json
|
|
118
|
-
axconfig auth export --agent claude-code --output creds.json --no-password
|
|
112
|
+
### Create Config
|
|
119
113
|
|
|
114
|
+
```bash
|
|
120
115
|
# Create config with permissions (outputs env vars)
|
|
121
|
-
axconfig create --agent claude
|
|
116
|
+
axconfig create --agent claude --output /tmp/config \
|
|
122
117
|
--allow "read,glob,bash:git *" \
|
|
123
118
|
--deny "bash:rm *"
|
|
124
119
|
|
|
125
|
-
# Create config with exported credentials
|
|
126
|
-
axconfig create --agent claude-code --output /tmp/config \
|
|
127
|
-
--allow read \
|
|
128
|
-
--with-credentials creds.json
|
|
129
|
-
|
|
130
120
|
# Export for shell usage
|
|
131
|
-
eval $(axconfig create --agent claude
|
|
132
|
-
```
|
|
121
|
+
eval $(axconfig create --agent claude --output /tmp/config --allow read)
|
|
133
122
|
|
|
134
|
-
|
|
123
|
+
# Export as JSON for parsing
|
|
124
|
+
axconfig create --agent claude --output /tmp/cfg --allow read --format json | jq '.env'
|
|
125
|
+
```
|
|
135
126
|
|
|
136
|
-
|
|
127
|
+
### Get/Set Unified Settings
|
|
137
128
|
|
|
138
129
|
```bash
|
|
139
|
-
#
|
|
140
|
-
axconfig
|
|
141
|
-
#
|
|
142
|
-
# claude-code authenticated OAuth (max)
|
|
143
|
-
# codex authenticated ChatGPT OAuth
|
|
144
|
-
# ...
|
|
130
|
+
# Get current settings (uses agent's default config location)
|
|
131
|
+
axconfig get --agent claude allow
|
|
132
|
+
# Output: read,glob,bash:git *
|
|
145
133
|
|
|
146
|
-
|
|
147
|
-
|
|
134
|
+
axconfig get --agent claude deny
|
|
135
|
+
# Output: bash:rm *
|
|
148
136
|
|
|
149
|
-
|
|
150
|
-
|
|
137
|
+
axconfig get --agent claude model
|
|
138
|
+
# Output: sonnet
|
|
151
139
|
|
|
152
|
-
#
|
|
153
|
-
axconfig
|
|
140
|
+
# Get settings as JSON
|
|
141
|
+
axconfig get --agent claude allow --format json
|
|
142
|
+
# Output: ["Read", "Glob", {"Bash": {"command": "git", "args": "*"}}]
|
|
154
143
|
|
|
155
|
-
#
|
|
156
|
-
axconfig
|
|
144
|
+
# Set settings (merge with existing by default)
|
|
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"
|
|
157
148
|
|
|
158
|
-
#
|
|
159
|
-
axconfig
|
|
149
|
+
# Replace instead of merge (for allow/deny)
|
|
150
|
+
axconfig set --agent claude allow "read,glob" --replace
|
|
160
151
|
|
|
161
|
-
#
|
|
162
|
-
|
|
163
|
-
-H "anthropic-beta: oauth-2025-04-20" \
|
|
164
|
-
https://api.anthropic.com/api/oauth/usage | jq .
|
|
152
|
+
# Set with custom config path
|
|
153
|
+
axconfig set --agent claude --path /tmp/cfg allow "read,glob"
|
|
165
154
|
```
|
|
166
155
|
|
|
167
|
-
|
|
156
|
+
### Get/Set Raw Config Values
|
|
168
157
|
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
├── builder.ts # ConfigBuilder registry
|
|
174
|
-
├── build-agent-config.ts # High-level buildAgentConfig function
|
|
175
|
-
├── check-auth.ts # Auth status detection for agents
|
|
176
|
-
├── extract-credentials.ts # Credential extraction for export
|
|
177
|
-
├── crypto.ts # AES-256-GCM encryption for credentials
|
|
178
|
-
├── cli.ts # CLI entry point
|
|
179
|
-
└── agents/
|
|
180
|
-
├── claude-code.ts # Claude Code config builder
|
|
181
|
-
├── codex.ts # Codex config builder
|
|
182
|
-
├── gemini.ts # Gemini CLI config builder
|
|
183
|
-
└── opencode.ts # OpenCode config builder
|
|
184
|
-
```
|
|
158
|
+
```bash
|
|
159
|
+
# Get raw config value by dotted path
|
|
160
|
+
axconfig get-raw --agent claude permissions.allow
|
|
161
|
+
# Output: ["Read", "Glob"]
|
|
185
162
|
|
|
186
|
-
|
|
163
|
+
# Get raw value as JSON
|
|
164
|
+
axconfig get-raw --agent claude permissions --format json
|
|
187
165
|
|
|
188
|
-
|
|
166
|
+
# Set raw config value (JSON or string)
|
|
167
|
+
axconfig set-raw --agent claude permissions.allow '["Read", "Glob"]'
|
|
168
|
+
axconfig set-raw --agent opencode permission.edit allow
|
|
169
|
+
```
|
|
189
170
|
|
|
190
|
-
|
|
191
|
-
// In axrun/src/cli.ts
|
|
192
|
-
import { buildAgentConfig } from "axconfig";
|
|
193
|
-
|
|
194
|
-
const configResult = buildAgentConfig({
|
|
195
|
-
agentId: options.agent,
|
|
196
|
-
allow: options.allow,
|
|
197
|
-
deny: options.deny,
|
|
198
|
-
output: "/tmp/axrun-config",
|
|
199
|
-
});
|
|
171
|
+
### Pipeline Examples
|
|
200
172
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
173
|
+
```bash
|
|
174
|
+
# Capture settings in shell variables
|
|
175
|
+
ALLOW=$(axconfig get --agent claude allow)
|
|
176
|
+
MODEL=$(axconfig get --agent claude model)
|
|
204
177
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
```
|
|
178
|
+
# Extract allow rules as JSON and filter with jq
|
|
179
|
+
axconfig get --agent claude allow --format json | jq '.'
|
|
208
180
|
|
|
209
|
-
|
|
181
|
+
# List all bash permissions, one per line
|
|
182
|
+
axconfig get --agent claude allow | tr ',' '\n' | grep 'bash:'
|
|
210
183
|
|
|
211
|
-
|
|
184
|
+
# Count unique bash command prefixes
|
|
185
|
+
axconfig get --agent claude allow \
|
|
186
|
+
| tr ',' '\n' | grep 'bash:' \
|
|
187
|
+
| cut -d: -f2 | cut -d' ' -f1 | sort | uniq -c | sort -rn
|
|
212
188
|
|
|
213
|
-
|
|
189
|
+
# Compare permissions between agents
|
|
190
|
+
diff <(axconfig get -a claude allow) <(axconfig get -a gemini allow)
|
|
214
191
|
|
|
215
|
-
|
|
192
|
+
# Check if a specific permission exists
|
|
193
|
+
axconfig get --agent claude allow | grep -q 'bash:git' && echo "git allowed"
|
|
216
194
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
195
|
+
# Add a new permission to existing allow list
|
|
196
|
+
CURRENT=$(axconfig get --agent claude allow)
|
|
197
|
+
axconfig set --agent claude allow "$CURRENT,write" --replace
|
|
220
198
|
```
|
|
221
199
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
Each agent has a preferred method for layering config:
|
|
225
|
-
|
|
226
|
-
| Agent | Method |
|
|
227
|
-
| ----------- | ---------------------------------------------------- |
|
|
228
|
-
| claude-code | `--settings <file>` flag merges with existing config |
|
|
229
|
-
| codex | `CODEX_HOME` with copied/symlinked auth files |
|
|
230
|
-
| gemini | `GEMINI_CLI_SYSTEM_SETTINGS_PATH` for settings |
|
|
231
|
-
| opencode | `OPENCODE_CONFIG` points to merged config |
|
|
200
|
+
## Module Structure
|
|
232
201
|
|
|
233
|
-
|
|
202
|
+
```
|
|
203
|
+
src/
|
|
204
|
+
├── types.ts # PermissionRule, ConfigReader, BuildResult, etc.
|
|
205
|
+
├── parse-permissions.ts # parseRule, parseRuleList, parsePermissions
|
|
206
|
+
├── builder.ts # ConfigBuilder registry
|
|
207
|
+
├── reader.ts # ConfigReader registry
|
|
208
|
+
├── resolve-config-path.ts # Resolve agent config directory
|
|
209
|
+
├── build-agent-config.ts # High-level buildAgentConfig function
|
|
210
|
+
├── cli.ts # CLI entry point
|
|
211
|
+
├── commands/
|
|
212
|
+
│ ├── create.ts # Create command handler
|
|
213
|
+
│ ├── get.ts # Get unified settings
|
|
214
|
+
│ ├── set.ts # Set unified settings
|
|
215
|
+
│ ├── get-raw.ts # Get raw config values
|
|
216
|
+
│ └── set-raw.ts # Set raw config values
|
|
217
|
+
└── agents/
|
|
218
|
+
├── claude.ts # Claude Code config builder + reader
|
|
219
|
+
├── codex.ts # Codex config builder + reader
|
|
220
|
+
├── gemini.ts # Gemini CLI config builder + reader
|
|
221
|
+
└── opencode.ts # OpenCode config builder + reader
|
|
222
|
+
```
|
|
234
223
|
|
|
235
224
|
## Agent Rule
|
|
236
225
|
|
|
@@ -243,15 +232,9 @@ Run `npx -y axconfig --help` to learn available options.
|
|
|
243
232
|
|
|
244
233
|
Use `axconfig` to manage AI agent configurations with unified permission syntax.
|
|
245
234
|
It translates `--allow` and `--deny` rules to agent-specific formats (Claude Code,
|
|
246
|
-
Codex, Gemini CLI, OpenCode)
|
|
235
|
+
Codex, Gemini CLI, OpenCode).
|
|
247
236
|
```
|
|
248
237
|
|
|
249
|
-
## Development Status
|
|
250
|
-
|
|
251
|
-
🚧 **Work in Progress**
|
|
252
|
-
|
|
253
|
-
Migrating config logic from axrun to axconfig.
|
|
254
|
-
|
|
255
238
|
## License
|
|
256
239
|
|
|
257
240
|
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code ConfigReader.
|
|
3
|
+
*
|
|
4
|
+
* Reads and writes Claude Code configuration from settings.json.
|
|
5
|
+
*/
|
|
6
|
+
import type { ConfigReader } from "../types.js";
|
|
7
|
+
/** Claude Code ConfigReader */
|
|
8
|
+
declare const claudeCodeConfigReader: ConfigReader;
|
|
9
|
+
export { claudeCodeConfigReader };
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code ConfigReader.
|
|
3
|
+
*
|
|
4
|
+
* Reads and writes Claude Code configuration from settings.json.
|
|
5
|
+
*/
|
|
6
|
+
import { mkdirSync } from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { getAgentPathInfo } from "axshared";
|
|
9
|
+
import { atomicWriteFileSync } from "../atomic-write.js";
|
|
10
|
+
import { registerConfigReader } from "../reader.js";
|
|
11
|
+
import { createJsonConfigOperations, readJsonConfig, } from "../read-write-json-config.js";
|
|
12
|
+
/** Reverse mapping from Claude Code tool names to canonical names */
|
|
13
|
+
const REVERSE_TOOL_MAP = {
|
|
14
|
+
Read: "read",
|
|
15
|
+
Write: "write",
|
|
16
|
+
Edit: "edit",
|
|
17
|
+
Bash: "bash",
|
|
18
|
+
Glob: "glob",
|
|
19
|
+
Grep: "grep",
|
|
20
|
+
WebFetch: "web",
|
|
21
|
+
WebSearch: "web",
|
|
22
|
+
};
|
|
23
|
+
/** Path-restricted tools (lowercase) */
|
|
24
|
+
const PATH_TOOLS = new Set(["read", "write", "edit"]);
|
|
25
|
+
/**
|
|
26
|
+
* Get the settings.json path for a config directory.
|
|
27
|
+
*/
|
|
28
|
+
function getSettingsPath(configDirectory) {
|
|
29
|
+
return path.join(configDirectory, "settings.json");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Read and parse settings.json, returning empty object if not found.
|
|
33
|
+
*/
|
|
34
|
+
function readSettings(configDirectory) {
|
|
35
|
+
return readJsonConfig(getSettingsPath(configDirectory));
|
|
36
|
+
}
|
|
37
|
+
// Create shared raw config operations
|
|
38
|
+
const { readRaw, writeRaw, deleteRaw } = createJsonConfigOperations(getSettingsPath, "Claude Code");
|
|
39
|
+
/**
|
|
40
|
+
* Parse a Claude Code rule string back to unified format.
|
|
41
|
+
*/
|
|
42
|
+
function parseClaudeRule(rule) {
|
|
43
|
+
// Check for pattern format: "Tool(pattern)"
|
|
44
|
+
const parenIndex = rule.indexOf("(");
|
|
45
|
+
if (parenIndex > 0 && rule.endsWith(")")) {
|
|
46
|
+
const toolName = rule.slice(0, parenIndex);
|
|
47
|
+
const pattern = rule.slice(parenIndex + 1, -1);
|
|
48
|
+
// Bash pattern: "Bash(git:*)" → pattern is "git:*", we want "git"
|
|
49
|
+
if (toolName === "Bash") {
|
|
50
|
+
const bashPattern = pattern.endsWith(":*")
|
|
51
|
+
? pattern.slice(0, -2)
|
|
52
|
+
: pattern;
|
|
53
|
+
return { type: "bash", pattern: bashPattern };
|
|
54
|
+
}
|
|
55
|
+
// Path pattern: "Read(src/**)" → tool is "read", pattern is "src/**"
|
|
56
|
+
const canonicalTool = REVERSE_TOOL_MAP[toolName];
|
|
57
|
+
if (canonicalTool && PATH_TOOLS.has(canonicalTool)) {
|
|
58
|
+
return {
|
|
59
|
+
type: "path",
|
|
60
|
+
tool: canonicalTool,
|
|
61
|
+
pattern,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
// Simple tool name: "Read" → { type: "tool", name: "read" }
|
|
67
|
+
const canonicalTool = REVERSE_TOOL_MAP[rule];
|
|
68
|
+
if (canonicalTool) {
|
|
69
|
+
return { type: "tool", name: canonicalTool };
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Read permissions from Claude Code config.
|
|
75
|
+
*/
|
|
76
|
+
function readPermissions(configDirectory) {
|
|
77
|
+
try {
|
|
78
|
+
const settings = readSettings(configDirectory);
|
|
79
|
+
const permissions = settings.permissions;
|
|
80
|
+
if (!permissions) {
|
|
81
|
+
return { ok: true, value: undefined };
|
|
82
|
+
}
|
|
83
|
+
const allowRules = (permissions.allow ?? [])
|
|
84
|
+
.map((rule) => parseClaudeRule(rule))
|
|
85
|
+
.filter((rule) => rule !== undefined);
|
|
86
|
+
const denyRules = (permissions.deny ?? [])
|
|
87
|
+
.map((rule) => parseClaudeRule(rule))
|
|
88
|
+
.filter((rule) => rule !== undefined);
|
|
89
|
+
const config = {
|
|
90
|
+
allow: allowRules,
|
|
91
|
+
deny: denyRules,
|
|
92
|
+
};
|
|
93
|
+
return { ok: true, value: config };
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
+
return {
|
|
98
|
+
ok: false,
|
|
99
|
+
error: `Failed to read Claude Code config: ${message}`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Read model from Claude Code config.
|
|
105
|
+
* Returns undefined when no model is explicitly configured.
|
|
106
|
+
*/
|
|
107
|
+
function readModel(configDirectory) {
|
|
108
|
+
try {
|
|
109
|
+
const settings = readSettings(configDirectory);
|
|
110
|
+
const model = settings.model;
|
|
111
|
+
if (model === undefined || model === null) {
|
|
112
|
+
return { ok: true, value: undefined };
|
|
113
|
+
}
|
|
114
|
+
if (typeof model !== "string") {
|
|
115
|
+
return { ok: false, error: "Model value is not a string" };
|
|
116
|
+
}
|
|
117
|
+
return { ok: true, value: model };
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
error: `Failed to read Claude Code model: ${message}`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Write model to Claude Code config.
|
|
129
|
+
*/
|
|
130
|
+
function writeModel(configDirectory, model) {
|
|
131
|
+
try {
|
|
132
|
+
mkdirSync(configDirectory, { recursive: true });
|
|
133
|
+
const settings = readSettings(configDirectory);
|
|
134
|
+
settings.model = model;
|
|
135
|
+
atomicWriteFileSync(getSettingsPath(configDirectory), JSON.stringify(settings, undefined, 2));
|
|
136
|
+
return { ok: true };
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
140
|
+
return {
|
|
141
|
+
ok: false,
|
|
142
|
+
error: `Failed to write Claude Code model: ${message}`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Get path info from axshared (single source of truth)
|
|
147
|
+
const pathInfo = getAgentPathInfo("claude");
|
|
148
|
+
/** Claude Code ConfigReader */
|
|
149
|
+
const claudeCodeConfigReader = {
|
|
150
|
+
agentId: "claude",
|
|
151
|
+
defaultConfigDir: pathInfo.defaultConfigDir,
|
|
152
|
+
envVar: pathInfo.envVar,
|
|
153
|
+
subdirectory: pathInfo.subdirectory,
|
|
154
|
+
buildRuntimeEnvironment: pathInfo.buildRuntimeEnvironment,
|
|
155
|
+
readPermissions,
|
|
156
|
+
readModel,
|
|
157
|
+
writeModel,
|
|
158
|
+
readRaw,
|
|
159
|
+
writeRaw,
|
|
160
|
+
deleteRaw,
|
|
161
|
+
};
|
|
162
|
+
// Self-register on import
|
|
163
|
+
registerConfigReader(claudeCodeConfigReader);
|
|
164
|
+
export { claudeCodeConfigReader };
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - Path patterns like "Read(src/**)"
|
|
10
10
|
*/
|
|
11
11
|
import type { ConfigBuilder } from "../types.js";
|
|
12
|
+
export { claudeCodeConfigReader } from "./claude-reader.js";
|
|
12
13
|
/** Claude Code ConfigBuilder */
|
|
13
14
|
declare const claudeCodeConfigBuilder: ConfigBuilder;
|
|
14
15
|
export { claudeCodeConfigBuilder };
|
|
@@ -8,9 +8,12 @@
|
|
|
8
8
|
* - Bash patterns like "Bash(git:*)"
|
|
9
9
|
* - Path patterns like "Read(src/**)"
|
|
10
10
|
*/
|
|
11
|
-
import { mkdirSync,
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
12
12
|
import path from "node:path";
|
|
13
|
+
import { atomicWriteFileSync } from "../atomic-write.js";
|
|
13
14
|
import { registerConfigBuilder } from "../builder.js";
|
|
15
|
+
// Re-export reader
|
|
16
|
+
export { claudeCodeConfigReader } from "./claude-reader.js";
|
|
14
17
|
/** Claude Code tool name mapping */
|
|
15
18
|
const TOOL_MAP = {
|
|
16
19
|
read: "Read",
|
|
@@ -47,23 +50,45 @@ function translateRule(rule) {
|
|
|
47
50
|
}
|
|
48
51
|
}
|
|
49
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Read existing settings.json, returning empty object if not found.
|
|
55
|
+
* Throws if file exists but contains invalid JSON to prevent data loss.
|
|
56
|
+
*/
|
|
57
|
+
function readExistingSettings(settingsPath) {
|
|
58
|
+
if (!existsSync(settingsPath)) {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const content = readFileSync(settingsPath, "utf8");
|
|
63
|
+
return JSON.parse(content);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
67
|
+
throw new Error(`Failed to parse existing settings at ${settingsPath}: ${message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
50
70
|
/**
|
|
51
71
|
* Build Claude Code configuration.
|
|
72
|
+
*
|
|
73
|
+
* Preserves existing settings (model, env, etc.) and only updates permissions.
|
|
52
74
|
*/
|
|
53
75
|
function build(config, output) {
|
|
54
76
|
mkdirSync(output, { recursive: true });
|
|
55
77
|
const warnings = [];
|
|
56
78
|
const permissions = config.permissions;
|
|
57
79
|
const settingsPath = path.join(output, "settings.json");
|
|
80
|
+
// Read existing settings to preserve non-permission fields
|
|
81
|
+
const existingSettings = readExistingSettings(settingsPath);
|
|
58
82
|
// If no permissions specified, deny all
|
|
59
83
|
if (!permissions) {
|
|
60
84
|
const settings = {
|
|
85
|
+
...existingSettings,
|
|
61
86
|
permissions: {
|
|
62
87
|
allow: [],
|
|
63
88
|
deny: [],
|
|
64
89
|
},
|
|
65
90
|
};
|
|
66
|
-
|
|
91
|
+
atomicWriteFileSync(settingsPath, JSON.stringify(settings, undefined, 2));
|
|
67
92
|
return {
|
|
68
93
|
ok: true,
|
|
69
94
|
env: { CLAUDE_CONFIG_DIR: output },
|
|
@@ -75,12 +100,13 @@ function build(config, output) {
|
|
|
75
100
|
const allowRules = permissions.allow.map((rule) => translateRule(rule));
|
|
76
101
|
const denyRules = permissions.deny.map((rule) => translateRule(rule));
|
|
77
102
|
const settings = {
|
|
103
|
+
...existingSettings,
|
|
78
104
|
permissions: {
|
|
79
105
|
allow: allowRules,
|
|
80
106
|
deny: denyRules,
|
|
81
107
|
},
|
|
82
108
|
};
|
|
83
|
-
|
|
109
|
+
atomicWriteFileSync(settingsPath, JSON.stringify(settings, undefined, 2));
|
|
84
110
|
return {
|
|
85
111
|
ok: true,
|
|
86
112
|
env: { CLAUDE_CONFIG_DIR: output },
|
|
@@ -89,7 +115,7 @@ function build(config, output) {
|
|
|
89
115
|
}
|
|
90
116
|
/** Claude Code ConfigBuilder */
|
|
91
117
|
const claudeCodeConfigBuilder = {
|
|
92
|
-
agentId: "claude
|
|
118
|
+
agentId: "claude",
|
|
93
119
|
capabilities: CAPABILITIES,
|
|
94
120
|
build,
|
|
95
121
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex ConfigReader.
|
|
3
|
+
*
|
|
4
|
+
* Reads and writes Codex configuration from config.toml and .rules files.
|
|
5
|
+
*/
|
|
6
|
+
import type { ConfigReader } from "../types.js";
|
|
7
|
+
/** Codex ConfigReader */
|
|
8
|
+
declare const codexConfigReader: ConfigReader;
|
|
9
|
+
export { codexConfigReader };
|