@tarquinen/opencode-dcp 1.2.3 → 1.2.5
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 +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/commands/context.d.ts +49 -0
- package/dist/lib/commands/context.d.ts.map +1 -0
- package/dist/lib/commands/context.js +190 -0
- package/dist/lib/commands/context.js.map +1 -0
- package/dist/lib/commands/help.d.ts +15 -0
- package/dist/lib/commands/help.d.ts.map +1 -0
- package/dist/lib/commands/help.js +30 -0
- package/dist/lib/commands/help.js.map +1 -0
- package/dist/lib/commands/stats.d.ts +15 -0
- package/dist/lib/commands/stats.d.ts.map +1 -0
- package/dist/lib/commands/stats.js +44 -0
- package/dist/lib/commands/stats.js.map +1 -0
- package/dist/lib/config.d.ts +1 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +22 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/hooks.d.ts +7 -0
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +47 -0
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +6 -0
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/prompts/system/both.d.ts +1 -1
- package/dist/lib/prompts/system/both.d.ts.map +1 -1
- package/dist/lib/prompts/system/both.js +4 -2
- package/dist/lib/prompts/system/both.js.map +1 -1
- package/dist/lib/prompts/system/discard.d.ts +1 -1
- package/dist/lib/prompts/system/discard.d.ts.map +1 -1
- package/dist/lib/prompts/system/discard.js +4 -2
- package/dist/lib/prompts/system/discard.js.map +1 -1
- package/dist/lib/prompts/system/extract.d.ts +1 -1
- package/dist/lib/prompts/system/extract.d.ts.map +1 -1
- package/dist/lib/prompts/system/extract.js +4 -2
- package/dist/lib/prompts/system/extract.js.map +1 -1
- package/dist/lib/state/persistence.d.ts +6 -0
- package/dist/lib/state/persistence.d.ts.map +1 -1
- package/dist/lib/state/persistence.js +34 -0
- package/dist/lib/state/persistence.js.map +1 -1
- package/dist/lib/state/tool-cache.d.ts.map +1 -1
- package/dist/lib/state/tool-cache.js +3 -1
- package/dist/lib/state/tool-cache.js.map +1 -1
- package/dist/lib/strategies/tools.d.ts.map +1 -1
- package/dist/lib/strategies/tools.js +8 -8
- package/dist/lib/strategies/tools.js.map +1 -1
- package/dist/lib/strategies/utils.d.ts +1 -3
- package/dist/lib/strategies/utils.d.ts.map +1 -1
- package/dist/lib/strategies/utils.js +9 -10
- package/dist/lib/strategies/utils.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Automatically reduces token usage in OpenCode by removing obsolete tool outputs from conversation history.
|
|
6
6
|
|
|
7
|
-

|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
@@ -19,8 +19,6 @@ Add to your OpenCode config:
|
|
|
19
19
|
|
|
20
20
|
Using `@latest` ensures you always get the newest version automatically when OpenCode starts.
|
|
21
21
|
|
|
22
|
-
> **Note:** If you use OAuth plugins (e.g., for Google or other services), place this plugin last in your `plugin` array to avoid interfering with their authentication flows.
|
|
23
|
-
|
|
24
22
|
Restart OpenCode. The plugin will automatically start optimizing your sessions.
|
|
25
23
|
|
|
26
24
|
## How Pruning Works
|
|
@@ -49,6 +47,8 @@ LLM providers like Anthropic and OpenAI cache prompts based on exact prefix matc
|
|
|
49
47
|
|
|
50
48
|
**Trade-off:** You lose some cache read benefits but gain larger token savings from reduced context size and performance improvements through reduced context poisoning. In most cases, the token savings outweigh the cache miss cost—especially in long sessions where context bloat becomes significant.
|
|
51
49
|
|
|
50
|
+
> **Note:** In testing, cache hit rates were approximately 65% with DCP enabled vs 85% without.
|
|
51
|
+
|
|
52
52
|
**Best use case:** Providers that count usage in requests, such as Github Copilot and Google Antigravity have no negative price impact.
|
|
53
53
|
|
|
54
54
|
## Configuration
|
|
@@ -71,6 +71,8 @@ DCP uses its own config file:
|
|
|
71
71
|
"debug": false,
|
|
72
72
|
// Notification display: "off", "minimal", or "detailed"
|
|
73
73
|
"pruneNotification": "detailed",
|
|
74
|
+
// Enable or disable slash commands (/dcp)
|
|
75
|
+
"commands": true,
|
|
74
76
|
// Protect from pruning for <turns> message turns
|
|
75
77
|
"turnProtection": {
|
|
76
78
|
"enabled": false,
|
|
@@ -126,6 +128,14 @@ DCP uses its own config file:
|
|
|
126
128
|
|
|
127
129
|
</details>
|
|
128
130
|
|
|
131
|
+
### Commands
|
|
132
|
+
|
|
133
|
+
DCP provides a `/dcp` slash command:
|
|
134
|
+
|
|
135
|
+
- `/dcp` — Shows available DCP commands
|
|
136
|
+
- `/dcp context` — Shows a breakdown of your current session's token usage by category (system, user, assistant, tools, etc.) and how much has been saved through pruning.
|
|
137
|
+
- `/dcp stats` — Shows cumulative pruning statistics across all sessions.
|
|
138
|
+
|
|
129
139
|
### Turn Protection
|
|
130
140
|
|
|
131
141
|
When enabled, turn protection prevents tool outputs from being pruned for a configurable number of message turns. This gives the AI time to reference recent tool outputs before they become prunable. Applies to both `discard` and `extract` tools, as well as automatic strategies.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAWjD,QAAA,MAAM,MAAM,EAAE,MAoFK,CAAA;AAEnB,eAAe,MAAM,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { getConfig } from "./lib/config";
|
|
|
2
2
|
import { Logger } from "./lib/logger";
|
|
3
3
|
import { createSessionState } from "./lib/state";
|
|
4
4
|
import { createDiscardTool, createExtractTool } from "./lib/strategies";
|
|
5
|
-
import { createChatMessageTransformHandler, createSystemPromptHandler } from "./lib/hooks";
|
|
5
|
+
import { createChatMessageTransformHandler, createCommandExecuteHandler, createSystemPromptHandler, } from "./lib/hooks";
|
|
6
6
|
const plugin = (async (ctx) => {
|
|
7
7
|
const config = getConfig(ctx);
|
|
8
8
|
if (!config.enabled) {
|
|
@@ -43,8 +43,13 @@ const plugin = (async (ctx) => {
|
|
|
43
43
|
}),
|
|
44
44
|
},
|
|
45
45
|
config: async (opencodeConfig) => {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
if (config.commands) {
|
|
47
|
+
opencodeConfig.command ??= {};
|
|
48
|
+
opencodeConfig.command["dcp"] = {
|
|
49
|
+
template: "",
|
|
50
|
+
description: "Show available DCP commands",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
48
53
|
const toolsToAdd = [];
|
|
49
54
|
if (config.tools.discard.enabled)
|
|
50
55
|
toolsToAdd.push("discard");
|
|
@@ -59,6 +64,7 @@ const plugin = (async (ctx) => {
|
|
|
59
64
|
logger.info(`Added ${toolsToAdd.map((t) => `'${t}'`).join(" and ")} to experimental.primary_tools via config mutation`);
|
|
60
65
|
}
|
|
61
66
|
},
|
|
67
|
+
"command.execute.before": createCommandExecuteHandler(ctx.client, state, logger, config),
|
|
62
68
|
};
|
|
63
69
|
});
|
|
64
70
|
export default plugin;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACvE,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCP Context Command
|
|
3
|
+
* Shows a visual breakdown of token usage in the current session.
|
|
4
|
+
*
|
|
5
|
+
* TOKEN CALCULATION STRATEGY
|
|
6
|
+
* ==========================
|
|
7
|
+
* We minimize tokenizer estimation by leveraging API-reported values wherever possible.
|
|
8
|
+
*
|
|
9
|
+
* WHAT WE GET FROM THE API (exact):
|
|
10
|
+
* - tokens.input : Input tokens for each assistant response
|
|
11
|
+
* - tokens.output : Output tokens generated (includes text + tool calls)
|
|
12
|
+
* - tokens.reasoning: Reasoning tokens used
|
|
13
|
+
* - tokens.cache : Cache read/write tokens
|
|
14
|
+
*
|
|
15
|
+
* HOW WE CALCULATE EACH CATEGORY:
|
|
16
|
+
*
|
|
17
|
+
* SYSTEM = firstAssistant.input + cache.read - tokenizer(firstUserMessage)
|
|
18
|
+
* The first response's input contains system + first user message.
|
|
19
|
+
*
|
|
20
|
+
* TOOLS = tokenizer(toolInputs + toolOutputs) - prunedTokens
|
|
21
|
+
* We must tokenize tools anyway for pruning decisions.
|
|
22
|
+
*
|
|
23
|
+
* USER = tokenizer(all user messages)
|
|
24
|
+
* User messages are typically small, so estimation is acceptable.
|
|
25
|
+
*
|
|
26
|
+
* ASSISTANT = total - system - user - tools
|
|
27
|
+
* Calculated as residual. This absorbs:
|
|
28
|
+
* - Assistant text output tokens
|
|
29
|
+
* - Reasoning tokens (if persisted by the model)
|
|
30
|
+
* - Any estimation errors
|
|
31
|
+
*
|
|
32
|
+
* TOTAL = input + output + reasoning + cache.read + cache.write
|
|
33
|
+
* Matches opencode's UI display.
|
|
34
|
+
*
|
|
35
|
+
* WHY ASSISTANT IS THE RESIDUAL:
|
|
36
|
+
* If reasoning tokens persist in context (model-dependent), they semantically
|
|
37
|
+
* belong with "Assistant" since reasoning IS assistant-generated content.
|
|
38
|
+
*/
|
|
39
|
+
import type { Logger } from "../logger";
|
|
40
|
+
import type { SessionState, WithParts } from "../state";
|
|
41
|
+
export interface ContextCommandContext {
|
|
42
|
+
client: any;
|
|
43
|
+
state: SessionState;
|
|
44
|
+
logger: Logger;
|
|
45
|
+
sessionId: string;
|
|
46
|
+
messages: WithParts[];
|
|
47
|
+
}
|
|
48
|
+
export declare function handleContextCommand(ctx: ContextCommandContext): Promise<void>;
|
|
49
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../lib/commands/context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAQvD,MAAM,WAAW,qBAAqB;IAClC,MAAM,EAAE,GAAG,CAAA;IACX,KAAK,EAAE,YAAY,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,SAAS,EAAE,CAAA;CACxB;AAsLD,wBAAsB,oBAAoB,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CASpF"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCP Context Command
|
|
3
|
+
* Shows a visual breakdown of token usage in the current session.
|
|
4
|
+
*
|
|
5
|
+
* TOKEN CALCULATION STRATEGY
|
|
6
|
+
* ==========================
|
|
7
|
+
* We minimize tokenizer estimation by leveraging API-reported values wherever possible.
|
|
8
|
+
*
|
|
9
|
+
* WHAT WE GET FROM THE API (exact):
|
|
10
|
+
* - tokens.input : Input tokens for each assistant response
|
|
11
|
+
* - tokens.output : Output tokens generated (includes text + tool calls)
|
|
12
|
+
* - tokens.reasoning: Reasoning tokens used
|
|
13
|
+
* - tokens.cache : Cache read/write tokens
|
|
14
|
+
*
|
|
15
|
+
* HOW WE CALCULATE EACH CATEGORY:
|
|
16
|
+
*
|
|
17
|
+
* SYSTEM = firstAssistant.input + cache.read - tokenizer(firstUserMessage)
|
|
18
|
+
* The first response's input contains system + first user message.
|
|
19
|
+
*
|
|
20
|
+
* TOOLS = tokenizer(toolInputs + toolOutputs) - prunedTokens
|
|
21
|
+
* We must tokenize tools anyway for pruning decisions.
|
|
22
|
+
*
|
|
23
|
+
* USER = tokenizer(all user messages)
|
|
24
|
+
* User messages are typically small, so estimation is acceptable.
|
|
25
|
+
*
|
|
26
|
+
* ASSISTANT = total - system - user - tools
|
|
27
|
+
* Calculated as residual. This absorbs:
|
|
28
|
+
* - Assistant text output tokens
|
|
29
|
+
* - Reasoning tokens (if persisted by the model)
|
|
30
|
+
* - Any estimation errors
|
|
31
|
+
*
|
|
32
|
+
* TOTAL = input + output + reasoning + cache.read + cache.write
|
|
33
|
+
* Matches opencode's UI display.
|
|
34
|
+
*
|
|
35
|
+
* WHY ASSISTANT IS THE RESIDUAL:
|
|
36
|
+
* If reasoning tokens persist in context (model-dependent), they semantically
|
|
37
|
+
* belong with "Assistant" since reasoning IS assistant-generated content.
|
|
38
|
+
*/
|
|
39
|
+
import { sendIgnoredMessage } from "../ui/notification";
|
|
40
|
+
import { formatTokenCount } from "../ui/utils";
|
|
41
|
+
import { isMessageCompacted } from "../shared-utils";
|
|
42
|
+
import { isIgnoredUserMessage } from "../messages/utils";
|
|
43
|
+
import { countTokens, getCurrentParams } from "../strategies/utils";
|
|
44
|
+
function analyzeTokens(state, messages) {
|
|
45
|
+
const breakdown = {
|
|
46
|
+
system: 0,
|
|
47
|
+
user: 0,
|
|
48
|
+
assistant: 0,
|
|
49
|
+
tools: 0,
|
|
50
|
+
toolCount: 0,
|
|
51
|
+
prunedTokens: state.stats.totalPruneTokens,
|
|
52
|
+
prunedCount: state.prune.toolIds.length,
|
|
53
|
+
total: 0,
|
|
54
|
+
};
|
|
55
|
+
let firstAssistant;
|
|
56
|
+
for (const msg of messages) {
|
|
57
|
+
if (msg.info.role === "assistant") {
|
|
58
|
+
const assistantInfo = msg.info;
|
|
59
|
+
if (assistantInfo.tokens?.input > 0 || assistantInfo.tokens?.cache?.read > 0) {
|
|
60
|
+
firstAssistant = assistantInfo;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
let lastAssistant;
|
|
66
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
67
|
+
const msg = messages[i];
|
|
68
|
+
if (msg.info.role === "assistant") {
|
|
69
|
+
const assistantInfo = msg.info;
|
|
70
|
+
if (assistantInfo.tokens?.output > 0) {
|
|
71
|
+
lastAssistant = assistantInfo;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const apiInput = lastAssistant?.tokens?.input || 0;
|
|
77
|
+
const apiOutput = lastAssistant?.tokens?.output || 0;
|
|
78
|
+
const apiReasoning = lastAssistant?.tokens?.reasoning || 0;
|
|
79
|
+
const apiCacheRead = lastAssistant?.tokens?.cache?.read || 0;
|
|
80
|
+
const apiCacheWrite = lastAssistant?.tokens?.cache?.write || 0;
|
|
81
|
+
breakdown.total = apiInput + apiOutput + apiReasoning + apiCacheRead + apiCacheWrite;
|
|
82
|
+
const userTextParts = [];
|
|
83
|
+
const toolInputParts = [];
|
|
84
|
+
const toolOutputParts = [];
|
|
85
|
+
let firstUserText = "";
|
|
86
|
+
let foundFirstUser = false;
|
|
87
|
+
for (const msg of messages) {
|
|
88
|
+
if (isMessageCompacted(state, msg))
|
|
89
|
+
continue;
|
|
90
|
+
if (msg.info.role === "user" && isIgnoredUserMessage(msg))
|
|
91
|
+
continue;
|
|
92
|
+
for (const part of msg.parts) {
|
|
93
|
+
if (part.type === "text" && msg.info.role === "user") {
|
|
94
|
+
const textPart = part;
|
|
95
|
+
const text = textPart.text || "";
|
|
96
|
+
userTextParts.push(text);
|
|
97
|
+
if (!foundFirstUser) {
|
|
98
|
+
firstUserText += text;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else if (part.type === "tool") {
|
|
102
|
+
const toolPart = part;
|
|
103
|
+
breakdown.toolCount++;
|
|
104
|
+
if (toolPart.state?.input) {
|
|
105
|
+
const inputStr = typeof toolPart.state.input === "string"
|
|
106
|
+
? toolPart.state.input
|
|
107
|
+
: JSON.stringify(toolPart.state.input);
|
|
108
|
+
toolInputParts.push(inputStr);
|
|
109
|
+
}
|
|
110
|
+
if (toolPart.state?.status === "completed" && toolPart.state?.output) {
|
|
111
|
+
const outputStr = typeof toolPart.state.output === "string"
|
|
112
|
+
? toolPart.state.output
|
|
113
|
+
: JSON.stringify(toolPart.state.output);
|
|
114
|
+
toolOutputParts.push(outputStr);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (msg.info.role === "user" && !isIgnoredUserMessage(msg) && !foundFirstUser) {
|
|
119
|
+
foundFirstUser = true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const firstUserTokens = countTokens(firstUserText);
|
|
123
|
+
breakdown.user = countTokens(userTextParts.join("\n"));
|
|
124
|
+
const toolInputTokens = countTokens(toolInputParts.join("\n"));
|
|
125
|
+
const toolOutputTokens = countTokens(toolOutputParts.join("\n"));
|
|
126
|
+
if (firstAssistant) {
|
|
127
|
+
const firstInput = (firstAssistant.tokens?.input || 0) + (firstAssistant.tokens?.cache?.read || 0);
|
|
128
|
+
breakdown.system = Math.max(0, firstInput - firstUserTokens);
|
|
129
|
+
}
|
|
130
|
+
breakdown.tools = Math.max(0, toolInputTokens + toolOutputTokens - breakdown.prunedTokens);
|
|
131
|
+
breakdown.assistant = Math.max(0, breakdown.total - breakdown.system - breakdown.user - breakdown.tools);
|
|
132
|
+
return breakdown;
|
|
133
|
+
}
|
|
134
|
+
function createBar(value, maxValue, width, char = "█") {
|
|
135
|
+
if (maxValue === 0)
|
|
136
|
+
return "";
|
|
137
|
+
const filled = Math.round((value / maxValue) * width);
|
|
138
|
+
const bar = char.repeat(Math.max(0, filled));
|
|
139
|
+
return bar;
|
|
140
|
+
}
|
|
141
|
+
function formatContextMessage(breakdown) {
|
|
142
|
+
const lines = [];
|
|
143
|
+
const barWidth = 30;
|
|
144
|
+
const toolsInContext = breakdown.toolCount - breakdown.prunedCount;
|
|
145
|
+
const toolsLabel = `Tools (${toolsInContext})`;
|
|
146
|
+
const categories = [
|
|
147
|
+
{ label: "System", value: breakdown.system, char: "█" },
|
|
148
|
+
{ label: "User", value: breakdown.user, char: "▓" },
|
|
149
|
+
{ label: "Assistant", value: breakdown.assistant, char: "▒" },
|
|
150
|
+
{ label: toolsLabel, value: breakdown.tools, char: "░" },
|
|
151
|
+
];
|
|
152
|
+
const maxLabelLen = Math.max(...categories.map((c) => c.label.length));
|
|
153
|
+
lines.push("╭───────────────────────────────────────────────────────────╮");
|
|
154
|
+
lines.push("│ DCP Context Analysis │");
|
|
155
|
+
lines.push("╰───────────────────────────────────────────────────────────╯");
|
|
156
|
+
lines.push("");
|
|
157
|
+
lines.push("Session Context Breakdown:");
|
|
158
|
+
lines.push("─".repeat(60));
|
|
159
|
+
lines.push("");
|
|
160
|
+
for (const cat of categories) {
|
|
161
|
+
const bar = createBar(cat.value, breakdown.total, barWidth, cat.char);
|
|
162
|
+
const percentage = breakdown.total > 0 ? ((cat.value / breakdown.total) * 100).toFixed(1) : "0.0";
|
|
163
|
+
const labelWithPct = `${cat.label.padEnd(maxLabelLen)} ${percentage.padStart(5)}% `;
|
|
164
|
+
const valueStr = formatTokenCount(cat.value).padStart(13);
|
|
165
|
+
lines.push(`${labelWithPct}│${bar.padEnd(barWidth)}│${valueStr}`);
|
|
166
|
+
}
|
|
167
|
+
lines.push("");
|
|
168
|
+
lines.push("─".repeat(60));
|
|
169
|
+
lines.push("");
|
|
170
|
+
lines.push("Summary:");
|
|
171
|
+
if (breakdown.prunedTokens > 0) {
|
|
172
|
+
const withoutPruning = breakdown.total + breakdown.prunedTokens;
|
|
173
|
+
lines.push(` Pruned: ${breakdown.prunedCount} tools (~${formatTokenCount(breakdown.prunedTokens)})`);
|
|
174
|
+
lines.push(` Current context: ~${formatTokenCount(breakdown.total)}`);
|
|
175
|
+
lines.push(` Without DCP: ~${formatTokenCount(withoutPruning)}`);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
lines.push(` Current context: ~${formatTokenCount(breakdown.total)}`);
|
|
179
|
+
}
|
|
180
|
+
lines.push("");
|
|
181
|
+
return lines.join("\n");
|
|
182
|
+
}
|
|
183
|
+
export async function handleContextCommand(ctx) {
|
|
184
|
+
const { client, state, logger, sessionId, messages } = ctx;
|
|
185
|
+
const breakdown = analyzeTokens(state, messages);
|
|
186
|
+
const message = formatContextMessage(breakdown);
|
|
187
|
+
const params = getCurrentParams(state, messages, logger);
|
|
188
|
+
await sendIgnoredMessage(client, sessionId, message, params, logger);
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../../lib/commands/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCP Help command handler.
|
|
3
|
+
* Shows available DCP commands and their descriptions.
|
|
4
|
+
*/
|
|
5
|
+
import type { Logger } from "../logger";
|
|
6
|
+
import type { SessionState, WithParts } from "../state";
|
|
7
|
+
export interface HelpCommandContext {
|
|
8
|
+
client: any;
|
|
9
|
+
state: SessionState;
|
|
10
|
+
logger: Logger;
|
|
11
|
+
sessionId: string;
|
|
12
|
+
messages: WithParts[];
|
|
13
|
+
}
|
|
14
|
+
export declare function handleHelpCommand(ctx: HelpCommandContext): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=help.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../../lib/commands/help.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAIvD,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,GAAG,CAAA;IACX,KAAK,EAAE,YAAY,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,SAAS,EAAE,CAAA;CACxB;AAqBD,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAS9E"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCP Help command handler.
|
|
3
|
+
* Shows available DCP commands and their descriptions.
|
|
4
|
+
*/
|
|
5
|
+
import { sendIgnoredMessage } from "../ui/notification";
|
|
6
|
+
import { getCurrentParams } from "../strategies/utils";
|
|
7
|
+
function formatHelpMessage() {
|
|
8
|
+
const lines = [];
|
|
9
|
+
lines.push("╭───────────────────────────────────────────────────────────╮");
|
|
10
|
+
lines.push("│ DCP Commands │");
|
|
11
|
+
lines.push("╰───────────────────────────────────────────────────────────╯");
|
|
12
|
+
lines.push("");
|
|
13
|
+
lines.push("Available commands:");
|
|
14
|
+
lines.push(" context - Show token usage breakdown for current session");
|
|
15
|
+
lines.push(" stats - Show DCP pruning statistics");
|
|
16
|
+
lines.push("");
|
|
17
|
+
lines.push("Examples:");
|
|
18
|
+
lines.push(" /dcp context");
|
|
19
|
+
lines.push(" /dcp stats");
|
|
20
|
+
lines.push("");
|
|
21
|
+
return lines.join("\n");
|
|
22
|
+
}
|
|
23
|
+
export async function handleHelpCommand(ctx) {
|
|
24
|
+
const { client, state, logger, sessionId, messages } = ctx;
|
|
25
|
+
const message = formatHelpMessage();
|
|
26
|
+
const params = getCurrentParams(state, messages, logger);
|
|
27
|
+
await sendIgnoredMessage(client, sessionId, message, params, logger);
|
|
28
|
+
logger.info("Help command executed");
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=help.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../../lib/commands/help.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAUtD,SAAS,iBAAiB;IACtB,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,KAAK,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAA;IAC3E,KAAK,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAA;IAC3E,KAAK,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAA;IAC3E,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACd,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAA;IACjC,KAAK,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAA;IACzE,KAAK,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAA;IACtD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACd,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IACvB,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;IAC5B,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;IAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAEd,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAuB;IAC3D,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAA;IAE1D,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAA;IAEnC,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;IACxD,MAAM,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAEpE,MAAM,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAA;AACxC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCP Stats command handler.
|
|
3
|
+
* Shows pruning statistics for the current session and all-time totals.
|
|
4
|
+
*/
|
|
5
|
+
import type { Logger } from "../logger";
|
|
6
|
+
import type { SessionState, WithParts } from "../state";
|
|
7
|
+
export interface StatsCommandContext {
|
|
8
|
+
client: any;
|
|
9
|
+
state: SessionState;
|
|
10
|
+
logger: Logger;
|
|
11
|
+
sessionId: string;
|
|
12
|
+
messages: WithParts[];
|
|
13
|
+
}
|
|
14
|
+
export declare function handleStatsCommand(ctx: StatsCommandContext): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../lib/commands/stats.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAMvD,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,GAAG,CAAA;IACX,KAAK,EAAE,YAAY,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,SAAS,EAAE,CAAA;CACxB;AA2BD,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DCP Stats command handler.
|
|
3
|
+
* Shows pruning statistics for the current session and all-time totals.
|
|
4
|
+
*/
|
|
5
|
+
import { sendIgnoredMessage } from "../ui/notification";
|
|
6
|
+
import { formatTokenCount } from "../ui/utils";
|
|
7
|
+
import { loadAllSessionStats } from "../state/persistence";
|
|
8
|
+
import { getCurrentParams } from "../strategies/utils";
|
|
9
|
+
function formatStatsMessage(sessionTokens, sessionTools, allTime) {
|
|
10
|
+
const lines = [];
|
|
11
|
+
lines.push("╭───────────────────────────────────────────────────────────╮");
|
|
12
|
+
lines.push("│ DCP Statistics │");
|
|
13
|
+
lines.push("╰───────────────────────────────────────────────────────────╯");
|
|
14
|
+
lines.push("");
|
|
15
|
+
lines.push("Session:");
|
|
16
|
+
lines.push("─".repeat(60));
|
|
17
|
+
lines.push(` Tokens pruned: ~${formatTokenCount(sessionTokens)}`);
|
|
18
|
+
lines.push(` Tools pruned: ${sessionTools}`);
|
|
19
|
+
lines.push("");
|
|
20
|
+
lines.push("All-time:");
|
|
21
|
+
lines.push("─".repeat(60));
|
|
22
|
+
lines.push(` Tokens saved: ~${formatTokenCount(allTime.totalTokens)}`);
|
|
23
|
+
lines.push(` Tools pruned: ${allTime.totalTools}`);
|
|
24
|
+
lines.push(` Sessions: ${allTime.sessionCount}`);
|
|
25
|
+
return lines.join("\n");
|
|
26
|
+
}
|
|
27
|
+
export async function handleStatsCommand(ctx) {
|
|
28
|
+
const { client, state, logger, sessionId, messages } = ctx;
|
|
29
|
+
// Session stats from in-memory state
|
|
30
|
+
const sessionTokens = state.stats.totalPruneTokens;
|
|
31
|
+
const sessionTools = state.prune.toolIds.length;
|
|
32
|
+
// All-time stats from storage files
|
|
33
|
+
const allTime = await loadAllSessionStats(logger);
|
|
34
|
+
const message = formatStatsMessage(sessionTokens, sessionTools, allTime);
|
|
35
|
+
const params = getCurrentParams(state, messages, logger);
|
|
36
|
+
await sendIgnoredMessage(client, sessionId, message, params, logger);
|
|
37
|
+
logger.info("Stats command executed", {
|
|
38
|
+
sessionTokens,
|
|
39
|
+
sessionTools,
|
|
40
|
+
allTimeTokens: allTime.totalTokens,
|
|
41
|
+
allTimeTools: allTime.totalTools,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../lib/commands/stats.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,mBAAmB,EAAwB,MAAM,sBAAsB,CAAA;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAUtD,SAAS,kBAAkB,CACvB,aAAqB,EACrB,YAAoB,EACpB,OAAwB;IAExB,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,KAAK,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAA;IAC3E,KAAK,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAA;IAC3E,KAAK,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAA;IAC3E,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACd,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACtB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;IAC1B,KAAK,CAAC,IAAI,CAAC,qBAAqB,gBAAgB,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;IAClE,KAAK,CAAC,IAAI,CAAC,qBAAqB,YAAY,EAAE,CAAC,CAAA;IAC/C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACd,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IACvB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAA;IAC1B,KAAK,CAAC,IAAI,CAAC,qBAAqB,gBAAgB,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;IACxE,KAAK,CAAC,IAAI,CAAC,qBAAqB,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;IACrD,KAAK,CAAC,IAAI,CAAC,qBAAqB,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;IAEvD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC3B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAwB;IAC7D,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,GAAG,CAAA;IAE1D,qCAAqC;IACrC,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,CAAC,gBAAgB,CAAA;IAClD,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAA;IAE/C,oCAAoC;IACpC,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAAA;IAEjD,MAAM,OAAO,GAAG,kBAAkB,CAAC,aAAa,EAAE,YAAY,EAAE,OAAO,CAAC,CAAA;IAExE,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;IACxD,MAAM,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAEpE,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;QAClC,aAAa;QACb,YAAY;QACZ,aAAa,EAAE,OAAO,CAAC,WAAW;QAClC,YAAY,EAAE,OAAO,CAAC,UAAU;KACnC,CAAC,CAAA;AACN,CAAC"}
|
package/dist/lib/config.d.ts
CHANGED
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../lib/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEtD,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,cAAc,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,gBAAgB,EAAE,OAAO,CAAA;CAC5B;AAED,MAAM,WAAW,YAAY;IACzB,YAAY,EAAE,OAAO,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,KAAK;IAClB,QAAQ,EAAE,YAAY,CAAA;IACtB,OAAO,EAAE,WAAW,CAAA;IACpB,OAAO,EAAE,WAAW,CAAA;CACvB;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,iBAAiB,EAAE,KAAK,GAAG,SAAS,GAAG,UAAU,CAAA;IACjD,cAAc,EAAE,cAAc,CAAA;IAC9B,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,KAAK,EAAE,KAAK,CAAA;IACZ,UAAU,EAAE;QACR,aAAa,EAAE,aAAa,CAAA;QAC5B,eAAe,EAAE,eAAe,CAAA;QAChC,WAAW,EAAE,WAAW,CAAA;KAC3B,CAAA;CACJ;AAgBD,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../lib/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAEtD,MAAM,WAAW,aAAa;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,cAAc,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,gBAAgB,EAAE,OAAO,CAAA;CAC5B;AAED,MAAM,WAAW,YAAY;IACzB,YAAY,EAAE,OAAO,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,KAAK;IAClB,QAAQ,EAAE,YAAY,CAAA;IACtB,OAAO,EAAE,WAAW,CAAA;IACpB,OAAO,EAAE,WAAW,CAAA;CACvB;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,cAAc,EAAE,MAAM,EAAE,CAAA;CAC3B;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,YAAY;IACzB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,iBAAiB,EAAE,KAAK,GAAG,SAAS,GAAG,UAAU,CAAA;IACjD,QAAQ,EAAE,OAAO,CAAA;IACjB,cAAc,EAAE,cAAc,CAAA;IAC9B,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,KAAK,EAAE,KAAK,CAAA;IACZ,UAAU,EAAE;QACR,aAAa,EAAE,aAAa,CAAA;QAC5B,eAAe,EAAE,eAAe,CAAA;QAChC,WAAW,EAAE,WAAW,CAAA;KAC3B,CAAA;CACJ;AAgBD,eAAO,MAAM,iBAAiB,aAmC5B,CAAA;AAgBF,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE,CAG9E;AAkjBD,wBAAgB,SAAS,CAAC,GAAG,EAAE,WAAW,GAAG,YAAY,CAgIxD"}
|
package/dist/lib/config.js
CHANGED
|
@@ -26,6 +26,7 @@ export const VALID_CONFIG_KEYS = new Set([
|
|
|
26
26
|
"turnProtection.enabled",
|
|
27
27
|
"turnProtection.turns",
|
|
28
28
|
"protectedFilePatterns",
|
|
29
|
+
"commands",
|
|
29
30
|
"tools",
|
|
30
31
|
"tools.settings",
|
|
31
32
|
"tools.settings.nudgeEnabled",
|
|
@@ -121,6 +122,15 @@ function validateConfigTypes(config) {
|
|
|
121
122
|
});
|
|
122
123
|
}
|
|
123
124
|
}
|
|
125
|
+
// Commands validator
|
|
126
|
+
const commands = config.commands;
|
|
127
|
+
if (commands !== undefined && typeof commands !== "boolean") {
|
|
128
|
+
errors.push({
|
|
129
|
+
key: "commands",
|
|
130
|
+
expected: "boolean",
|
|
131
|
+
actual: typeof commands,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
124
134
|
// Tools validators
|
|
125
135
|
const tools = config.tools;
|
|
126
136
|
if (tools) {
|
|
@@ -278,6 +288,7 @@ const defaultConfig = {
|
|
|
278
288
|
enabled: true,
|
|
279
289
|
debug: false,
|
|
280
290
|
pruneNotification: "detailed",
|
|
291
|
+
commands: true,
|
|
281
292
|
turnProtection: {
|
|
282
293
|
enabled: false,
|
|
283
294
|
turns: 4,
|
|
@@ -380,6 +391,8 @@ function createDefaultConfig() {
|
|
|
380
391
|
"debug": false,
|
|
381
392
|
// Notification display: "off", "minimal", or "detailed"
|
|
382
393
|
"pruneNotification": "detailed",
|
|
394
|
+
// Enable or disable slash commands (/dcp)
|
|
395
|
+
"commands": true,
|
|
383
396
|
// Protect from pruning for <turns> message turns
|
|
384
397
|
"turnProtection": {
|
|
385
398
|
"enabled": false,
|
|
@@ -505,9 +518,15 @@ function mergeTools(base, override) {
|
|
|
505
518
|
},
|
|
506
519
|
};
|
|
507
520
|
}
|
|
521
|
+
function mergeCommands(base, override) {
|
|
522
|
+
if (override === undefined)
|
|
523
|
+
return base;
|
|
524
|
+
return override;
|
|
525
|
+
}
|
|
508
526
|
function deepCloneConfig(config) {
|
|
509
527
|
return {
|
|
510
528
|
...config,
|
|
529
|
+
commands: config.commands,
|
|
511
530
|
turnProtection: { ...config.turnProtection },
|
|
512
531
|
protectedFilePatterns: [...config.protectedFilePatterns],
|
|
513
532
|
tools: {
|
|
@@ -561,6 +580,7 @@ export function getConfig(ctx) {
|
|
|
561
580
|
enabled: result.data.enabled ?? config.enabled,
|
|
562
581
|
debug: result.data.debug ?? config.debug,
|
|
563
582
|
pruneNotification: result.data.pruneNotification ?? config.pruneNotification,
|
|
583
|
+
commands: mergeCommands(config.commands, result.data.commands),
|
|
564
584
|
turnProtection: {
|
|
565
585
|
enabled: result.data.turnProtection?.enabled ?? config.turnProtection.enabled,
|
|
566
586
|
turns: result.data.turnProtection?.turns ?? config.turnProtection.turns,
|
|
@@ -605,6 +625,7 @@ export function getConfig(ctx) {
|
|
|
605
625
|
enabled: result.data.enabled ?? config.enabled,
|
|
606
626
|
debug: result.data.debug ?? config.debug,
|
|
607
627
|
pruneNotification: result.data.pruneNotification ?? config.pruneNotification,
|
|
628
|
+
commands: mergeCommands(config.commands, result.data.commands),
|
|
608
629
|
turnProtection: {
|
|
609
630
|
enabled: result.data.turnProtection?.enabled ?? config.turnProtection.enabled,
|
|
610
631
|
turns: result.data.turnProtection?.turns ?? config.turnProtection.turns,
|
|
@@ -645,6 +666,7 @@ export function getConfig(ctx) {
|
|
|
645
666
|
enabled: result.data.enabled ?? config.enabled,
|
|
646
667
|
debug: result.data.debug ?? config.debug,
|
|
647
668
|
pruneNotification: result.data.pruneNotification ?? config.pruneNotification,
|
|
669
|
+
commands: mergeCommands(config.commands, result.data.commands),
|
|
648
670
|
turnProtection: {
|
|
649
671
|
enabled: result.data.turnProtection?.enabled ?? config.turnProtection.enabled,
|
|
650
672
|
turns: result.data.turnProtection?.turns ?? config.turnProtection.turns,
|