Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ LLM providers like Anthropic and OpenAI cache prompts based on exact prefix matc

## Configuration

DCP uses its own config file (`~/.config/opencode/dcp.jsonc` or `.opencode/dcp.jsonc`), created automatically on first run.
DCP uses its own config file:

- Global: `~/.config/opencode/dcp.jsonc` (or `dcp.json`), created automatically on first run
- Custom config directory: `$OPENCODE_CONFIG_DIR/dcp.jsonc` (or `dcp.json`), if `OPENCODE_CONFIG_DIR` is set
- Project: `.opencode/dcp.jsonc` (or `dcp.json`) in your project’s `.opencode` directory

<details>
<summary><strong>Default Configuration</strong> (click to expand)</summary>
Expand Down Expand Up @@ -104,7 +108,9 @@ The `protectedTools` arrays in each strategy add to this default list.

### Config Precedence

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.
Settings are merged in order:
Defaults → Global (`~/.config/opencode/dcp.jsonc`) → Config Dir (`$OPENCODE_CONFIG_DIR/dcp.jsonc`) → Project (`.opencode/dcp.jsonc`).
Each level overrides the previous, so project settings take priority over config-dir and global, which take priority over defaults.

Restart OpenCode after making config changes.

Expand Down
52 changes: 49 additions & 3 deletions lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,14 +259,30 @@ function findOpencodeDir(startDir: string): string | null {
return null
}

function getConfigPaths(ctx?: PluginInput): { global: string | null, project: string | null } {
function getConfigPaths(ctx?: PluginInput): { global: string | null, configDir: string | null, project: string | null} {

// Global: ~/.config/opencode/dcp.jsonc|json
let globalPath: string | null = null
if (existsSync(GLOBAL_CONFIG_PATH_JSONC)) {
globalPath = GLOBAL_CONFIG_PATH_JSONC
} else if (existsSync(GLOBAL_CONFIG_PATH_JSON)) {
globalPath = GLOBAL_CONFIG_PATH_JSON
}


// Custom config directory: $OPENCODE_CONFIG_DIR/dcp.jsonc|json
let configDirPath: string | null = null
const opencodeConfigDir = process.env.OPENCODE_CONFIG_DIR
if (opencodeConfigDir) {
const configJsonc = join(opencodeConfigDir, 'dcp.jsonc')
const configJson = join(opencodeConfigDir, 'dcp.json')
if (existsSync(configJsonc)) {
configDirPath = configJsonc
} else if (existsSync(configJson)) {
configDirPath = configJson
}
}

// Project: <project>/.opencode/dcp.jsonc|json
let projectPath: string | null = null
if (ctx?.directory) {
const opencodeDir = findOpencodeDir(ctx.directory)
Expand All @@ -281,7 +297,7 @@ function getConfigPaths(ctx?: PluginInput): { global: string | null, project: st
}
}

return { global: globalPath, project: projectPath }
return { global: globalPath, configDir: configDirPath, project: projectPath }
}

function createDefaultConfig(): void {
Expand Down Expand Up @@ -425,6 +441,7 @@ function deepCloneConfig(config: PluginConfig): PluginConfig {
}
}


export function getConfig(ctx: PluginInput): PluginConfig {
let config = deepCloneConfig(defaultConfig)
const configPaths = getConfigPaths(ctx)
Expand Down Expand Up @@ -461,6 +478,35 @@ export function getConfig(ctx: PluginInput): PluginConfig {
createDefaultConfig()
}

// Load and merge $OPENCODE_CONFIG_DIR/dcp.jsonc|json (overrides global)
if (configPaths.configDir) {
const result = loadConfigFile(configPaths.configDir)
if (result.parseError) {
setTimeout(async () => {
try {
ctx.client.tui.showToast({
body: {
title: "DCP: Invalid configDir config",
message: `${configPaths.configDir}\n${result.parseError}\nUsing global/default values`,
variant: "warning",
duration: 7000
}
})
} catch {}
}, 7000)
} else if (result.data) {
// Validate config keys and types
showConfigValidationWarnings(ctx, configPaths.configDir, result.data, true)
config = {
enabled: result.data.enabled ?? config.enabled,
debug: result.data.debug ?? config.debug,
showUpdateToasts: result.data.showUpdateToasts ?? config.showUpdateToasts,
pruningSummary: result.data.pruningSummary ?? config.pruningSummary,
strategies: mergeStrategies(config.strategies, result.data.strategies as any)
}
}
}

// Load and merge project config (overrides global)
if (configPaths.project) {
const result = loadConfigFile(configPaths.project)
Expand Down