@tarquinen/opencode-dcp 1.1.0-beta.1 → 1.1.0-beta.2
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 +64 -64
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -10
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +17 -23
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +316 -274
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +3 -0
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/logger.d.ts +17 -0
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +90 -7
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/messages/prune.d.ts.map +1 -1
- package/dist/lib/messages/prune.js +37 -28
- package/dist/lib/messages/prune.js.map +1 -1
- package/dist/lib/messages/utils.js +7 -7
- package/dist/lib/model-selector.d.ts +3 -3
- package/dist/lib/model-selector.d.ts.map +1 -1
- package/dist/lib/model-selector.js +34 -34
- package/dist/lib/model-selector.js.map +1 -1
- package/dist/lib/prompt.d.ts.map +1 -1
- package/dist/lib/prompt.js +36 -24
- package/dist/lib/prompt.js.map +1 -1
- package/dist/lib/shared-utils.d.ts.map +1 -1
- package/dist/lib/shared-utils.js +1 -1
- package/dist/lib/shared-utils.js.map +1 -1
- package/dist/lib/state/persistence.d.ts.map +1 -1
- package/dist/lib/state/persistence.js +4 -7
- package/dist/lib/state/persistence.js.map +1 -1
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/state.js +7 -5
- package/dist/lib/state/state.js.map +1 -1
- package/dist/lib/state/tool-cache.d.ts.map +1 -1
- package/dist/lib/state/tool-cache.js +7 -13
- package/dist/lib/state/tool-cache.js.map +1 -1
- package/dist/lib/strategies/deduplication.js +3 -3
- package/dist/lib/strategies/deduplication.js.map +1 -1
- package/dist/lib/strategies/on-idle.d.ts.map +1 -1
- package/dist/lib/strategies/on-idle.js +24 -24
- package/dist/lib/strategies/on-idle.js.map +1 -1
- package/dist/lib/strategies/supersede-writes.js +4 -4
- package/dist/lib/strategies/supersede-writes.js.map +1 -1
- package/dist/lib/strategies/tools.d.ts.map +1 -1
- package/dist/lib/strategies/tools.js +23 -17
- package/dist/lib/strategies/tools.js.map +1 -1
- package/dist/lib/strategies/utils.d.ts.map +1 -1
- package/dist/lib/strategies/utils.js +20 -9
- package/dist/lib/strategies/utils.js.map +1 -1
- package/dist/lib/ui/notification.d.ts.map +1 -1
- package/dist/lib/ui/notification.js +26 -23
- package/dist/lib/ui/notification.js.map +1 -1
- package/dist/lib/ui/utils.d.ts.map +1 -1
- package/dist/lib/ui/utils.js +9 -9
- package/dist/lib/ui/utils.js.map +1 -1
- package/package.json +61 -58
package/dist/lib/config.js
CHANGED
|
@@ -1,59 +1,51 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from
|
|
2
|
-
import { join, dirname } from
|
|
3
|
-
import { homedir } from
|
|
4
|
-
import { parse } from
|
|
5
|
-
const DEFAULT_PROTECTED_TOOLS = [
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { parse } from "jsonc-parser";
|
|
5
|
+
const DEFAULT_PROTECTED_TOOLS = ["task", "todowrite", "todoread", "discard", "extract", "batch"];
|
|
6
6
|
// Valid config keys for validation against user config
|
|
7
7
|
export const VALID_CONFIG_KEYS = new Set([
|
|
8
8
|
// Top-level keys
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
"enabled",
|
|
10
|
+
"debug",
|
|
11
|
+
"showUpdateToasts", // Deprecated but kept for backwards compatibility
|
|
12
|
+
"pruneNotification",
|
|
13
|
+
"turnProtection",
|
|
14
|
+
"turnProtection.enabled",
|
|
15
|
+
"turnProtection.turns",
|
|
16
|
+
"tools",
|
|
17
|
+
"tools.settings",
|
|
18
|
+
"tools.settings.nudgeEnabled",
|
|
19
|
+
"tools.settings.nudgeFrequency",
|
|
20
|
+
"tools.settings.protectedTools",
|
|
21
|
+
"tools.discard",
|
|
22
|
+
"tools.discard.enabled",
|
|
23
|
+
"tools.extract",
|
|
24
|
+
"tools.extract.enabled",
|
|
25
|
+
"tools.extract.showDistillation",
|
|
26
|
+
"strategies",
|
|
14
27
|
// strategies.deduplication
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
28
|
+
"strategies.deduplication",
|
|
29
|
+
"strategies.deduplication.enabled",
|
|
30
|
+
"strategies.deduplication.protectedTools",
|
|
18
31
|
// strategies.supersedeWrites
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
"strategies.supersedeWrites",
|
|
33
|
+
"strategies.supersedeWrites.enabled",
|
|
21
34
|
// strategies.onIdle
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// strategies.discardTool
|
|
29
|
-
'strategies.discardTool',
|
|
30
|
-
'strategies.discardTool.enabled',
|
|
31
|
-
'strategies.discardTool.protectedTools',
|
|
32
|
-
'strategies.discardTool.turnProtection',
|
|
33
|
-
'strategies.discardTool.turnProtection.enabled',
|
|
34
|
-
'strategies.discardTool.turnProtection.turns',
|
|
35
|
-
'strategies.discardTool.nudge',
|
|
36
|
-
'strategies.discardTool.nudge.enabled',
|
|
37
|
-
'strategies.discardTool.nudge.frequency',
|
|
38
|
-
// strategies.extractTool
|
|
39
|
-
'strategies.extractTool',
|
|
40
|
-
'strategies.extractTool.enabled',
|
|
41
|
-
'strategies.extractTool.protectedTools',
|
|
42
|
-
'strategies.extractTool.turnProtection',
|
|
43
|
-
'strategies.extractTool.turnProtection.enabled',
|
|
44
|
-
'strategies.extractTool.turnProtection.turns',
|
|
45
|
-
'strategies.extractTool.nudge',
|
|
46
|
-
'strategies.extractTool.nudge.enabled',
|
|
47
|
-
'strategies.extractTool.nudge.frequency',
|
|
48
|
-
'strategies.extractTool.showDistillation'
|
|
35
|
+
"strategies.onIdle",
|
|
36
|
+
"strategies.onIdle.enabled",
|
|
37
|
+
"strategies.onIdle.model",
|
|
38
|
+
"strategies.onIdle.showModelErrorToasts",
|
|
39
|
+
"strategies.onIdle.strictModelSelection",
|
|
40
|
+
"strategies.onIdle.protectedTools",
|
|
49
41
|
]);
|
|
50
42
|
// Extract all key paths from a config object for validation
|
|
51
|
-
function getConfigKeyPaths(obj, prefix =
|
|
43
|
+
function getConfigKeyPaths(obj, prefix = "") {
|
|
52
44
|
const keys = [];
|
|
53
45
|
for (const key of Object.keys(obj)) {
|
|
54
46
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
55
47
|
keys.push(fullKey);
|
|
56
|
-
if (obj[key] && typeof obj[key] ===
|
|
48
|
+
if (obj[key] && typeof obj[key] === "object" && !Array.isArray(obj[key])) {
|
|
57
49
|
keys.push(...getConfigKeyPaths(obj[key], fullKey));
|
|
58
50
|
}
|
|
59
51
|
}
|
|
@@ -62,108 +54,174 @@ function getConfigKeyPaths(obj, prefix = '') {
|
|
|
62
54
|
// Returns invalid keys found in user config
|
|
63
55
|
export function getInvalidConfigKeys(userConfig) {
|
|
64
56
|
const userKeys = getConfigKeyPaths(userConfig);
|
|
65
|
-
return userKeys.filter(key => !VALID_CONFIG_KEYS.has(key));
|
|
57
|
+
return userKeys.filter((key) => !VALID_CONFIG_KEYS.has(key));
|
|
66
58
|
}
|
|
67
59
|
function validateConfigTypes(config) {
|
|
68
60
|
const errors = [];
|
|
69
61
|
// Top-level validators
|
|
70
|
-
if (config.enabled !== undefined && typeof config.enabled !==
|
|
71
|
-
errors.push({ key:
|
|
62
|
+
if (config.enabled !== undefined && typeof config.enabled !== "boolean") {
|
|
63
|
+
errors.push({ key: "enabled", expected: "boolean", actual: typeof config.enabled });
|
|
72
64
|
}
|
|
73
|
-
if (config.debug !== undefined && typeof config.debug !==
|
|
74
|
-
errors.push({ key:
|
|
65
|
+
if (config.debug !== undefined && typeof config.debug !== "boolean") {
|
|
66
|
+
errors.push({ key: "debug", expected: "boolean", actual: typeof config.debug });
|
|
75
67
|
}
|
|
76
|
-
if (config.
|
|
77
|
-
const validValues = [
|
|
78
|
-
if (!validValues.includes(config.
|
|
79
|
-
errors.push({
|
|
68
|
+
if (config.pruneNotification !== undefined) {
|
|
69
|
+
const validValues = ["off", "minimal", "detailed"];
|
|
70
|
+
if (!validValues.includes(config.pruneNotification)) {
|
|
71
|
+
errors.push({
|
|
72
|
+
key: "pruneNotification",
|
|
73
|
+
expected: '"off" | "minimal" | "detailed"',
|
|
74
|
+
actual: JSON.stringify(config.pruneNotification),
|
|
75
|
+
});
|
|
80
76
|
}
|
|
81
77
|
}
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
78
|
+
// Top-level turnProtection validator
|
|
79
|
+
if (config.turnProtection) {
|
|
80
|
+
if (config.turnProtection.enabled !== undefined &&
|
|
81
|
+
typeof config.turnProtection.enabled !== "boolean") {
|
|
82
|
+
errors.push({
|
|
83
|
+
key: "turnProtection.enabled",
|
|
84
|
+
expected: "boolean",
|
|
85
|
+
actual: typeof config.turnProtection.enabled,
|
|
86
|
+
});
|
|
88
87
|
}
|
|
89
|
-
if (
|
|
90
|
-
|
|
88
|
+
if (config.turnProtection.turns !== undefined &&
|
|
89
|
+
typeof config.turnProtection.turns !== "number") {
|
|
90
|
+
errors.push({
|
|
91
|
+
key: "turnProtection.turns",
|
|
92
|
+
expected: "number",
|
|
93
|
+
actual: typeof config.turnProtection.turns,
|
|
94
|
+
});
|
|
91
95
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
}
|
|
97
|
+
// Tools validators
|
|
98
|
+
const tools = config.tools;
|
|
99
|
+
if (tools) {
|
|
100
|
+
if (tools.settings) {
|
|
101
|
+
if (tools.settings.nudgeEnabled !== undefined &&
|
|
102
|
+
typeof tools.settings.nudgeEnabled !== "boolean") {
|
|
103
|
+
errors.push({
|
|
104
|
+
key: "tools.settings.nudgeEnabled",
|
|
105
|
+
expected: "boolean",
|
|
106
|
+
actual: typeof tools.settings.nudgeEnabled,
|
|
107
|
+
});
|
|
102
108
|
}
|
|
103
|
-
if (
|
|
104
|
-
|
|
109
|
+
if (tools.settings.nudgeFrequency !== undefined &&
|
|
110
|
+
typeof tools.settings.nudgeFrequency !== "number") {
|
|
111
|
+
errors.push({
|
|
112
|
+
key: "tools.settings.nudgeFrequency",
|
|
113
|
+
expected: "number",
|
|
114
|
+
actual: typeof tools.settings.nudgeFrequency,
|
|
115
|
+
});
|
|
105
116
|
}
|
|
106
|
-
if (
|
|
107
|
-
|
|
117
|
+
if (tools.settings.protectedTools !== undefined &&
|
|
118
|
+
!Array.isArray(tools.settings.protectedTools)) {
|
|
119
|
+
errors.push({
|
|
120
|
+
key: "tools.settings.protectedTools",
|
|
121
|
+
expected: "string[]",
|
|
122
|
+
actual: typeof tools.settings.protectedTools,
|
|
123
|
+
});
|
|
108
124
|
}
|
|
109
125
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
126
|
+
if (tools.discard) {
|
|
127
|
+
if (tools.discard.enabled !== undefined && typeof tools.discard.enabled !== "boolean") {
|
|
128
|
+
errors.push({
|
|
129
|
+
key: "tools.discard.enabled",
|
|
130
|
+
expected: "boolean",
|
|
131
|
+
actual: typeof tools.discard.enabled,
|
|
132
|
+
});
|
|
117
133
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
}
|
|
135
|
+
if (tools.extract) {
|
|
136
|
+
if (tools.extract.enabled !== undefined && typeof tools.extract.enabled !== "boolean") {
|
|
137
|
+
errors.push({
|
|
138
|
+
key: "tools.extract.enabled",
|
|
139
|
+
expected: "boolean",
|
|
140
|
+
actual: typeof tools.extract.enabled,
|
|
141
|
+
});
|
|
125
142
|
}
|
|
126
|
-
if (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
143
|
+
if (tools.extract.showDistillation !== undefined &&
|
|
144
|
+
typeof tools.extract.showDistillation !== "boolean") {
|
|
145
|
+
errors.push({
|
|
146
|
+
key: "tools.extract.showDistillation",
|
|
147
|
+
expected: "boolean",
|
|
148
|
+
actual: typeof tools.extract.showDistillation,
|
|
149
|
+
});
|
|
133
150
|
}
|
|
134
151
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
152
|
+
}
|
|
153
|
+
// Strategies validators
|
|
154
|
+
const strategies = config.strategies;
|
|
155
|
+
if (strategies) {
|
|
156
|
+
// deduplication
|
|
157
|
+
if (strategies.deduplication?.enabled !== undefined &&
|
|
158
|
+
typeof strategies.deduplication.enabled !== "boolean") {
|
|
159
|
+
errors.push({
|
|
160
|
+
key: "strategies.deduplication.enabled",
|
|
161
|
+
expected: "boolean",
|
|
162
|
+
actual: typeof strategies.deduplication.enabled,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (strategies.deduplication?.protectedTools !== undefined &&
|
|
166
|
+
!Array.isArray(strategies.deduplication.protectedTools)) {
|
|
167
|
+
errors.push({
|
|
168
|
+
key: "strategies.deduplication.protectedTools",
|
|
169
|
+
expected: "string[]",
|
|
170
|
+
actual: typeof strategies.deduplication.protectedTools,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
// onIdle
|
|
174
|
+
if (strategies.onIdle) {
|
|
175
|
+
if (strategies.onIdle.enabled !== undefined &&
|
|
176
|
+
typeof strategies.onIdle.enabled !== "boolean") {
|
|
177
|
+
errors.push({
|
|
178
|
+
key: "strategies.onIdle.enabled",
|
|
179
|
+
expected: "boolean",
|
|
180
|
+
actual: typeof strategies.onIdle.enabled,
|
|
181
|
+
});
|
|
139
182
|
}
|
|
140
|
-
if (strategies.
|
|
141
|
-
|
|
183
|
+
if (strategies.onIdle.model !== undefined &&
|
|
184
|
+
typeof strategies.onIdle.model !== "string") {
|
|
185
|
+
errors.push({
|
|
186
|
+
key: "strategies.onIdle.model",
|
|
187
|
+
expected: "string",
|
|
188
|
+
actual: typeof strategies.onIdle.model,
|
|
189
|
+
});
|
|
142
190
|
}
|
|
143
|
-
if (strategies.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
191
|
+
if (strategies.onIdle.showModelErrorToasts !== undefined &&
|
|
192
|
+
typeof strategies.onIdle.showModelErrorToasts !== "boolean") {
|
|
193
|
+
errors.push({
|
|
194
|
+
key: "strategies.onIdle.showModelErrorToasts",
|
|
195
|
+
expected: "boolean",
|
|
196
|
+
actual: typeof strategies.onIdle.showModelErrorToasts,
|
|
197
|
+
});
|
|
150
198
|
}
|
|
151
|
-
if (strategies.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
199
|
+
if (strategies.onIdle.strictModelSelection !== undefined &&
|
|
200
|
+
typeof strategies.onIdle.strictModelSelection !== "boolean") {
|
|
201
|
+
errors.push({
|
|
202
|
+
key: "strategies.onIdle.strictModelSelection",
|
|
203
|
+
expected: "boolean",
|
|
204
|
+
actual: typeof strategies.onIdle.strictModelSelection,
|
|
205
|
+
});
|
|
158
206
|
}
|
|
159
|
-
if (strategies.
|
|
160
|
-
|
|
207
|
+
if (strategies.onIdle.protectedTools !== undefined &&
|
|
208
|
+
!Array.isArray(strategies.onIdle.protectedTools)) {
|
|
209
|
+
errors.push({
|
|
210
|
+
key: "strategies.onIdle.protectedTools",
|
|
211
|
+
expected: "string[]",
|
|
212
|
+
actual: typeof strategies.onIdle.protectedTools,
|
|
213
|
+
});
|
|
161
214
|
}
|
|
162
215
|
}
|
|
163
216
|
// supersedeWrites
|
|
164
217
|
if (strategies.supersedeWrites) {
|
|
165
|
-
if (strategies.supersedeWrites.enabled !== undefined &&
|
|
166
|
-
|
|
218
|
+
if (strategies.supersedeWrites.enabled !== undefined &&
|
|
219
|
+
typeof strategies.supersedeWrites.enabled !== "boolean") {
|
|
220
|
+
errors.push({
|
|
221
|
+
key: "strategies.supersedeWrites.enabled",
|
|
222
|
+
expected: "boolean",
|
|
223
|
+
actual: typeof strategies.supersedeWrites.enabled,
|
|
224
|
+
});
|
|
167
225
|
}
|
|
168
226
|
}
|
|
169
227
|
}
|
|
@@ -176,11 +234,11 @@ function showConfigValidationWarnings(ctx, configPath, configData, isProject) {
|
|
|
176
234
|
if (invalidKeys.length === 0 && typeErrors.length === 0) {
|
|
177
235
|
return;
|
|
178
236
|
}
|
|
179
|
-
const configType = isProject ?
|
|
237
|
+
const configType = isProject ? "project config" : "config";
|
|
180
238
|
const messages = [];
|
|
181
239
|
if (invalidKeys.length > 0) {
|
|
182
|
-
const keyList = invalidKeys.slice(0, 3).join(
|
|
183
|
-
const suffix = invalidKeys.length > 3 ? ` (+${invalidKeys.length - 3} more)` :
|
|
240
|
+
const keyList = invalidKeys.slice(0, 3).join(", ");
|
|
241
|
+
const suffix = invalidKeys.length > 3 ? ` (+${invalidKeys.length - 3} more)` : "";
|
|
184
242
|
messages.push(`Unknown keys: ${keyList}${suffix}`);
|
|
185
243
|
}
|
|
186
244
|
if (typeErrors.length > 0) {
|
|
@@ -196,10 +254,10 @@ function showConfigValidationWarnings(ctx, configPath, configData, isProject) {
|
|
|
196
254
|
ctx.client.tui.showToast({
|
|
197
255
|
body: {
|
|
198
256
|
title: `DCP: Invalid ${configType}`,
|
|
199
|
-
message: `${configPath}\n${messages.join(
|
|
257
|
+
message: `${configPath}\n${messages.join("\n")}`,
|
|
200
258
|
variant: "warning",
|
|
201
|
-
duration: 7000
|
|
202
|
-
}
|
|
259
|
+
duration: 7000,
|
|
260
|
+
},
|
|
203
261
|
});
|
|
204
262
|
}
|
|
205
263
|
catch { }
|
|
@@ -208,55 +266,48 @@ function showConfigValidationWarnings(ctx, configPath, configData, isProject) {
|
|
|
208
266
|
const defaultConfig = {
|
|
209
267
|
enabled: true,
|
|
210
268
|
debug: false,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
269
|
+
pruneNotification: "detailed",
|
|
270
|
+
turnProtection: {
|
|
271
|
+
enabled: false,
|
|
272
|
+
turns: 4,
|
|
273
|
+
},
|
|
274
|
+
tools: {
|
|
275
|
+
settings: {
|
|
276
|
+
nudgeEnabled: true,
|
|
277
|
+
nudgeFrequency: 10,
|
|
278
|
+
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
|
|
279
|
+
},
|
|
280
|
+
discard: {
|
|
214
281
|
enabled: true,
|
|
215
|
-
protectedTools: [...DEFAULT_PROTECTED_TOOLS]
|
|
216
282
|
},
|
|
217
|
-
|
|
218
|
-
enabled: true
|
|
283
|
+
extract: {
|
|
284
|
+
enabled: true,
|
|
285
|
+
showDistillation: false,
|
|
219
286
|
},
|
|
220
|
-
|
|
287
|
+
},
|
|
288
|
+
strategies: {
|
|
289
|
+
deduplication: {
|
|
221
290
|
enabled: true,
|
|
222
291
|
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
|
|
223
|
-
turnProtection: {
|
|
224
|
-
enabled: false,
|
|
225
|
-
turns: 4
|
|
226
|
-
},
|
|
227
|
-
nudge: {
|
|
228
|
-
enabled: true,
|
|
229
|
-
frequency: 10
|
|
230
|
-
}
|
|
231
292
|
},
|
|
232
|
-
|
|
293
|
+
supersedeWrites: {
|
|
233
294
|
enabled: true,
|
|
234
|
-
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
|
|
235
|
-
turnProtection: {
|
|
236
|
-
enabled: false,
|
|
237
|
-
turns: 4
|
|
238
|
-
},
|
|
239
|
-
nudge: {
|
|
240
|
-
enabled: true,
|
|
241
|
-
frequency: 10
|
|
242
|
-
},
|
|
243
|
-
showDistillation: false
|
|
244
295
|
},
|
|
245
296
|
onIdle: {
|
|
246
297
|
enabled: false,
|
|
247
298
|
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
|
|
248
299
|
showModelErrorToasts: true,
|
|
249
|
-
strictModelSelection: false
|
|
250
|
-
}
|
|
251
|
-
}
|
|
300
|
+
strictModelSelection: false,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
252
303
|
};
|
|
253
|
-
const GLOBAL_CONFIG_DIR = join(homedir(),
|
|
254
|
-
const GLOBAL_CONFIG_PATH_JSONC = join(GLOBAL_CONFIG_DIR,
|
|
255
|
-
const GLOBAL_CONFIG_PATH_JSON = join(GLOBAL_CONFIG_DIR,
|
|
304
|
+
const GLOBAL_CONFIG_DIR = join(homedir(), ".config", "opencode");
|
|
305
|
+
const GLOBAL_CONFIG_PATH_JSONC = join(GLOBAL_CONFIG_DIR, "dcp.jsonc");
|
|
306
|
+
const GLOBAL_CONFIG_PATH_JSON = join(GLOBAL_CONFIG_DIR, "dcp.json");
|
|
256
307
|
function findOpencodeDir(startDir) {
|
|
257
308
|
let current = startDir;
|
|
258
|
-
while (current !==
|
|
259
|
-
const candidate = join(current,
|
|
309
|
+
while (current !== "/") {
|
|
310
|
+
const candidate = join(current, ".opencode");
|
|
260
311
|
if (existsSync(candidate) && statSync(candidate).isDirectory()) {
|
|
261
312
|
return candidate;
|
|
262
313
|
}
|
|
@@ -280,8 +331,8 @@ function getConfigPaths(ctx) {
|
|
|
280
331
|
let configDirPath = null;
|
|
281
332
|
const opencodeConfigDir = process.env.OPENCODE_CONFIG_DIR;
|
|
282
333
|
if (opencodeConfigDir) {
|
|
283
|
-
const configJsonc = join(opencodeConfigDir,
|
|
284
|
-
const configJson = join(opencodeConfigDir,
|
|
334
|
+
const configJsonc = join(opencodeConfigDir, "dcp.jsonc");
|
|
335
|
+
const configJson = join(opencodeConfigDir, "dcp.json");
|
|
285
336
|
if (existsSync(configJsonc)) {
|
|
286
337
|
configDirPath = configJsonc;
|
|
287
338
|
}
|
|
@@ -294,8 +345,8 @@ function getConfigPaths(ctx) {
|
|
|
294
345
|
if (ctx?.directory) {
|
|
295
346
|
const opencodeDir = findOpencodeDir(ctx.directory);
|
|
296
347
|
if (opencodeDir) {
|
|
297
|
-
const projectJsonc = join(opencodeDir,
|
|
298
|
-
const projectJson = join(opencodeDir,
|
|
348
|
+
const projectJsonc = join(opencodeDir, "dcp.jsonc");
|
|
349
|
+
const projectJson = join(opencodeDir, "dcp.json");
|
|
299
350
|
if (existsSync(projectJsonc)) {
|
|
300
351
|
projectPath = projectJsonc;
|
|
301
352
|
}
|
|
@@ -315,9 +366,35 @@ function createDefaultConfig() {
|
|
|
315
366
|
"enabled": true,
|
|
316
367
|
// Enable debug logging to ~/.config/opencode/logs/dcp/
|
|
317
368
|
"debug": false,
|
|
318
|
-
//
|
|
319
|
-
"
|
|
320
|
-
//
|
|
369
|
+
// Notification display: "off", "minimal", or "detailed"
|
|
370
|
+
"pruneNotification": "detailed",
|
|
371
|
+
// Protect from pruning for <turns> message turns
|
|
372
|
+
"turnProtection": {
|
|
373
|
+
"enabled": false,
|
|
374
|
+
"turns": 4
|
|
375
|
+
},
|
|
376
|
+
// LLM-driven context pruning tools
|
|
377
|
+
"tools": {
|
|
378
|
+
// Shared settings for all prune tools
|
|
379
|
+
"settings": {
|
|
380
|
+
// Nudge the LLM to use prune tools (every <nudgeFrequency> tool results)
|
|
381
|
+
"nudgeEnabled": true,
|
|
382
|
+
"nudgeFrequency": 10,
|
|
383
|
+
// Additional tools to protect from pruning
|
|
384
|
+
"protectedTools": []
|
|
385
|
+
},
|
|
386
|
+
// Removes tool content from context without preservation (for completed tasks or noise)
|
|
387
|
+
"discard": {
|
|
388
|
+
"enabled": true
|
|
389
|
+
},
|
|
390
|
+
// Distills key findings into preserved knowledge before removing raw content
|
|
391
|
+
"extract": {
|
|
392
|
+
"enabled": true,
|
|
393
|
+
// Show distillation content as an ignored message notification
|
|
394
|
+
"showDistillation": false
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
// Automatic pruning strategies
|
|
321
398
|
"strategies": {
|
|
322
399
|
// Remove duplicate tool calls (same tool with same arguments)
|
|
323
400
|
"deduplication": {
|
|
@@ -329,40 +406,6 @@ function createDefaultConfig() {
|
|
|
329
406
|
"supersedeWrites": {
|
|
330
407
|
"enabled": true
|
|
331
408
|
},
|
|
332
|
-
// Removes tool content from context without preservation (for completed tasks or noise)
|
|
333
|
-
"discardTool": {
|
|
334
|
-
"enabled": true,
|
|
335
|
-
// Additional tools to protect from pruning
|
|
336
|
-
"protectedTools": [],
|
|
337
|
-
// Protect from pruning for <turn protection> message turns
|
|
338
|
-
"turnProtection": {
|
|
339
|
-
"enabled": false,
|
|
340
|
-
"turns": 4
|
|
341
|
-
},
|
|
342
|
-
// Nudge the LLM to use the discard tool (every <frequency> tool results)
|
|
343
|
-
"nudge": {
|
|
344
|
-
"enabled": true,
|
|
345
|
-
"frequency": 10
|
|
346
|
-
}
|
|
347
|
-
},
|
|
348
|
-
// Distills key findings into preserved knowledge before removing raw content
|
|
349
|
-
"extractTool": {
|
|
350
|
-
"enabled": true,
|
|
351
|
-
// Additional tools to protect from pruning
|
|
352
|
-
"protectedTools": [],
|
|
353
|
-
// Protect from pruning for <turn protection> message turns
|
|
354
|
-
"turnProtection": {
|
|
355
|
-
"enabled": false,
|
|
356
|
-
"turns": 4
|
|
357
|
-
},
|
|
358
|
-
// Nudge the LLM to use the extract tool (every <frequency> tool results)
|
|
359
|
-
"nudge": {
|
|
360
|
-
"enabled": true,
|
|
361
|
-
"frequency": 10
|
|
362
|
-
},
|
|
363
|
-
// Show distillation content as an ignored message notification
|
|
364
|
-
"showDistillation": false
|
|
365
|
-
},
|
|
366
409
|
// (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
|
|
367
410
|
"onIdle": {
|
|
368
411
|
"enabled": false,
|
|
@@ -378,12 +421,12 @@ function createDefaultConfig() {
|
|
|
378
421
|
}
|
|
379
422
|
}
|
|
380
423
|
`;
|
|
381
|
-
writeFileSync(GLOBAL_CONFIG_PATH_JSONC, configContent,
|
|
424
|
+
writeFileSync(GLOBAL_CONFIG_PATH_JSONC, configContent, "utf-8");
|
|
382
425
|
}
|
|
383
426
|
function loadConfigFile(configPath) {
|
|
384
427
|
let fileContent;
|
|
385
428
|
try {
|
|
386
|
-
fileContent = readFileSync(configPath,
|
|
429
|
+
fileContent = readFileSync(configPath, "utf-8");
|
|
387
430
|
}
|
|
388
431
|
catch {
|
|
389
432
|
// File doesn't exist or can't be read - not a parse error
|
|
@@ -392,12 +435,12 @@ function loadConfigFile(configPath) {
|
|
|
392
435
|
try {
|
|
393
436
|
const parsed = parse(fileContent);
|
|
394
437
|
if (parsed === undefined || parsed === null) {
|
|
395
|
-
return { data: null, parseError:
|
|
438
|
+
return { data: null, parseError: "Config file is empty or invalid" };
|
|
396
439
|
}
|
|
397
440
|
return { data: parsed };
|
|
398
441
|
}
|
|
399
442
|
catch (error) {
|
|
400
|
-
return { data: null, parseError: error.message ||
|
|
443
|
+
return { data: null, parseError: error.message || "Failed to parse config" };
|
|
401
444
|
}
|
|
402
445
|
}
|
|
403
446
|
function mergeStrategies(base, override) {
|
|
@@ -409,9 +452,9 @@ function mergeStrategies(base, override) {
|
|
|
409
452
|
protectedTools: [
|
|
410
453
|
...new Set([
|
|
411
454
|
...base.deduplication.protectedTools,
|
|
412
|
-
...(override.deduplication?.protectedTools ?? [])
|
|
413
|
-
])
|
|
414
|
-
]
|
|
455
|
+
...(override.deduplication?.protectedTools ?? []),
|
|
456
|
+
]),
|
|
457
|
+
],
|
|
415
458
|
},
|
|
416
459
|
onIdle: {
|
|
417
460
|
enabled: override.onIdle?.enabled ?? base.onIdle.enabled,
|
|
@@ -421,79 +464,63 @@ function mergeStrategies(base, override) {
|
|
|
421
464
|
protectedTools: [
|
|
422
465
|
...new Set([
|
|
423
466
|
...base.onIdle.protectedTools,
|
|
424
|
-
...(override.onIdle?.protectedTools ?? [])
|
|
425
|
-
])
|
|
426
|
-
]
|
|
427
|
-
},
|
|
428
|
-
discardTool: {
|
|
429
|
-
enabled: override.discardTool?.enabled ?? base.discardTool.enabled,
|
|
430
|
-
protectedTools: [
|
|
431
|
-
...new Set([
|
|
432
|
-
...base.discardTool.protectedTools,
|
|
433
|
-
...(override.discardTool?.protectedTools ?? [])
|
|
434
|
-
])
|
|
467
|
+
...(override.onIdle?.protectedTools ?? []),
|
|
468
|
+
]),
|
|
435
469
|
],
|
|
436
|
-
turnProtection: {
|
|
437
|
-
enabled: override.discardTool?.turnProtection?.enabled ?? base.discardTool.turnProtection.enabled,
|
|
438
|
-
turns: override.discardTool?.turnProtection?.turns ?? base.discardTool.turnProtection.turns
|
|
439
|
-
},
|
|
440
|
-
nudge: {
|
|
441
|
-
enabled: override.discardTool?.nudge?.enabled ?? base.discardTool.nudge.enabled,
|
|
442
|
-
frequency: override.discardTool?.nudge?.frequency ?? base.discardTool.nudge.frequency
|
|
443
|
-
}
|
|
444
470
|
},
|
|
445
|
-
|
|
446
|
-
enabled: override.
|
|
471
|
+
supersedeWrites: {
|
|
472
|
+
enabled: override.supersedeWrites?.enabled ?? base.supersedeWrites.enabled,
|
|
473
|
+
},
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
function mergeTools(base, override) {
|
|
477
|
+
if (!override)
|
|
478
|
+
return base;
|
|
479
|
+
return {
|
|
480
|
+
settings: {
|
|
481
|
+
nudgeEnabled: override.settings?.nudgeEnabled ?? base.settings.nudgeEnabled,
|
|
482
|
+
nudgeFrequency: override.settings?.nudgeFrequency ?? base.settings.nudgeFrequency,
|
|
447
483
|
protectedTools: [
|
|
448
484
|
...new Set([
|
|
449
|
-
...base.
|
|
450
|
-
...(override.
|
|
451
|
-
])
|
|
485
|
+
...base.settings.protectedTools,
|
|
486
|
+
...(override.settings?.protectedTools ?? []),
|
|
487
|
+
]),
|
|
452
488
|
],
|
|
453
|
-
turnProtection: {
|
|
454
|
-
enabled: override.extractTool?.turnProtection?.enabled ?? base.extractTool.turnProtection.enabled,
|
|
455
|
-
turns: override.extractTool?.turnProtection?.turns ?? base.extractTool.turnProtection.turns
|
|
456
|
-
},
|
|
457
|
-
nudge: {
|
|
458
|
-
enabled: override.extractTool?.nudge?.enabled ?? base.extractTool.nudge.enabled,
|
|
459
|
-
frequency: override.extractTool?.nudge?.frequency ?? base.extractTool.nudge.frequency
|
|
460
|
-
},
|
|
461
|
-
showDistillation: override.extractTool?.showDistillation ?? base.extractTool.showDistillation
|
|
462
489
|
},
|
|
463
|
-
|
|
464
|
-
enabled: override.
|
|
465
|
-
}
|
|
490
|
+
discard: {
|
|
491
|
+
enabled: override.discard?.enabled ?? base.discard.enabled,
|
|
492
|
+
},
|
|
493
|
+
extract: {
|
|
494
|
+
enabled: override.extract?.enabled ?? base.extract.enabled,
|
|
495
|
+
showDistillation: override.extract?.showDistillation ?? base.extract.showDistillation,
|
|
496
|
+
},
|
|
466
497
|
};
|
|
467
498
|
}
|
|
468
499
|
function deepCloneConfig(config) {
|
|
469
500
|
return {
|
|
470
501
|
...config,
|
|
502
|
+
turnProtection: { ...config.turnProtection },
|
|
503
|
+
tools: {
|
|
504
|
+
settings: {
|
|
505
|
+
...config.tools.settings,
|
|
506
|
+
protectedTools: [...config.tools.settings.protectedTools],
|
|
507
|
+
},
|
|
508
|
+
discard: { ...config.tools.discard },
|
|
509
|
+
extract: { ...config.tools.extract },
|
|
510
|
+
},
|
|
471
511
|
strategies: {
|
|
472
512
|
deduplication: {
|
|
473
513
|
...config.strategies.deduplication,
|
|
474
|
-
protectedTools: [...config.strategies.deduplication.protectedTools]
|
|
514
|
+
protectedTools: [...config.strategies.deduplication.protectedTools],
|
|
475
515
|
},
|
|
476
516
|
onIdle: {
|
|
477
517
|
...config.strategies.onIdle,
|
|
478
|
-
protectedTools: [...config.strategies.onIdle.protectedTools]
|
|
479
|
-
},
|
|
480
|
-
discardTool: {
|
|
481
|
-
...config.strategies.discardTool,
|
|
482
|
-
protectedTools: [...config.strategies.discardTool.protectedTools],
|
|
483
|
-
turnProtection: { ...config.strategies.discardTool.turnProtection },
|
|
484
|
-
nudge: { ...config.strategies.discardTool.nudge }
|
|
485
|
-
},
|
|
486
|
-
extractTool: {
|
|
487
|
-
...config.strategies.extractTool,
|
|
488
|
-
protectedTools: [...config.strategies.extractTool.protectedTools],
|
|
489
|
-
turnProtection: { ...config.strategies.extractTool.turnProtection },
|
|
490
|
-
nudge: { ...config.strategies.extractTool.nudge },
|
|
491
|
-
showDistillation: config.strategies.extractTool.showDistillation
|
|
518
|
+
protectedTools: [...config.strategies.onIdle.protectedTools],
|
|
492
519
|
},
|
|
493
520
|
supersedeWrites: {
|
|
494
|
-
...config.strategies.supersedeWrites
|
|
495
|
-
}
|
|
496
|
-
}
|
|
521
|
+
...config.strategies.supersedeWrites,
|
|
522
|
+
},
|
|
523
|
+
},
|
|
497
524
|
};
|
|
498
525
|
}
|
|
499
526
|
export function getConfig(ctx) {
|
|
@@ -510,8 +537,8 @@ export function getConfig(ctx) {
|
|
|
510
537
|
title: "DCP: Invalid config",
|
|
511
538
|
message: `${configPaths.global}\n${result.parseError}\nUsing default values`,
|
|
512
539
|
variant: "warning",
|
|
513
|
-
duration: 7000
|
|
514
|
-
}
|
|
540
|
+
duration: 7000,
|
|
541
|
+
},
|
|
515
542
|
});
|
|
516
543
|
}
|
|
517
544
|
catch { }
|
|
@@ -523,8 +550,13 @@ export function getConfig(ctx) {
|
|
|
523
550
|
config = {
|
|
524
551
|
enabled: result.data.enabled ?? config.enabled,
|
|
525
552
|
debug: result.data.debug ?? config.debug,
|
|
526
|
-
|
|
527
|
-
|
|
553
|
+
pruneNotification: result.data.pruneNotification ?? config.pruneNotification,
|
|
554
|
+
turnProtection: {
|
|
555
|
+
enabled: result.data.turnProtection?.enabled ?? config.turnProtection.enabled,
|
|
556
|
+
turns: result.data.turnProtection?.turns ?? config.turnProtection.turns,
|
|
557
|
+
},
|
|
558
|
+
tools: mergeTools(config.tools, result.data.tools),
|
|
559
|
+
strategies: mergeStrategies(config.strategies, result.data.strategies),
|
|
528
560
|
};
|
|
529
561
|
}
|
|
530
562
|
}
|
|
@@ -543,8 +575,8 @@ export function getConfig(ctx) {
|
|
|
543
575
|
title: "DCP: Invalid configDir config",
|
|
544
576
|
message: `${configPaths.configDir}\n${result.parseError}\nUsing global/default values`,
|
|
545
577
|
variant: "warning",
|
|
546
|
-
duration: 7000
|
|
547
|
-
}
|
|
578
|
+
duration: 7000,
|
|
579
|
+
},
|
|
548
580
|
});
|
|
549
581
|
}
|
|
550
582
|
catch { }
|
|
@@ -556,8 +588,13 @@ export function getConfig(ctx) {
|
|
|
556
588
|
config = {
|
|
557
589
|
enabled: result.data.enabled ?? config.enabled,
|
|
558
590
|
debug: result.data.debug ?? config.debug,
|
|
559
|
-
|
|
560
|
-
|
|
591
|
+
pruneNotification: result.data.pruneNotification ?? config.pruneNotification,
|
|
592
|
+
turnProtection: {
|
|
593
|
+
enabled: result.data.turnProtection?.enabled ?? config.turnProtection.enabled,
|
|
594
|
+
turns: result.data.turnProtection?.turns ?? config.turnProtection.turns,
|
|
595
|
+
},
|
|
596
|
+
tools: mergeTools(config.tools, result.data.tools),
|
|
597
|
+
strategies: mergeStrategies(config.strategies, result.data.strategies),
|
|
561
598
|
};
|
|
562
599
|
}
|
|
563
600
|
}
|
|
@@ -572,8 +609,8 @@ export function getConfig(ctx) {
|
|
|
572
609
|
title: "DCP: Invalid project config",
|
|
573
610
|
message: `${configPaths.project}\n${result.parseError}\nUsing global/default values`,
|
|
574
611
|
variant: "warning",
|
|
575
|
-
duration: 7000
|
|
576
|
-
}
|
|
612
|
+
duration: 7000,
|
|
613
|
+
},
|
|
577
614
|
});
|
|
578
615
|
}
|
|
579
616
|
catch { }
|
|
@@ -585,8 +622,13 @@ export function getConfig(ctx) {
|
|
|
585
622
|
config = {
|
|
586
623
|
enabled: result.data.enabled ?? config.enabled,
|
|
587
624
|
debug: result.data.debug ?? config.debug,
|
|
588
|
-
|
|
589
|
-
|
|
625
|
+
pruneNotification: result.data.pruneNotification ?? config.pruneNotification,
|
|
626
|
+
turnProtection: {
|
|
627
|
+
enabled: result.data.turnProtection?.enabled ?? config.turnProtection.enabled,
|
|
628
|
+
turns: result.data.turnProtection?.turns ?? config.turnProtection.turns,
|
|
629
|
+
},
|
|
630
|
+
tools: mergeTools(config.tools, result.data.tools),
|
|
631
|
+
strategies: mergeStrategies(config.strategies, result.data.strategies),
|
|
590
632
|
};
|
|
591
633
|
}
|
|
592
634
|
}
|