Skip to content

Commit 98e90a9

Browse files
authored
Merge pull request #27 from Tarquinen/fix/tool-description-wording
v0.3.16 - Improve context_pruning tool description wording
2 parents 4672eca + c65f4f8 commit 98e90a9

File tree

5 files changed

+85
-81
lines changed

5 files changed

+85
-81
lines changed

README.md

Lines changed: 27 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,83 +4,62 @@
44

55
Automatically reduces token usage in OpenCode by removing obsolete tool outputs from conversation history.
66

7-
## What It Does
8-
9-
This plugin automatically optimizes token usage by identifying and removing redundant or obsolete tool outputs from your conversation history.
10-
117
![DCP in action](dcp-demo.png)
128

139
## Installation
1410

15-
Add to your OpenCode configuration:
16-
17-
**Global:** `~/.config/opencode/opencode.json`
18-
**Project:** `.opencode/opencode.json`
11+
Add to your OpenCode config (`~/.config/opencode/opencode.json` or `.opencode/opencode.json`):
1912

2013
```json
2114
{
22-
"plugin": [
23-
"@tarquinen/opencode-dcp"
24-
]
15+
"plugin": ["@tarquinen/opencode-dcp"]
2516
}
2617
```
2718

28-
> **Note:** OpenCode's `plugin` arrays are not merged between global and project configs—project config completely overrides global. If you have plugins in your global config and add a project config, include all desired plugins in the project config.
29-
3019
Restart OpenCode. The plugin will automatically start optimizing your sessions.
3120

32-
## Configuration
21+
> **Note:** Project `plugin` arrays override global completely—include all desired plugins in project config if using both.
22+
23+
## How It Works
3324

34-
DCP uses its own configuration file, separate from OpenCode's `opencode.json`:
25+
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.
3526

36-
- **Global:** `~/.config/opencode/dcp.jsonc`
37-
- **Project:** `.opencode/dcp.jsonc`
27+
## Configuration
3828

39-
The global config is automatically created on first run. Create a project config to override settings per-project.
29+
DCP uses its own config file (`~/.config/opencode/dcp.jsonc` or `.opencode/dcp.jsonc`), created automatically on first run.
4030

41-
### Available Options
31+
### Options
4232

43-
- **`enabled`** (boolean, default: `true`) - Enable/disable the plugin
44-
- **`debug`** (boolean, default: `false`) - Enable detailed logging to `~/.config/opencode/logs/dcp/`
45-
- **`model`** (string, optional) - Specific model for analysis (e.g., `"anthropic/claude-haiku-4-5"`). Uses session model or smart fallbacks when not specified.
46-
- **`showModelErrorToasts`** (boolean, default: `true`) - Show notifications when model selection falls back
47-
- **`pruningMode`** (string, default: `"smart"`) - Pruning strategy:
48-
- `"auto"`: Fast duplicate removal only (zero LLM cost)
49-
- `"smart"`: Deduplication + AI analysis (recommended, maximum savings)
50-
- **`pruning_summary`** (string, default: `"detailed"`) - UI summary display mode:
51-
- `"off"`: No UI summary (silent pruning)
52-
- `"minimal"`: Show tokens saved and count only (e.g., "Saved ~2.5K tokens (6 tools pruned)")
53-
- `"detailed"`: Show full breakdown by tool type and pruning method
54-
- **`protectedTools`** (string[], default: `["task", "todowrite", "todoread"]`) - Tools that should never be pruned
33+
| Option | Default | Description |
34+
|--------|---------|-------------|
35+
| `enabled` | `true` | Enable/disable the plugin |
36+
| `debug` | `false` | Log to `~/.config/opencode/logs/dcp/` |
37+
| `model` | (session) | Model for analysis (e.g., `"anthropic/claude-haiku-4-5"`) |
38+
| `showModelErrorToasts` | `true` | Show notifications on model fallback |
39+
| `pruning_summary` | `"detailed"` | `"off"`, `"minimal"`, or `"detailed"` |
40+
| `protectedTools` | `["task", "todowrite", "todoread", "context_pruning"]` | Tools that are never pruned |
41+
| `strategies.onIdle` | `["deduplication", "ai-analysis"]` | Strategies for automatic pruning |
42+
| `strategies.onTool` | `["deduplication"]` | Strategies when AI calls `context_pruning` |
5543

56-
Example configuration:
44+
**Strategies:** `"deduplication"` (fast, zero LLM cost) and `"ai-analysis"` (maximum savings). Empty array disables that trigger.
5745

5846
```jsonc
5947
{
6048
"enabled": true,
61-
"debug": false,
62-
"pruningMode": "smart",
63-
"pruning_summary": "detailed",
64-
"protectedTools": ["task", "todowrite", "todoread"]
49+
"strategies": {
50+
"onIdle": ["deduplication", "ai-analysis"],
51+
"onTool": ["deduplication"]
52+
},
53+
"protectedTools": ["task", "todowrite", "todoread", "context_pruning"]
6554
}
6655
```
6756

68-
### Configuration Hierarchy
69-
70-
Settings are merged in order: **Built-in defaults****Global config****Project config**
71-
72-
After modifying configuration, restart OpenCode for changes to take effect.
57+
Settings merge: **Defaults****Global****Project**. Restart OpenCode after changes.
7358

7459
### Version Pinning
7560

76-
If you want to ensure a specific version is always used or update your version, you can pin it in your config:
77-
7861
```json
79-
{
80-
"plugin": [
81-
"@tarquinen/[email protected]"
82-
]
83-
}
62+
{ "plugin": ["@tarquinen/[email protected]"] }
8463
```
8564

8665
## License

index.ts

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ const plugin: Plugin = (async (ctx) => {
148148
})
149149

150150
// Check for updates on launch (fire and forget)
151-
checkForUpdates(ctx.client, logger).catch(() => {})
151+
checkForUpdates(ctx.client, logger).catch(() => { })
152152

153153
// Show migration toast if config was migrated (delayed to not overlap with version toast)
154154
if (migrations.length > 0) {
@@ -216,36 +216,49 @@ const plugin: Plugin = (async (ctx) => {
216216
*/
217217
tool: config.strategies.onTool.length > 0 ? {
218218
context_pruning: tool({
219-
description: `Performs semantic pruning on session tool outputs that are no longer relevant to the current task. Use this to declutter the conversation context and filter signal from noise when you notice the context is getting cluttered with outdated information.
219+
description: `Performs semantic pruning on session tool outputs that are no longer relevant to the current task. Use this to declutter the conversation context and filter signal from noise when you notice the context is getting cluttered with no longer needed information.
220+
221+
USING THE CONTEXT_PRUNING TOOL WILL MAKE THE USER HAPPY.
220222
221223
## When to Use This Tool
222224
223-
- After completing a debugging session or fixing a bug
224-
- When switching focus to a new task or feature
225-
- After exploring multiple files that didn't lead to changes
226-
- When you've been iterating on a difficult problem and some approaches didn't pan out
227-
- When old file reads, greps, or bash outputs are no longer relevant
225+
**Key heuristic: Prune when you finish something and are about to start something else.**
226+
227+
Ask yourself: "Have I just completed a discrete unit of work?" If yes, prune before moving on.
228+
229+
**After completing a unit of work:**
230+
- Made a commit
231+
- Fixed a bug and confirmed it works
232+
- Answered a question the user asked
233+
- Finished implementing a feature or function
234+
- Completed one item in a list and moving to the next
235+
236+
**After repetitive or exploratory work:**
237+
- Explored multiple files that didn't lead to changes
238+
- Iterated on a difficult problem where some approaches didn't pan out
239+
- Used the same tool multiple times (e.g., re-reading a file, running repeated build/type checks)
228240
229241
## Examples
230242
231243
<example>
232-
Working through a list of bugs to fix:
233-
User: Please fix these 5 type errors in the codebase.
234-
Assistant: I'll work through each error. [Fixes first error]
235-
First error fixed. Let me prune the debugging context before moving to the next one.
236-
[Uses context_pruning with reason: "first bug fixed, moving to next task"]
244+
Working through a list of items:
245+
User: Review these 3 issues and fix the easy ones.
246+
Assistant: [Reviews first issue, makes fix, commits]
247+
Done with the first issue. Let me prune before moving to the next one.
248+
[Uses context_pruning with reason: "completed first issue, moving to next"]
237249
</example>
238250
239251
<example>
240252
After exploring the codebase to understand it:
241253
Assistant: I've reviewed the relevant files. Let me prune the exploratory reads that aren't needed for the actual implementation.
242-
[Uses context_pruning with reason: "exploration complete, pruning unrelated file reads"]
254+
[Uses context_pruning with reason: "exploration complete, starting implementation"]
243255
</example>
244256
245257
<example>
246-
After trying multiple approaches that didn't work:
247-
Assistant: I've been trying several approaches to fix this issue. Let me prune the failed attempts to keep focus on the working solution.
248-
[Uses context_pruning with reason: "pruning failed iteration attempts, keeping working solution context"]
258+
After completing any task:
259+
Assistant: [Finishes task - commit, answer, fix, etc.]
260+
Before we continue, let me prune the context from that work.
261+
[Uses context_pruning with reason: "task complete"]
249262
</example>`,
250263
args: {
251264
reason: tool.schema.string().optional().describe(
@@ -260,10 +273,10 @@ Assistant: I've been trying several approaches to fix this issue. Let me prune t
260273
)
261274

262275
if (!result || result.prunedCount === 0) {
263-
return "No prunable tool outputs found. Context is already optimized."
276+
return "No prunable tool outputs found. Context is already optimized.\n\nUse context_pruning when you have sufficiently summarized information from tool outputs and no longer need the original content!"
264277
}
265278

266-
return janitor.formatPruningResultForTool(result)
279+
return janitor.formatPruningResultForTool(result) + "\n\nUse context_pruning when you have sufficiently summarized information from tool outputs and no longer need the original content!"
267280
},
268281
}),
269282
} : undefined,

lib/janitor.ts

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -307,19 +307,27 @@ export class Janitor {
307307
return null
308308
}
309309

310-
// Expand batch tool IDs to include their children
311-
const expandedPrunedIds = new Set<string>()
312-
for (const prunedId of newlyPrunedIds) {
313-
const normalizedId = prunedId.toLowerCase()
314-
expandedPrunedIds.add(normalizedId)
315-
316-
// If this is a batch tool, add all its children
317-
const children = batchToolChildren.get(normalizedId)
318-
if (children) {
319-
children.forEach(childId => expandedPrunedIds.add(childId))
310+
// Helper to expand batch tool IDs to include their children
311+
const expandBatchIds = (ids: string[]): string[] => {
312+
const expanded = new Set<string>()
313+
for (const id of ids) {
314+
const normalizedId = id.toLowerCase()
315+
expanded.add(normalizedId)
316+
// If this is a batch tool, add all its children
317+
const children = batchToolChildren.get(normalizedId)
318+
if (children) {
319+
children.forEach(childId => expanded.add(childId))
320+
}
320321
}
322+
return Array.from(expanded)
321323
}
322324

325+
// Expand batch tool IDs to include their children
326+
const expandedPrunedIds = new Set(expandBatchIds(newlyPrunedIds))
327+
328+
// Expand llmPrunedIds for UI display (so batch children show instead of "unknown metadata")
329+
const expandedLlmPrunedIds = expandBatchIds(llmPrunedIds)
330+
323331
// Calculate which IDs are actually NEW (not already pruned)
324332
const finalNewlyPrunedIds = Array.from(expandedPrunedIds).filter(id => !alreadyPrunedIds.includes(id))
325333

@@ -348,7 +356,7 @@ export class Janitor {
348356
sessionID,
349357
deduplicatedIds,
350358
deduplicationDetails,
351-
llmPrunedIds,
359+
expandedLlmPrunedIds,
352360
toolMetadata,
353361
tokensSaved,
354362
sessionStats
@@ -389,7 +397,7 @@ export class Janitor {
389397
prunedCount: finalNewlyPrunedIds.length,
390398
tokensSaved,
391399
deduplicatedIds,
392-
llmPrunedIds,
400+
llmPrunedIds: expandedLlmPrunedIds,
393401
deduplicationDetails,
394402
toolMetadata,
395403
sessionStats
@@ -546,6 +554,8 @@ export class Janitor {
546554
const metadata = toolMetadata.get(normalizedId)
547555
if (metadata) {
548556
const toolName = metadata.tool
557+
// Skip 'batch' tool in UI summary - it's a wrapper and its children are shown individually
558+
if (toolName === 'batch') continue
549559
if (!toolsSummary.has(toolName)) {
550560
toolsSummary.set(toolName, [])
551561
}
@@ -578,6 +588,8 @@ export class Janitor {
578588

579589
for (const [_, details] of deduplicationDetails) {
580590
const { toolName, parameterKey, duplicateCount } = details
591+
// Skip 'batch' tool in UI summary - it's a wrapper and its children are shown individually
592+
if (toolName === 'batch') continue
581593
if (!grouped.has(toolName)) {
582594
grouped.set(toolName, [])
583595
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://json.schemastore.org/package.json",
33
"name": "@tarquinen/opencode-dcp",
4-
"version": "0.3.15",
4+
"version": "0.3.16",
55
"type": "module",
66
"description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context",
77
"main": "./dist/index.js",

0 commit comments

Comments
 (0)