@tarquinen/opencode-dcp 1.0.4 → 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 +68 -43
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +47 -17
- package/dist/index.js.map +1 -1
- package/dist/lib/config.d.ts +20 -7
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +320 -158
- 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 +90 -29
- 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 +37 -25
- package/dist/lib/prompt.js.map +1 -1
- package/dist/lib/prompts/discard-tool-spec.txt +56 -0
- package/dist/lib/prompts/extract-tool-spec.txt +79 -0
- package/dist/lib/prompts/nudge/nudge-both.txt +10 -0
- package/dist/lib/prompts/nudge/nudge-discard.txt +9 -0
- package/dist/lib/prompts/nudge/nudge-extract.txt +9 -0
- package/dist/lib/prompts/{synthetic.txt → system/system-prompt-both.txt} +23 -13
- package/dist/lib/prompts/system/system-prompt-discard.txt +49 -0
- package/dist/lib/prompts/system/system-prompt-extract.txt +49 -0
- 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 +1 -0
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/state/state.js +26 -6
- 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 +24 -10
- package/dist/lib/state/tool-cache.js.map +1 -1
- package/dist/lib/state/types.d.ts +2 -0
- package/dist/lib/state/types.d.ts.map +1 -1
- package/dist/lib/strategies/deduplication.js +4 -4
- package/dist/lib/strategies/deduplication.js.map +1 -1
- package/dist/lib/strategies/index.d.ts +1 -1
- package/dist/lib/strategies/index.d.ts.map +1 -1
- package/dist/lib/strategies/index.js +1 -1
- package/dist/lib/strategies/index.js.map +1 -1
- package/dist/lib/strategies/on-idle.d.ts.map +1 -1
- package/dist/lib/strategies/on-idle.js +25 -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/{prune-tool.d.ts → tools.d.ts} +3 -6
- package/dist/lib/strategies/tools.d.ts.map +1 -0
- package/dist/lib/strategies/tools.js +127 -0
- package/dist/lib/strategies/tools.js.map +1 -0
- package/dist/lib/strategies/utils.d.ts +0 -1
- package/dist/lib/strategies/utils.d.ts.map +1 -1
- package/dist/lib/strategies/utils.js +20 -10
- package/dist/lib/strategies/utils.js.map +1 -1
- package/dist/lib/ui/notification.d.ts +1 -0
- package/dist/lib/ui/notification.d.ts.map +1 -1
- package/dist/lib/ui/notification.js +44 -20
- 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/prompts/nudge.txt +0 -10
- package/dist/lib/prompts/tool.txt +0 -72
- package/dist/lib/strategies/prune-tool.d.ts.map +0 -1
- package/dist/lib/strategies/prune-tool.js +0 -88
- package/dist/lib/strategies/prune-tool.js.map +0 -1
- /package/dist/lib/prompts/{pruning.txt → on-idle-analysis.txt} +0 -0
package/dist/lib/config.js
CHANGED
|
@@ -1,45 +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.pruneTool
|
|
29
|
-
'strategies.pruneTool',
|
|
30
|
-
'strategies.pruneTool.enabled',
|
|
31
|
-
'strategies.pruneTool.protectedTools',
|
|
32
|
-
'strategies.pruneTool.nudge',
|
|
33
|
-
'strategies.pruneTool.nudge.enabled',
|
|
34
|
-
'strategies.pruneTool.nudge.frequency'
|
|
35
|
+
"strategies.onIdle",
|
|
36
|
+
"strategies.onIdle.enabled",
|
|
37
|
+
"strategies.onIdle.model",
|
|
38
|
+
"strategies.onIdle.showModelErrorToasts",
|
|
39
|
+
"strategies.onIdle.strictModelSelection",
|
|
40
|
+
"strategies.onIdle.protectedTools",
|
|
35
41
|
]);
|
|
36
42
|
// Extract all key paths from a config object for validation
|
|
37
|
-
function getConfigKeyPaths(obj, prefix =
|
|
43
|
+
function getConfigKeyPaths(obj, prefix = "") {
|
|
38
44
|
const keys = [];
|
|
39
45
|
for (const key of Object.keys(obj)) {
|
|
40
46
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
41
47
|
keys.push(fullKey);
|
|
42
|
-
if (obj[key] && typeof obj[key] ===
|
|
48
|
+
if (obj[key] && typeof obj[key] === "object" && !Array.isArray(obj[key])) {
|
|
43
49
|
keys.push(...getConfigKeyPaths(obj[key], fullKey));
|
|
44
50
|
}
|
|
45
51
|
}
|
|
@@ -48,72 +54,174 @@ function getConfigKeyPaths(obj, prefix = '') {
|
|
|
48
54
|
// Returns invalid keys found in user config
|
|
49
55
|
export function getInvalidConfigKeys(userConfig) {
|
|
50
56
|
const userKeys = getConfigKeyPaths(userConfig);
|
|
51
|
-
return userKeys.filter(key => !VALID_CONFIG_KEYS.has(key));
|
|
57
|
+
return userKeys.filter((key) => !VALID_CONFIG_KEYS.has(key));
|
|
52
58
|
}
|
|
53
59
|
function validateConfigTypes(config) {
|
|
54
60
|
const errors = [];
|
|
55
61
|
// Top-level validators
|
|
56
|
-
if (config.enabled !== undefined && typeof config.enabled !==
|
|
57
|
-
errors.push({ key:
|
|
62
|
+
if (config.enabled !== undefined && typeof config.enabled !== "boolean") {
|
|
63
|
+
errors.push({ key: "enabled", expected: "boolean", actual: typeof config.enabled });
|
|
58
64
|
}
|
|
59
|
-
if (config.debug !== undefined && typeof config.debug !==
|
|
60
|
-
errors.push({ key:
|
|
65
|
+
if (config.debug !== undefined && typeof config.debug !== "boolean") {
|
|
66
|
+
errors.push({ key: "debug", expected: "boolean", actual: typeof config.debug });
|
|
61
67
|
}
|
|
62
|
-
if (config.
|
|
63
|
-
const validValues = [
|
|
64
|
-
if (!validValues.includes(config.
|
|
65
|
-
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
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
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
|
+
});
|
|
87
|
+
}
|
|
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
|
+
});
|
|
95
|
+
}
|
|
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
|
+
});
|
|
108
|
+
}
|
|
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
|
+
});
|
|
116
|
+
}
|
|
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
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
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
|
+
});
|
|
133
|
+
}
|
|
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
|
+
});
|
|
142
|
+
}
|
|
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
|
+
});
|
|
150
|
+
}
|
|
66
151
|
}
|
|
67
152
|
}
|
|
68
153
|
// Strategies validators
|
|
69
154
|
const strategies = config.strategies;
|
|
70
155
|
if (strategies) {
|
|
71
156
|
// deduplication
|
|
72
|
-
if (strategies.deduplication?.enabled !== undefined &&
|
|
73
|
-
|
|
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
|
+
});
|
|
74
164
|
}
|
|
75
|
-
if (strategies.deduplication?.protectedTools !== undefined &&
|
|
76
|
-
|
|
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
|
+
});
|
|
77
172
|
}
|
|
78
173
|
// onIdle
|
|
79
174
|
if (strategies.onIdle) {
|
|
80
|
-
if (strategies.onIdle.enabled !== undefined &&
|
|
81
|
-
|
|
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
|
+
});
|
|
82
182
|
}
|
|
83
|
-
if (strategies.onIdle.model !== undefined &&
|
|
84
|
-
|
|
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
|
+
});
|
|
85
190
|
}
|
|
86
|
-
if (strategies.onIdle.showModelErrorToasts !== undefined &&
|
|
87
|
-
|
|
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
|
+
});
|
|
88
198
|
}
|
|
89
|
-
if (strategies.onIdle.strictModelSelection !== undefined &&
|
|
90
|
-
|
|
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
|
+
});
|
|
91
206
|
}
|
|
92
|
-
if (strategies.onIdle.protectedTools !== undefined &&
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
errors.push({ key: 'strategies.pruneTool.enabled', expected: 'boolean', actual: typeof strategies.pruneTool.enabled });
|
|
100
|
-
}
|
|
101
|
-
if (strategies.pruneTool.protectedTools !== undefined && !Array.isArray(strategies.pruneTool.protectedTools)) {
|
|
102
|
-
errors.push({ key: 'strategies.pruneTool.protectedTools', expected: 'string[]', actual: typeof strategies.pruneTool.protectedTools });
|
|
103
|
-
}
|
|
104
|
-
if (strategies.pruneTool.nudge) {
|
|
105
|
-
if (strategies.pruneTool.nudge.enabled !== undefined && typeof strategies.pruneTool.nudge.enabled !== 'boolean') {
|
|
106
|
-
errors.push({ key: 'strategies.pruneTool.nudge.enabled', expected: 'boolean', actual: typeof strategies.pruneTool.nudge.enabled });
|
|
107
|
-
}
|
|
108
|
-
if (strategies.pruneTool.nudge.frequency !== undefined && typeof strategies.pruneTool.nudge.frequency !== 'number') {
|
|
109
|
-
errors.push({ key: 'strategies.pruneTool.nudge.frequency', expected: 'number', actual: typeof strategies.pruneTool.nudge.frequency });
|
|
110
|
-
}
|
|
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
|
+
});
|
|
111
214
|
}
|
|
112
215
|
}
|
|
113
216
|
// supersedeWrites
|
|
114
217
|
if (strategies.supersedeWrites) {
|
|
115
|
-
if (strategies.supersedeWrites.enabled !== undefined &&
|
|
116
|
-
|
|
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
|
+
});
|
|
117
225
|
}
|
|
118
226
|
}
|
|
119
227
|
}
|
|
@@ -126,11 +234,11 @@ function showConfigValidationWarnings(ctx, configPath, configData, isProject) {
|
|
|
126
234
|
if (invalidKeys.length === 0 && typeErrors.length === 0) {
|
|
127
235
|
return;
|
|
128
236
|
}
|
|
129
|
-
const configType = isProject ?
|
|
237
|
+
const configType = isProject ? "project config" : "config";
|
|
130
238
|
const messages = [];
|
|
131
239
|
if (invalidKeys.length > 0) {
|
|
132
|
-
const keyList = invalidKeys.slice(0, 3).join(
|
|
133
|
-
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)` : "";
|
|
134
242
|
messages.push(`Unknown keys: ${keyList}${suffix}`);
|
|
135
243
|
}
|
|
136
244
|
if (typeErrors.length > 0) {
|
|
@@ -146,10 +254,10 @@ function showConfigValidationWarnings(ctx, configPath, configData, isProject) {
|
|
|
146
254
|
ctx.client.tui.showToast({
|
|
147
255
|
body: {
|
|
148
256
|
title: `DCP: Invalid ${configType}`,
|
|
149
|
-
message: `${configPath}\n${messages.join(
|
|
257
|
+
message: `${configPath}\n${messages.join("\n")}`,
|
|
150
258
|
variant: "warning",
|
|
151
|
-
duration: 7000
|
|
152
|
-
}
|
|
259
|
+
duration: 7000,
|
|
260
|
+
},
|
|
153
261
|
});
|
|
154
262
|
}
|
|
155
263
|
catch { }
|
|
@@ -158,38 +266,48 @@ function showConfigValidationWarnings(ctx, configPath, configData, isProject) {
|
|
|
158
266
|
const defaultConfig = {
|
|
159
267
|
enabled: true,
|
|
160
268
|
debug: false,
|
|
161
|
-
|
|
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: {
|
|
281
|
+
enabled: true,
|
|
282
|
+
},
|
|
283
|
+
extract: {
|
|
284
|
+
enabled: true,
|
|
285
|
+
showDistillation: false,
|
|
286
|
+
},
|
|
287
|
+
},
|
|
162
288
|
strategies: {
|
|
163
289
|
deduplication: {
|
|
164
290
|
enabled: true,
|
|
165
|
-
protectedTools: [...DEFAULT_PROTECTED_TOOLS]
|
|
291
|
+
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
|
|
166
292
|
},
|
|
167
293
|
supersedeWrites: {
|
|
168
|
-
enabled: true
|
|
169
|
-
},
|
|
170
|
-
pruneTool: {
|
|
171
294
|
enabled: true,
|
|
172
|
-
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
|
|
173
|
-
nudge: {
|
|
174
|
-
enabled: true,
|
|
175
|
-
frequency: 10
|
|
176
|
-
}
|
|
177
295
|
},
|
|
178
296
|
onIdle: {
|
|
179
297
|
enabled: false,
|
|
180
298
|
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
|
|
181
299
|
showModelErrorToasts: true,
|
|
182
|
-
strictModelSelection: false
|
|
183
|
-
}
|
|
184
|
-
}
|
|
300
|
+
strictModelSelection: false,
|
|
301
|
+
},
|
|
302
|
+
},
|
|
185
303
|
};
|
|
186
|
-
const GLOBAL_CONFIG_DIR = join(homedir(),
|
|
187
|
-
const GLOBAL_CONFIG_PATH_JSONC = join(GLOBAL_CONFIG_DIR,
|
|
188
|
-
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");
|
|
189
307
|
function findOpencodeDir(startDir) {
|
|
190
308
|
let current = startDir;
|
|
191
|
-
while (current !==
|
|
192
|
-
const candidate = join(current,
|
|
309
|
+
while (current !== "/") {
|
|
310
|
+
const candidate = join(current, ".opencode");
|
|
193
311
|
if (existsSync(candidate) && statSync(candidate).isDirectory()) {
|
|
194
312
|
return candidate;
|
|
195
313
|
}
|
|
@@ -213,8 +331,8 @@ function getConfigPaths(ctx) {
|
|
|
213
331
|
let configDirPath = null;
|
|
214
332
|
const opencodeConfigDir = process.env.OPENCODE_CONFIG_DIR;
|
|
215
333
|
if (opencodeConfigDir) {
|
|
216
|
-
const configJsonc = join(opencodeConfigDir,
|
|
217
|
-
const configJson = join(opencodeConfigDir,
|
|
334
|
+
const configJsonc = join(opencodeConfigDir, "dcp.jsonc");
|
|
335
|
+
const configJson = join(opencodeConfigDir, "dcp.json");
|
|
218
336
|
if (existsSync(configJsonc)) {
|
|
219
337
|
configDirPath = configJsonc;
|
|
220
338
|
}
|
|
@@ -227,8 +345,8 @@ function getConfigPaths(ctx) {
|
|
|
227
345
|
if (ctx?.directory) {
|
|
228
346
|
const opencodeDir = findOpencodeDir(ctx.directory);
|
|
229
347
|
if (opencodeDir) {
|
|
230
|
-
const projectJsonc = join(opencodeDir,
|
|
231
|
-
const projectJson = join(opencodeDir,
|
|
348
|
+
const projectJsonc = join(opencodeDir, "dcp.jsonc");
|
|
349
|
+
const projectJson = join(opencodeDir, "dcp.json");
|
|
232
350
|
if (existsSync(projectJsonc)) {
|
|
233
351
|
projectPath = projectJsonc;
|
|
234
352
|
}
|
|
@@ -248,9 +366,35 @@ function createDefaultConfig() {
|
|
|
248
366
|
"enabled": true,
|
|
249
367
|
// Enable debug logging to ~/.config/opencode/logs/dcp/
|
|
250
368
|
"debug": false,
|
|
251
|
-
//
|
|
252
|
-
"
|
|
253
|
-
//
|
|
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
|
|
254
398
|
"strategies": {
|
|
255
399
|
// Remove duplicate tool calls (same tool with same arguments)
|
|
256
400
|
"deduplication": {
|
|
@@ -262,17 +406,6 @@ function createDefaultConfig() {
|
|
|
262
406
|
"supersedeWrites": {
|
|
263
407
|
"enabled": true
|
|
264
408
|
},
|
|
265
|
-
// Exposes a prune tool to your LLM to call when it determines pruning is necessary
|
|
266
|
-
"pruneTool": {
|
|
267
|
-
"enabled": true,
|
|
268
|
-
// Additional tools to protect from pruning
|
|
269
|
-
"protectedTools": [],
|
|
270
|
-
// Nudge the LLM to use the prune tool (every <frequency> tool results)
|
|
271
|
-
"nudge": {
|
|
272
|
-
"enabled": true,
|
|
273
|
-
"frequency": 10
|
|
274
|
-
}
|
|
275
|
-
},
|
|
276
409
|
// (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
|
|
277
410
|
"onIdle": {
|
|
278
411
|
"enabled": false,
|
|
@@ -288,12 +421,12 @@ function createDefaultConfig() {
|
|
|
288
421
|
}
|
|
289
422
|
}
|
|
290
423
|
`;
|
|
291
|
-
writeFileSync(GLOBAL_CONFIG_PATH_JSONC, configContent,
|
|
424
|
+
writeFileSync(GLOBAL_CONFIG_PATH_JSONC, configContent, "utf-8");
|
|
292
425
|
}
|
|
293
426
|
function loadConfigFile(configPath) {
|
|
294
427
|
let fileContent;
|
|
295
428
|
try {
|
|
296
|
-
fileContent = readFileSync(configPath,
|
|
429
|
+
fileContent = readFileSync(configPath, "utf-8");
|
|
297
430
|
}
|
|
298
431
|
catch {
|
|
299
432
|
// File doesn't exist or can't be read - not a parse error
|
|
@@ -302,12 +435,12 @@ function loadConfigFile(configPath) {
|
|
|
302
435
|
try {
|
|
303
436
|
const parsed = parse(fileContent);
|
|
304
437
|
if (parsed === undefined || parsed === null) {
|
|
305
|
-
return { data: null, parseError:
|
|
438
|
+
return { data: null, parseError: "Config file is empty or invalid" };
|
|
306
439
|
}
|
|
307
440
|
return { data: parsed };
|
|
308
441
|
}
|
|
309
442
|
catch (error) {
|
|
310
|
-
return { data: null, parseError: error.message ||
|
|
443
|
+
return { data: null, parseError: error.message || "Failed to parse config" };
|
|
311
444
|
}
|
|
312
445
|
}
|
|
313
446
|
function mergeStrategies(base, override) {
|
|
@@ -319,9 +452,9 @@ function mergeStrategies(base, override) {
|
|
|
319
452
|
protectedTools: [
|
|
320
453
|
...new Set([
|
|
321
454
|
...base.deduplication.protectedTools,
|
|
322
|
-
...(override.deduplication?.protectedTools ?? [])
|
|
323
|
-
])
|
|
324
|
-
]
|
|
455
|
+
...(override.deduplication?.protectedTools ?? []),
|
|
456
|
+
]),
|
|
457
|
+
],
|
|
325
458
|
},
|
|
326
459
|
onIdle: {
|
|
327
460
|
enabled: override.onIdle?.enabled ?? base.onIdle.enabled,
|
|
@@ -331,49 +464,63 @@ function mergeStrategies(base, override) {
|
|
|
331
464
|
protectedTools: [
|
|
332
465
|
...new Set([
|
|
333
466
|
...base.onIdle.protectedTools,
|
|
334
|
-
...(override.onIdle?.protectedTools ?? [])
|
|
335
|
-
])
|
|
336
|
-
]
|
|
467
|
+
...(override.onIdle?.protectedTools ?? []),
|
|
468
|
+
]),
|
|
469
|
+
],
|
|
470
|
+
},
|
|
471
|
+
supersedeWrites: {
|
|
472
|
+
enabled: override.supersedeWrites?.enabled ?? base.supersedeWrites.enabled,
|
|
337
473
|
},
|
|
338
|
-
|
|
339
|
-
|
|
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,
|
|
340
483
|
protectedTools: [
|
|
341
484
|
...new Set([
|
|
342
|
-
...base.
|
|
343
|
-
...(override.
|
|
344
|
-
])
|
|
485
|
+
...base.settings.protectedTools,
|
|
486
|
+
...(override.settings?.protectedTools ?? []),
|
|
487
|
+
]),
|
|
345
488
|
],
|
|
346
|
-
nudge: {
|
|
347
|
-
enabled: override.pruneTool?.nudge?.enabled ?? base.pruneTool.nudge.enabled,
|
|
348
|
-
frequency: override.pruneTool?.nudge?.frequency ?? base.pruneTool.nudge.frequency
|
|
349
|
-
}
|
|
350
489
|
},
|
|
351
|
-
|
|
352
|
-
enabled: override.
|
|
353
|
-
}
|
|
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
|
+
},
|
|
354
497
|
};
|
|
355
498
|
}
|
|
356
499
|
function deepCloneConfig(config) {
|
|
357
500
|
return {
|
|
358
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
|
+
},
|
|
359
511
|
strategies: {
|
|
360
512
|
deduplication: {
|
|
361
513
|
...config.strategies.deduplication,
|
|
362
|
-
protectedTools: [...config.strategies.deduplication.protectedTools]
|
|
514
|
+
protectedTools: [...config.strategies.deduplication.protectedTools],
|
|
363
515
|
},
|
|
364
516
|
onIdle: {
|
|
365
517
|
...config.strategies.onIdle,
|
|
366
|
-
protectedTools: [...config.strategies.onIdle.protectedTools]
|
|
367
|
-
},
|
|
368
|
-
pruneTool: {
|
|
369
|
-
...config.strategies.pruneTool,
|
|
370
|
-
protectedTools: [...config.strategies.pruneTool.protectedTools],
|
|
371
|
-
nudge: { ...config.strategies.pruneTool.nudge }
|
|
518
|
+
protectedTools: [...config.strategies.onIdle.protectedTools],
|
|
372
519
|
},
|
|
373
520
|
supersedeWrites: {
|
|
374
|
-
...config.strategies.supersedeWrites
|
|
375
|
-
}
|
|
376
|
-
}
|
|
521
|
+
...config.strategies.supersedeWrites,
|
|
522
|
+
},
|
|
523
|
+
},
|
|
377
524
|
};
|
|
378
525
|
}
|
|
379
526
|
export function getConfig(ctx) {
|
|
@@ -390,8 +537,8 @@ export function getConfig(ctx) {
|
|
|
390
537
|
title: "DCP: Invalid config",
|
|
391
538
|
message: `${configPaths.global}\n${result.parseError}\nUsing default values`,
|
|
392
539
|
variant: "warning",
|
|
393
|
-
duration: 7000
|
|
394
|
-
}
|
|
540
|
+
duration: 7000,
|
|
541
|
+
},
|
|
395
542
|
});
|
|
396
543
|
}
|
|
397
544
|
catch { }
|
|
@@ -403,8 +550,13 @@ export function getConfig(ctx) {
|
|
|
403
550
|
config = {
|
|
404
551
|
enabled: result.data.enabled ?? config.enabled,
|
|
405
552
|
debug: result.data.debug ?? config.debug,
|
|
406
|
-
|
|
407
|
-
|
|
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),
|
|
408
560
|
};
|
|
409
561
|
}
|
|
410
562
|
}
|
|
@@ -423,8 +575,8 @@ export function getConfig(ctx) {
|
|
|
423
575
|
title: "DCP: Invalid configDir config",
|
|
424
576
|
message: `${configPaths.configDir}\n${result.parseError}\nUsing global/default values`,
|
|
425
577
|
variant: "warning",
|
|
426
|
-
duration: 7000
|
|
427
|
-
}
|
|
578
|
+
duration: 7000,
|
|
579
|
+
},
|
|
428
580
|
});
|
|
429
581
|
}
|
|
430
582
|
catch { }
|
|
@@ -436,8 +588,13 @@ export function getConfig(ctx) {
|
|
|
436
588
|
config = {
|
|
437
589
|
enabled: result.data.enabled ?? config.enabled,
|
|
438
590
|
debug: result.data.debug ?? config.debug,
|
|
439
|
-
|
|
440
|
-
|
|
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),
|
|
441
598
|
};
|
|
442
599
|
}
|
|
443
600
|
}
|
|
@@ -452,8 +609,8 @@ export function getConfig(ctx) {
|
|
|
452
609
|
title: "DCP: Invalid project config",
|
|
453
610
|
message: `${configPaths.project}\n${result.parseError}\nUsing global/default values`,
|
|
454
611
|
variant: "warning",
|
|
455
|
-
duration: 7000
|
|
456
|
-
}
|
|
612
|
+
duration: 7000,
|
|
613
|
+
},
|
|
457
614
|
});
|
|
458
615
|
}
|
|
459
616
|
catch { }
|
|
@@ -465,8 +622,13 @@ export function getConfig(ctx) {
|
|
|
465
622
|
config = {
|
|
466
623
|
enabled: result.data.enabled ?? config.enabled,
|
|
467
624
|
debug: result.data.debug ?? config.debug,
|
|
468
|
-
|
|
469
|
-
|
|
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),
|
|
470
632
|
};
|
|
471
633
|
}
|
|
472
634
|
}
|