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
59 changes: 31 additions & 28 deletions .github/workflows/pr-checks.yml
Original file line number Diff line number Diff line change
@@ -1,33 +1,36 @@
name: PR Checks

on:
pull_request:
branches: [master, dev]
pull_request:
branches: [master, dev]

jobs:
validate:
name: Type Check, Build & Audit
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Type check
run: npm run typecheck

- name: Build
run: npm run build

- name: Security audit
run: npm audit --audit-level=high
continue-on-error: false
validate:
name: Type Check, Build & Audit
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"

- name: Install dependencies
run: npm ci

- name: Format check
run: npm run format:check

- name: Type check
run: npm run typecheck

- name: Build
run: npm run build

- name: Security audit
run: npm audit --audit-level=high
continue-on-error: false
10 changes: 10 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"semi": false,
"singleQuote": false,
"tabWidth": 4,
"useTabs": false,
"trailingComma": "all",
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "always"
}
111 changes: 56 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Add to your OpenCode config:
```jsonc
// opencode.jsonc
{
"plugin": ["@tarquinen/opencode-dcp@latest"]
"plugin": ["@tarquinen/opencode-dcp@latest"],
}
```

Expand Down Expand Up @@ -56,63 +56,63 @@ DCP uses its own config file:

```jsonc
{
// Enable or disable the plugin
"enabled": true,
// Enable debug logging to ~/.config/opencode/logs/dcp/
"debug": false,
// Notification display: "off", "minimal", or "detailed"
"pruneNotification": "detailed",
// Protect from pruning for <turns> message turns
"turnProtection": {
"enabled": false,
"turns": 4
},
// LLM-driven context pruning tools
"tools": {
// Shared settings for all prune tools
"settings": {
// Nudge the LLM to use prune tools (every <nudgeFrequency> tool results)
"nudgeEnabled": true,
"nudgeFrequency": 10,
// Additional tools to protect from pruning
"protectedTools": []
// Enable or disable the plugin
"enabled": true,
// Enable debug logging to ~/.config/opencode/logs/dcp/
"debug": false,
// Notification display: "off", "minimal", or "detailed"
"pruneNotification": "detailed",
// Protect from pruning for <turns> message turns
"turnProtection": {
"enabled": false,
"turns": 4,
},
// Removes tool content from context without preservation (for completed tasks or noise)
"discard": {
"enabled": true
// LLM-driven context pruning tools
"tools": {
// Shared settings for all prune tools
"settings": {
// Nudge the LLM to use prune tools (every <nudgeFrequency> tool results)
"nudgeEnabled": true,
"nudgeFrequency": 10,
// Additional tools to protect from pruning
"protectedTools": [],
},
// Removes tool content from context without preservation (for completed tasks or noise)
"discard": {
"enabled": true,
},
// Distills key findings into preserved knowledge before removing raw content
"extract": {
"enabled": true,
// Show distillation content as an ignored message notification
"showDistillation": false,
},
},
// Distills key findings into preserved knowledge before removing raw content
"extract": {
"enabled": true,
// Show distillation content as an ignored message notification
"showDistillation": false
}
},
// Automatic pruning strategies
"strategies": {
// Remove duplicate tool calls (same tool with same arguments)
"deduplication": {
"enabled": true,
// Additional tools to protect from pruning
"protectedTools": []
// Automatic pruning strategies
"strategies": {
// Remove duplicate tool calls (same tool with same arguments)
"deduplication": {
"enabled": true,
// Additional tools to protect from pruning
"protectedTools": [],
},
// Prune write tool inputs when the file has been subsequently read
"supersedeWrites": {
"enabled": true,
},
// (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
"onIdle": {
"enabled": false,
// Additional tools to protect from pruning
"protectedTools": [],
// Override model for analysis (format: "provider/model")
// "model": "anthropic/claude-haiku-4-5",
// Show toast notifications when model selection fails
"showModelErrorToasts": true,
// When true, fallback models are not permitted
"strictModelSelection": false,
},
},
// Prune write tool inputs when the file has been subsequently read
"supersedeWrites": {
"enabled": true
},
// (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
"onIdle": {
"enabled": false,
// Additional tools to protect from pruning
"protectedTools": [],
// Override model for analysis (format: "provider/model")
// "model": "anthropic/claude-haiku-4-5",
// Show toast notifications when model selection fails
"showModelErrorToasts": true,
// When true, fallback models are not permitted
"strictModelSelection": false
}
}
}
```

Expand All @@ -128,6 +128,7 @@ By default, these tools are always protected from pruning across all strategies:
`task`, `todowrite`, `todoread`, `discard`, `extract`, `batch`

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

- `tools.settings.protectedTools` — Protects tools from the `discard` and `extract` tools
- `strategies.deduplication.protectedTools` — Protects tools from deduplication
- `strategies.onIdle.protectedTools` — Protects tools from on-idle analysis
Expand Down
19 changes: 12 additions & 7 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const plugin: Plugin = (async (ctx) => {
}

// Suppress AI SDK warnings
if (typeof globalThis !== 'undefined') {
(globalThis as any).AI_SDK_LOG_WARNINGS = false
if (typeof globalThis !== "undefined") {
;(globalThis as any).AI_SDK_LOG_WARNINGS = false
}

const logger = new Logger(config.debug)
Expand All @@ -26,7 +26,10 @@ const plugin: Plugin = (async (ctx) => {
})

return {
"experimental.chat.system.transform": async (_input: unknown, output: { system: string[] }) => {
"experimental.chat.system.transform": async (
_input: unknown,
output: { system: string[] },
) => {
const discardEnabled = config.tools.discard.enabled
const extractEnabled = config.tools.extract.enabled

Expand All @@ -48,7 +51,7 @@ const plugin: Plugin = (async (ctx) => {
ctx.client,
state,
logger,
config
config,
),
tool: {
...(config.tools.discard.enabled && {
Expand All @@ -57,7 +60,7 @@ const plugin: Plugin = (async (ctx) => {
state,
logger,
config,
workingDirectory: ctx.directory
workingDirectory: ctx.directory,
}),
}),
...(config.tools.extract.enabled && {
Expand All @@ -66,7 +69,7 @@ const plugin: Plugin = (async (ctx) => {
state,
logger,
config,
workingDirectory: ctx.directory
workingDirectory: ctx.directory,
}),
}),
},
Expand All @@ -83,7 +86,9 @@ const plugin: Plugin = (async (ctx) => {
...opencodeConfig.experimental,
primary_tools: [...existingPrimaryTools, ...toolsToAdd],
}
logger.info(`Added ${toolsToAdd.map(t => `'${t}'`).join(" and ")} to experimental.primary_tools via config mutation`)
logger.info(
`Added ${toolsToAdd.map((t) => `'${t}'`).join(" and ")} to experimental.primary_tools via config mutation`,
)
}
},
event: createEventHandler(ctx.client, config, state, logger, ctx.directory),
Expand Down
Loading