Skip to content

Commit 1cd7cfe

Browse files
committed
feat: support DCP config in OPENCODE_CONFIG_DIR
1 parent bc0c5e5 commit 1cd7cfe

File tree

2 files changed

+57
-6
lines changed

2 files changed

+57
-6
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ LLM providers like Anthropic and OpenAI cache prompts based on exact prefix matc
4747

4848
## Configuration
4949

50-
DCP uses its own config file (`~/.config/opencode/dcp.jsonc` or `.opencode/dcp.jsonc`), created automatically on first run.
50+
DCP uses its own config file:
51+
52+
- Global: `~/.config/opencode/dcp.jsonc` (or `dcp.json`), created automatically on first run
53+
- Custom config directory: `$OPENCODE_CONFIG_DIR/dcp.jsonc` (or `dcp.json`), if `OPENCODE_CONFIG_DIR` is set
54+
- Project: `.opencode/dcp.jsonc` (or `dcp.json`) in your project’s `.opencode` directory
5155

5256
### Options
5357

@@ -80,7 +84,9 @@ DCP uses its own config file (`~/.config/opencode/dcp.jsonc` or `.opencode/dcp.j
8084

8185
### Config Precedence
8286

83-
Settings are merged in order: **Defaults****Global** (`~/.config/opencode/dcp.jsonc`) → **Project** (`.opencode/dcp.jsonc`). Each level overrides the previous, so project settings take priority over global, which takes priority over defaults.
87+
Settings are merged in order:
88+
Defaults → Global (`~/.config/opencode/dcp.jsonc`) → Config Dir (`$OPENCODE_CONFIG_DIR/dcp.jsonc`) → Project (`.opencode/dcp.jsonc`).
89+
Each level overrides the previous, so project settings take priority over config-dir and global, which take priority over defaults.
8490

8591
Restart OpenCode after making config changes.
8692

lib/config.ts

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,30 @@ function findOpencodeDir(startDir: string): string | null {
7474
return null
7575
}
7676

77-
function getConfigPaths(ctx?: PluginInput): { global: string | null, project: string | null } {
77+
function getConfigPaths(ctx?: PluginInput): { global: string | null, configDir: string | null, project: string | null} {
78+
79+
// Global: ~/.config/opencode/dcp.jsonc|json
7880
let globalPath: string | null = null
7981
if (existsSync(GLOBAL_CONFIG_PATH_JSONC)) {
8082
globalPath = GLOBAL_CONFIG_PATH_JSONC
8183
} else if (existsSync(GLOBAL_CONFIG_PATH_JSON)) {
8284
globalPath = GLOBAL_CONFIG_PATH_JSON
8385
}
84-
86+
87+
// Custom config directory: $OPENCODE_CONFIG_DIR/dcp.jsonc|json
88+
let configDirPath: string | null = null
89+
const opencodeConfigDir = process.env.OPENCODE_CONFIG_DIR
90+
if (opencodeConfigDir) {
91+
const configJsonc = join(opencodeConfigDir, 'dcp.jsonc')
92+
const configJson = join(opencodeConfigDir, 'dcp.json')
93+
if (existsSync(configJsonc)) {
94+
configDirPath = configJsonc
95+
} else if (existsSync(configJson)) {
96+
configDirPath = configJson
97+
}
98+
}
99+
100+
// Project: <project>/.opencode/dcp.jsonc|json
85101
let projectPath: string | null = null
86102
if (ctx?.directory) {
87103
const opencodeDir = findOpencodeDir(ctx.directory)
@@ -96,7 +112,7 @@ function getConfigPaths(ctx?: PluginInput): { global: string | null, project: st
96112
}
97113
}
98114

99-
return { global: globalPath, project: projectPath }
115+
return { global: globalPath, configDir: configDirPath, project: projectPath }
100116
}
101117

102118
function createDefaultConfig(): void {
@@ -218,6 +234,35 @@ export function getConfig(ctx?: PluginInput): ConfigResult {
218234
logger.info('config', 'Created default global config', { path: GLOBAL_CONFIG_PATH_JSONC })
219235
}
220236

237+
// Config dir: $OPENCODE_CONFIG_DIR/dcp.jsonc|json (overrides global)
238+
if (configPaths.configDir) {
239+
const configDirConfig = loadConfigFile(configPaths.configDir)
240+
if (configDirConfig) {
241+
const invalidKeys = getInvalidKeys(configDirConfig)
242+
if (invalidKeys.length > 0) {
243+
logger.warn('config', 'Config-dir config has invalid keys (ignored)', {
244+
path: configPaths.configDir,
245+
keys: invalidKeys
246+
})
247+
migrations.push(`Config-dir config has invalid keys: ${invalidKeys.join(', ')}`)
248+
} else {
249+
config = {
250+
enabled: configDirConfig.enabled ?? config.enabled,
251+
debug: configDirConfig.debug ?? config.debug,
252+
protectedTools: [...new Set([...config.protectedTools, ...(configDirConfig.protectedTools ?? [])])],
253+
model: configDirConfig.model ?? config.model,
254+
showModelErrorToasts: configDirConfig.showModelErrorToasts ?? config.showModelErrorToasts,
255+
showUpdateToasts: configDirConfig.showUpdateToasts ?? config.showUpdateToasts,
256+
strictModelSelection: configDirConfig.strictModelSelection ?? config.strictModelSelection,
257+
strategies: mergeStrategies(config.strategies, configDirConfig.strategies as any),
258+
pruning_summary: configDirConfig.pruning_summary ?? config.pruning_summary,
259+
nudge_freq: configDirConfig.nudge_freq ?? config.nudge_freq
260+
}
261+
logger.info('config', 'Loaded config-dir config (overrides global)', { path: configPaths.configDir })
262+
}
263+
}
264+
}
265+
221266
if (configPaths.project) {
222267
const projectConfig = loadConfigFile(configPaths.project)
223268
if (projectConfig) {
@@ -242,7 +287,7 @@ export function getConfig(ctx?: PluginInput): ConfigResult {
242287
pruning_summary: projectConfig.pruning_summary ?? config.pruning_summary,
243288
nudge_freq: projectConfig.nudge_freq ?? config.nudge_freq
244289
}
245-
logger.info('config', 'Loaded project config (overrides global)', { path: configPaths.project })
290+
logger.info('config', 'Loaded project config (overrides global + config-dir)', { path: configPaths.project })
246291
}
247292
}
248293
} else if (ctx?.directory) {

0 commit comments

Comments
 (0)