Skip to content

Commit 6ff41a8

Browse files
committed
refactor: extract extractParameterKey to display-utils module
1 parent 38796d3 commit 6ff41a8

File tree

4 files changed

+86
-83
lines changed

4 files changed

+86
-83
lines changed

README.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@ Automatically reduces token usage in OpenCode by removing obsolete tool outputs
66

77
![DCP in action](dcp-demo.png)
88

9-
## Pruning Strategies
10-
11-
DCP implements two complementary strategies:
12-
13-
**Deduplication** — Fast, zero-cost pruning that identifies repeated tool calls (e.g., reading the same file multiple times) and keeps only the most recent output. Runs instantly with no LLM calls.
14-
15-
**AI Analysis** — Uses a language model to semantically analyze conversation context and identify tool outputs that are no longer relevant to the current task. More thorough but incurs LLM cost.
16-
179
## Installation
1810

1911
Add to your OpenCode config:
@@ -29,8 +21,13 @@ When a new version is available, DCP will show a toast notification. Update by c
2921

3022
Restart OpenCode. The plugin will automatically start optimizing your sessions.
3123

32-
> **Note:** Project `plugin` arrays override global completely—include all desired plugins in project config if using both.
24+
## Pruning Strategies
25+
26+
DCP implements two complementary strategies:
3327

28+
**Deduplication** — Fast, zero-cost pruning that identifies repeated tool calls (e.g., reading the same file multiple times) and keeps only the most recent output. Runs instantly with no LLM calls.
29+
30+
**AI Analysis** — Uses a language model to semantically analyze conversation context and identify tool outputs that are no longer relevant to the current task. More thorough but incurs LLM cost.
3431
## How It Works
3532

3633
DCP is **non-destructive**—pruning state is kept in memory only. When requests go to your LLM, DCP replaces pruned outputs with a placeholder; original session data stays intact.

lib/deduplicator.ts

Lines changed: 2 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { extractParameterKey } from "./display-utils"
2+
13
export interface DuplicateDetectionResult {
24
duplicateIds: string[] // IDs to prune (older duplicates)
35
deduplicationDetails: Map<string, {
@@ -85,76 +87,3 @@ function sortObjectKeys(obj: any): any {
8587
}
8688
return sorted
8789
}
88-
89-
export function extractParameterKey(metadata: { tool: string, parameters?: any }): string {
90-
if (!metadata.parameters) return ''
91-
92-
const { tool, parameters } = metadata
93-
94-
if (tool === "read" && parameters.filePath) {
95-
return parameters.filePath
96-
}
97-
if (tool === "write" && parameters.filePath) {
98-
return parameters.filePath
99-
}
100-
if (tool === "edit" && parameters.filePath) {
101-
return parameters.filePath
102-
}
103-
104-
if (tool === "list") {
105-
return parameters.path || '(current directory)'
106-
}
107-
if (tool === "glob") {
108-
if (parameters.pattern) {
109-
const pathInfo = parameters.path ? ` in ${parameters.path}` : ""
110-
return `"${parameters.pattern}"${pathInfo}`
111-
}
112-
return '(unknown pattern)'
113-
}
114-
if (tool === "grep") {
115-
if (parameters.pattern) {
116-
const pathInfo = parameters.path ? ` in ${parameters.path}` : ""
117-
return `"${parameters.pattern}"${pathInfo}`
118-
}
119-
return '(unknown pattern)'
120-
}
121-
122-
if (tool === "bash") {
123-
if (parameters.description) return parameters.description
124-
if (parameters.command) {
125-
return parameters.command.length > 50
126-
? parameters.command.substring(0, 50) + "..."
127-
: parameters.command
128-
}
129-
}
130-
131-
if (tool === "webfetch" && parameters.url) {
132-
return parameters.url
133-
}
134-
if (tool === "websearch" && parameters.query) {
135-
return `"${parameters.query}"`
136-
}
137-
if (tool === "codesearch" && parameters.query) {
138-
return `"${parameters.query}"`
139-
}
140-
141-
if (tool === "todowrite") {
142-
return `${parameters.todos?.length || 0} todos`
143-
}
144-
if (tool === "todoread") {
145-
return "read todo list"
146-
}
147-
148-
if (tool === "task" && parameters.description) {
149-
return parameters.description
150-
}
151-
if (tool === "batch") {
152-
return `${parameters.tool_calls?.length || 0} parallel tools`
153-
}
154-
155-
const paramStr = JSON.stringify(parameters)
156-
if (paramStr === '{}' || paramStr === '[]' || paramStr === 'null') {
157-
return ''
158-
}
159-
return paramStr.substring(0, 50)
160-
}

lib/display-utils.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Extracts a human-readable key from tool metadata for display purposes.
3+
* Used by both deduplication and AI analysis to show what was pruned.
4+
*/
5+
export function extractParameterKey(metadata: { tool: string, parameters?: any }): string {
6+
if (!metadata.parameters) return ''
7+
8+
const { tool, parameters } = metadata
9+
10+
if (tool === "read" && parameters.filePath) {
11+
return parameters.filePath
12+
}
13+
if (tool === "write" && parameters.filePath) {
14+
return parameters.filePath
15+
}
16+
if (tool === "edit" && parameters.filePath) {
17+
return parameters.filePath
18+
}
19+
20+
if (tool === "list") {
21+
return parameters.path || '(current directory)'
22+
}
23+
if (tool === "glob") {
24+
if (parameters.pattern) {
25+
const pathInfo = parameters.path ? ` in ${parameters.path}` : ""
26+
return `"${parameters.pattern}"${pathInfo}`
27+
}
28+
return '(unknown pattern)'
29+
}
30+
if (tool === "grep") {
31+
if (parameters.pattern) {
32+
const pathInfo = parameters.path ? ` in ${parameters.path}` : ""
33+
return `"${parameters.pattern}"${pathInfo}`
34+
}
35+
return '(unknown pattern)'
36+
}
37+
38+
if (tool === "bash") {
39+
if (parameters.description) return parameters.description
40+
if (parameters.command) {
41+
return parameters.command.length > 50
42+
? parameters.command.substring(0, 50) + "..."
43+
: parameters.command
44+
}
45+
}
46+
47+
if (tool === "webfetch" && parameters.url) {
48+
return parameters.url
49+
}
50+
if (tool === "websearch" && parameters.query) {
51+
return `"${parameters.query}"`
52+
}
53+
if (tool === "codesearch" && parameters.query) {
54+
return `"${parameters.query}"`
55+
}
56+
57+
if (tool === "todowrite") {
58+
return `${parameters.todos?.length || 0} todos`
59+
}
60+
if (tool === "todoread") {
61+
return "read todo list"
62+
}
63+
64+
if (tool === "task" && parameters.description) {
65+
return parameters.description
66+
}
67+
if (tool === "batch") {
68+
return `${parameters.tool_calls?.length || 0} parallel tools`
69+
}
70+
71+
const paramStr = JSON.stringify(parameters)
72+
if (paramStr === '{}' || paramStr === '[]' || paramStr === 'null') {
73+
return ''
74+
}
75+
return paramStr.substring(0, 50)
76+
}

lib/janitor.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import type { PruningStrategy } from "./config"
44
import { buildAnalysisPrompt } from "./prompt"
55
import { selectModel, extractModelFromSession } from "./model-selector"
66
import { estimateTokensBatch, formatTokenCount } from "./tokenizer"
7-
import { detectDuplicates, extractParameterKey } from "./deduplicator"
7+
import { detectDuplicates } from "./deduplicator"
8+
import { extractParameterKey } from "./display-utils"
89

910
export interface SessionStats {
1011
totalToolsPruned: number

0 commit comments

Comments
 (0)