Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,5 @@ test-update.ts
# Documentation (local development only)
docs/
SCHEMA_NOTES.md

repomix-output.xml
9 changes: 9 additions & 0 deletions .repomixignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.github/
.logs/
.opencode/
dist/
.repomixignore
repomix-output.xml
bun.lock
package-lock.jsonc
LICENCE
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ DCP uses multiple tools and strategies to reduce context size:

### Tools

**Discard** — Exposes a `discard` tool that the AI can call to remove completed or noisy tool content from context.
**Prune** — Exposes a `prune` tool that the AI can call to remove completed or noisy tool content from context.

**Extract** — Exposes an `extract` tool that the AI can call to distill valuable context into concise summaries before removing the tool content.
**Distill** — Exposes a `distill` tool that the AI can call to distill valuable context into concise summaries before removing the tool content.

**Squash** — Exposes a `squash` tool that the AI can call to collapse a large section of conversation (messages and tools) into a single summary.
**Compress** — Exposes a `compress` tool that the AI can call to collapse a large section of conversation (messages and tools) into a single summary.

### Strategies

Expand Down Expand Up @@ -60,7 +60,7 @@ DCP uses its own config file:

- Global: `~/.config/opencode/dcp.jsonc` (or `dcp.json`), created automatically on first run
- Custom config directory: `$OPENCODE_CONFIG_DIR/dcp.jsonc` (or `dcp.json`), if `OPENCODE_CONFIG_DIR` is set
- Project: `.opencode/dcp.jsonc` (or `dcp.json`) in your projects `.opencode` directory
- Project: `.opencode/dcp.jsonc` (or `dcp.json`) in your project's `.opencode` directory

<details>
<summary><strong>Default Configuration</strong> (click to expand)</summary>
Expand Down Expand Up @@ -99,17 +99,17 @@ DCP uses its own config file:
"protectedTools": [],
},
// Removes tool content from context without preservation (for completed tasks or noise)
"discard": {
"prune": {
"enabled": true,
},
// Distills key findings into preserved knowledge before removing raw content
"extract": {
"distill": {
"enabled": true,
// Show distillation content as an ignored message notification
"showDistillation": false,
},
// Collapses a range of conversation content into a single summary
"squash": {
"compress": {
"enabled": true,
// Show summary content as an ignored message notification
"showSummary": true,
Expand Down Expand Up @@ -152,12 +152,12 @@ DCP provides a `/dcp` slash command:

### Turn Protection

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.
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 `prune` and `distill` tools, as well as automatic strategies.

### Protected Tools

By default, these tools are always protected from pruning across all strategies:
`task`, `todowrite`, `todoread`, `discard`, `extract`, `squash`, `batch`, `write`, `edit`, `plan_enter`, `plan_exit`
`task`, `todowrite`, `todoread`, `prune`, `distill`, `compress`, `batch`, `write`, `edit`, `plan_enter`, `plan_exit`

The `protectedTools` arrays in each section add to this default list.

Expand Down
41 changes: 41 additions & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# DCP CLI

Dev tool for previewing prompt outputs. Verify parsing works correctly and quickly check specific tool combinations.

## Usage

```bash
bun run dcp [TYPE] [-p] [-d] [-c]
```

## Types

| Flag | Description |
| -------------------- | --------------------------- |
| `--system` | System prompt |
| `--nudge` | Nudge prompt |
| `--prune-list` | Example prunable tools list |
| `--compress-context` | Example compress context |

## Tool Flags

| Flag | Description |
| ---------------- | -------------------- |
| `-p, --prune` | Enable prune tool |
| `-d, --distill` | Enable distill tool |
| `-c, --compress` | Enable compress tool |

If no tool flags specified, all are enabled.

## Examples

```bash
bun run dcp --system -p -d -c # System prompt with all tools
bun run dcp --system -p # System prompt with prune only
bun run dcp --nudge -d -c # Nudge with distill and compress
bun run dcp --prune-list # Example prunable tools list
```

## Purpose

This CLI does NOT ship with the plugin. It's purely for DX - iterate on prompt templates and verify the `<tool></tool>` conditional parsing produces the expected output.
117 changes: 117 additions & 0 deletions cli/print.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/usr/bin/env npx tsx

import { renderSystemPrompt, renderNudge, type ToolFlags } from "../lib/prompts/index.js"
import {
wrapPrunableTools,
wrapCompressContext,
wrapCooldownMessage,
} from "../lib/messages/inject.js"

const args = process.argv.slice(2)

const flags: ToolFlags = {
prune: args.includes("-p") || args.includes("--prune"),
distill: args.includes("-d") || args.includes("--distill"),
compress: args.includes("-c") || args.includes("--compress"),
}

// Default to all enabled if none specified
if (!flags.prune && !flags.distill && !flags.compress) {
flags.prune = true
flags.distill = true
flags.compress = true
}

const showSystem = args.includes("--system")
const showNudge = args.includes("--nudge")
const showPruneList = args.includes("--prune-list")
const showCompressContext = args.includes("--compress-context")
const showCooldown = args.includes("--cooldown")
const showHelp = args.includes("--help") || args.includes("-h")

if (
showHelp ||
(!showSystem && !showNudge && !showPruneList && !showCompressContext && !showCooldown)
) {
console.log(`
Usage: bun run dcp [TYPE] [-p] [-d] [-c]

Types:
--system System prompt
--nudge Nudge prompt
--prune-list Example prunable tools list
--compress-context Example compress context
--cooldown Cooldown message after pruning

Tool flags (for --system and --nudge):
-p, --prune Enable prune tool
-d, --distill Enable distill tool
-c, --compress Enable compress tool

If no tool flags specified, all are enabled.

Examples:
bun run dcp --system -p -d -c # System prompt with all tools
bun run dcp --system -p # System prompt with prune only
bun run dcp --nudge -d -c # Nudge with distill and compress
bun run dcp --prune-list # Example prunable tools list
`)
process.exit(0)
}

const header = (title: string) => {
console.log()
console.log("─".repeat(60))
console.log(title)
console.log("─".repeat(60))
}

if (showSystem) {
const enabled = [
flags.prune && "prune",
flags.distill && "distill",
flags.compress && "compress",
]
.filter(Boolean)
.join(", ")
header(`SYSTEM PROMPT (tools: ${enabled})`)
console.log(renderSystemPrompt(flags))
}

if (showNudge) {
const enabled = [
flags.prune && "prune",
flags.distill && "distill",
flags.compress && "compress",
]
.filter(Boolean)
.join(", ")
header(`NUDGE (tools: ${enabled})`)
console.log(renderNudge(flags))
}

if (showPruneList) {
header("PRUNABLE TOOLS LIST (mock example)")
const mockList = `5: read, /path/to/file.ts
8: bash, npm run build
12: glob, src/**/*.ts
15: read, /path/to/another-file.ts`
console.log(wrapPrunableTools(mockList))
}

if (showCompressContext) {
header("COMPRESS CONTEXT (mock example)")
console.log(wrapCompressContext(45))
}

if (showCooldown) {
const enabled = [
flags.prune && "prune",
flags.distill && "distill",
flags.compress && "compress",
]
.filter(Boolean)
.join(", ")
header(`COOLDOWN MESSAGE (tools: ${enabled})`)
console.log(wrapCooldownMessage(flags))
}
18 changes: 9 additions & 9 deletions dcp.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,27 +105,27 @@
}
}
},
"discard": {
"prune": {
"type": "object",
"description": "Configuration for the discard tool",
"description": "Configuration for the prune tool",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"default": true,
"description": "Enable the discard tool"
"description": "Enable the prune tool"
}
}
},
"extract": {
"distill": {
"type": "object",
"description": "Configuration for the extract tool",
"description": "Configuration for the distill tool",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"default": true,
"description": "Enable the extract tool"
"description": "Enable the distill tool"
},
"showDistillation": {
"type": "boolean",
Expand All @@ -134,15 +134,15 @@
}
}
},
"squash": {
"compress": {
"type": "object",
"description": "Configuration for the squash tool",
"description": "Configuration for the compress tool",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"default": true,
"description": "Enable the squash tool"
"description": "Enable the compress tool"
},
"showSummary": {
"type": "boolean",
Expand Down
20 changes: 10 additions & 10 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Plugin } from "@opencode-ai/plugin"
import { getConfig } from "./lib/config"
import { Logger } from "./lib/logger"
import { createSessionState } from "./lib/state"
import { createDiscardTool, createExtractTool, createSquashTool } from "./lib/strategies"
import { createPruneTool, createDistillTool, createCompressTool } from "./lib/strategies"
import {
createChatMessageTransformHandler,
createCommandExecuteHandler,
Expand Down Expand Up @@ -61,26 +61,26 @@ const plugin: Plugin = (async (ctx) => {
ctx.directory,
),
tool: {
...(config.tools.discard.enabled && {
discard: createDiscardTool({
...(config.tools.distill.enabled && {
distill: createDistillTool({
client: ctx.client,
state,
logger,
config,
workingDirectory: ctx.directory,
}),
}),
...(config.tools.extract.enabled && {
extract: createExtractTool({
...(config.tools.compress.enabled && {
compress: createCompressTool({
client: ctx.client,
state,
logger,
config,
workingDirectory: ctx.directory,
}),
}),
...(config.tools.squash.enabled && {
squash: createSquashTool({
...(config.tools.prune.enabled && {
prune: createPruneTool({
client: ctx.client,
state,
logger,
Expand All @@ -99,9 +99,9 @@ const plugin: Plugin = (async (ctx) => {
}

const toolsToAdd: string[] = []
if (config.tools.discard.enabled) toolsToAdd.push("discard")
if (config.tools.extract.enabled) toolsToAdd.push("extract")
if (config.tools.squash.enabled) toolsToAdd.push("squash")
if (config.tools.prune.enabled) toolsToAdd.push("prune")
if (config.tools.distill.enabled) toolsToAdd.push("distill")
if (config.tools.compress.enabled) toolsToAdd.push("compress")

if (toolsToAdd.length > 0) {
const existingPrimaryTools = opencodeConfig.experimental?.primary_tools ?? []
Expand Down
Loading