axconfig 1.0.0 → 2.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 +110 -119
- package/dist/agents/claude-code-reader.d.ts +9 -0
- package/dist/agents/claude-code-reader.js +160 -0
- package/dist/agents/claude-code.d.ts +1 -0
- package/dist/agents/claude-code.js +30 -4
- package/dist/agents/codex-reader.d.ts +9 -0
- package/dist/agents/codex-reader.js +162 -0
- package/dist/agents/codex.d.ts +1 -0
- package/dist/agents/codex.js +26 -8
- package/dist/agents/copilot-reader.d.ts +13 -0
- package/dist/agents/copilot-reader.js +120 -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 +158 -0
- package/dist/agents/gemini.d.ts +1 -0
- package/dist/agents/gemini.js +28 -5
- package/dist/agents/opencode-reader.d.ts +9 -0
- package/dist/agents/opencode-reader.js +135 -0
- package/dist/agents/opencode.d.ts +1 -0
- package/dist/agents/opencode.js +20 -4
- 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 +1 -0
- package/dist/cli.js +58 -36
- 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/index.d.ts +10 -27
- package/dist/index.js +11 -24
- 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 +19 -0
- package/dist/resolve-config-path.js +32 -0
- package/dist/types.d.ts +70 -6
- package/dist/types.js +1 -3
- package/package.json +5 -3
- package/dist/auth/agents/claude-code.d.ts +0 -9
- package/dist/auth/agents/claude-code.js +0 -106
- package/dist/auth/agents/codex.d.ts +0 -9
- package/dist/auth/agents/codex.js +0 -75
- package/dist/auth/agents/gemini.d.ts +0 -9
- package/dist/auth/agents/gemini.js +0 -97
- package/dist/auth/agents/opencode.d.ts +0 -9
- package/dist/auth/agents/opencode.js +0 -62
- 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/get-access-token.d.ts +0 -35
- package/dist/auth/get-access-token.js +0 -116
- package/dist/auth/types.d.ts +0 -19
- package/dist/auth/types.js +0 -4
- package/dist/commands/auth.d.ts +0 -19
- package/dist/commands/auth.js +0 -87
- 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
|
|
|
@@ -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(
|
|
@@ -101,128 +92,134 @@ if (result.ok) {
|
|
|
101
92
|
// result.env = { CLAUDE_CONFIG_DIR: "/tmp/my-config" }
|
|
102
93
|
// result.warnings = [...]
|
|
103
94
|
}
|
|
95
|
+
|
|
96
|
+
// Read existing config
|
|
97
|
+
const reader = getConfigReader("claude-code");
|
|
98
|
+
const configDir = resolveConfigPath("claude-code"); // ~/.claude/
|
|
99
|
+
|
|
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" });
|
|
104
108
|
```
|
|
105
109
|
|
|
106
110
|
## CLI Commands
|
|
107
111
|
|
|
108
|
-
|
|
109
|
-
# List agents and their auth status
|
|
110
|
-
axconfig auth list
|
|
111
|
-
axconfig auth list --json
|
|
112
|
-
|
|
113
|
-
# Export credentials to encrypted file
|
|
114
|
-
axconfig auth export --agent claude-code --output creds.json
|
|
115
|
-
axconfig auth export --agent claude-code --output creds.json --no-password
|
|
112
|
+
### Create Config
|
|
116
113
|
|
|
114
|
+
```bash
|
|
117
115
|
# Create config with permissions (outputs env vars)
|
|
118
116
|
axconfig create --agent claude-code --output /tmp/config \
|
|
119
117
|
--allow "read,glob,bash:git *" \
|
|
120
118
|
--deny "bash:rm *"
|
|
121
119
|
|
|
122
|
-
# Create config with exported credentials
|
|
123
|
-
axconfig create --agent claude-code --output /tmp/config \
|
|
124
|
-
--allow read \
|
|
125
|
-
--with-credentials creds.json
|
|
126
|
-
|
|
127
120
|
# Export for shell usage
|
|
128
121
|
eval $(axconfig create --agent claude-code --output /tmp/config --allow read)
|
|
129
|
-
```
|
|
130
122
|
|
|
131
|
-
|
|
123
|
+
# Export as JSON for parsing
|
|
124
|
+
axconfig create --agent claude-code --output /tmp/cfg --allow read --format json | jq '.env'
|
|
125
|
+
```
|
|
132
126
|
|
|
133
|
-
|
|
127
|
+
### Get/Set Unified Settings
|
|
134
128
|
|
|
135
129
|
```bash
|
|
136
|
-
#
|
|
137
|
-
axconfig
|
|
138
|
-
#
|
|
139
|
-
# claude-code authenticated OAuth (max)
|
|
140
|
-
# codex authenticated ChatGPT OAuth
|
|
141
|
-
# ...
|
|
130
|
+
# Get current settings (uses agent's default config location)
|
|
131
|
+
axconfig get --agent claude-code allow
|
|
132
|
+
# Output: read,glob,bash:git *
|
|
142
133
|
|
|
143
|
-
|
|
144
|
-
|
|
134
|
+
axconfig get --agent claude-code deny
|
|
135
|
+
# Output: bash:rm *
|
|
145
136
|
|
|
146
|
-
|
|
147
|
-
|
|
137
|
+
axconfig get --agent claude-code model
|
|
138
|
+
# Output: sonnet
|
|
148
139
|
|
|
149
|
-
#
|
|
150
|
-
axconfig
|
|
140
|
+
# Get settings as JSON
|
|
141
|
+
axconfig get --agent claude-code allow --format json
|
|
142
|
+
# Output: ["Read", "Glob", {"Bash": {"command": "git", "args": "*"}}]
|
|
151
143
|
|
|
152
|
-
#
|
|
153
|
-
axconfig
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
axconfig auth list --json | jq -e '.[] | select(.agentId == "claude-code") | .authenticated'
|
|
157
|
-
```
|
|
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"
|
|
158
148
|
|
|
159
|
-
|
|
149
|
+
# Replace instead of merge (for allow/deny)
|
|
150
|
+
axconfig set --agent claude-code allow "read,glob" --replace
|
|
160
151
|
|
|
152
|
+
# Set with custom config path
|
|
153
|
+
axconfig set --agent claude-code --path /tmp/cfg allow "read,glob"
|
|
161
154
|
```
|
|
162
|
-
src/
|
|
163
|
-
├── types.ts # PermissionRule, AxrunConfig, BuildResult, etc.
|
|
164
|
-
├── parse-permissions.ts # parseRule, parseRuleList, parsePermissions
|
|
165
|
-
├── builder.ts # ConfigBuilder registry
|
|
166
|
-
├── build-agent-config.ts # High-level buildAgentConfig function
|
|
167
|
-
├── check-auth.ts # Auth status detection for agents
|
|
168
|
-
├── extract-credentials.ts # Credential extraction for export
|
|
169
|
-
├── crypto.ts # AES-256-GCM encryption for credentials
|
|
170
|
-
├── cli.ts # CLI entry point
|
|
171
|
-
└── agents/
|
|
172
|
-
├── claude-code.ts # Claude Code config builder
|
|
173
|
-
├── codex.ts # Codex config builder
|
|
174
|
-
├── gemini.ts # Gemini CLI config builder
|
|
175
|
-
└── opencode.ts # OpenCode config builder
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
## Integration with axrun
|
|
179
155
|
|
|
180
|
-
|
|
156
|
+
### Get/Set Raw Config Values
|
|
181
157
|
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const configResult = buildAgentConfig({
|
|
187
|
-
agentId: options.agent,
|
|
188
|
-
allow: options.allow,
|
|
189
|
-
deny: options.deny,
|
|
190
|
-
output: "/tmp/axrun-config",
|
|
191
|
-
});
|
|
158
|
+
```bash
|
|
159
|
+
# Get raw config value by dotted path
|
|
160
|
+
axconfig get-raw --agent claude-code permissions.allow
|
|
161
|
+
# Output: ["Read", "Glob"]
|
|
192
162
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
163
|
+
# Get raw value as JSON
|
|
164
|
+
axconfig get-raw --agent claude-code permissions --format json
|
|
196
165
|
|
|
197
|
-
|
|
198
|
-
|
|
166
|
+
# Set raw config value (JSON or string)
|
|
167
|
+
axconfig set-raw --agent claude-code permissions.allow '["Read", "Glob"]'
|
|
168
|
+
axconfig set-raw --agent opencode permission.edit allow
|
|
199
169
|
```
|
|
200
170
|
|
|
201
|
-
|
|
171
|
+
### Pipeline Examples
|
|
202
172
|
|
|
203
|
-
|
|
173
|
+
```bash
|
|
174
|
+
# Capture settings in shell variables
|
|
175
|
+
ALLOW=$(axconfig get --agent claude-code allow)
|
|
176
|
+
MODEL=$(axconfig get --agent claude-code model)
|
|
204
177
|
|
|
205
|
-
|
|
178
|
+
# Extract allow rules as JSON and filter with jq
|
|
179
|
+
axconfig get --agent claude-code allow --format json | jq '.'
|
|
206
180
|
|
|
207
|
-
|
|
181
|
+
# List all bash permissions, one per line
|
|
182
|
+
axconfig get --agent claude-code allow | tr ',' '\n' | grep 'bash:'
|
|
208
183
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
184
|
+
# Count unique bash command prefixes
|
|
185
|
+
axconfig get --agent claude-code allow \
|
|
186
|
+
| tr ',' '\n' | grep 'bash:' \
|
|
187
|
+
| cut -d: -f2 | cut -d' ' -f1 | sort | uniq -c | sort -rn
|
|
188
|
+
|
|
189
|
+
# Compare permissions between agents
|
|
190
|
+
diff <(axconfig get -a claude-code allow) <(axconfig get -a gemini allow)
|
|
213
191
|
|
|
214
|
-
|
|
192
|
+
# Check if a specific permission exists
|
|
193
|
+
axconfig get --agent claude-code allow | grep -q 'bash:git' && echo "git allowed"
|
|
215
194
|
|
|
216
|
-
|
|
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
|
|
198
|
+
```
|
|
217
199
|
|
|
218
|
-
|
|
219
|
-
| ----------- | ---------------------------------------------------- |
|
|
220
|
-
| claude-code | `--settings <file>` flag merges with existing config |
|
|
221
|
-
| codex | `CODEX_HOME` with copied/symlinked auth files |
|
|
222
|
-
| gemini | `GEMINI_CLI_SYSTEM_SETTINGS_PATH` for settings |
|
|
223
|
-
| opencode | `OPENCODE_CONFIG` points to merged config |
|
|
200
|
+
## Module Structure
|
|
224
201
|
|
|
225
|
-
|
|
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-code.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
|
+
```
|
|
226
223
|
|
|
227
224
|
## Agent Rule
|
|
228
225
|
|
|
@@ -235,15 +232,9 @@ Run `npx -y axconfig --help` to learn available options.
|
|
|
235
232
|
|
|
236
233
|
Use `axconfig` to manage AI agent configurations with unified permission syntax.
|
|
237
234
|
It translates `--allow` and `--deny` rules to agent-specific formats (Claude Code,
|
|
238
|
-
Codex, Gemini CLI, OpenCode)
|
|
235
|
+
Codex, Gemini CLI, OpenCode).
|
|
239
236
|
```
|
|
240
237
|
|
|
241
|
-
## Development Status
|
|
242
|
-
|
|
243
|
-
🚧 **Work in Progress**
|
|
244
|
-
|
|
245
|
-
Migrating config logic from axrun to axconfig.
|
|
246
|
-
|
|
247
238
|
## License
|
|
248
239
|
|
|
249
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,160 @@
|
|
|
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 { homedir } from "node:os";
|
|
8
|
+
import path from "node:path";
|
|
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
|
+
/** Claude Code ConfigReader */
|
|
147
|
+
const claudeCodeConfigReader = {
|
|
148
|
+
agentId: "claude",
|
|
149
|
+
defaultConfigDir: () => path.join(homedir(), ".claude"),
|
|
150
|
+
envVar: "CLAUDE_CONFIG_DIR",
|
|
151
|
+
readPermissions,
|
|
152
|
+
readModel,
|
|
153
|
+
writeModel,
|
|
154
|
+
readRaw,
|
|
155
|
+
writeRaw,
|
|
156
|
+
deleteRaw,
|
|
157
|
+
};
|
|
158
|
+
// Self-register on import
|
|
159
|
+
registerConfigReader(claudeCodeConfigReader);
|
|
160
|
+
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-code-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-code-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 };
|