diff --git a/README.md b/README.md index afaab76..937207c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Automatically reduces token usage in OpenCode by removing obsolete tool outputs from conversation history. -![DCP in action](dcp-demo3.png) +![DCP in action](dcp-demo.png) ## Installation @@ -13,7 +13,7 @@ Add to your OpenCode config: ```jsonc // opencode.jsonc { - "plugin": ["@tarquinen/opencode-dcp"], + "plugin": ["@tarquinen/opencode-dcp@0.4.2"], "experimental": { "primary_tools": ["prune"] } @@ -22,7 +22,9 @@ Add to your OpenCode config: The `experimental.primary_tools` setting ensures the `prune` tool is only available to the primary agent (not subagents). -DCP automatically checks for new versions in the background. You'll see a toast notification when an update is available. To enable automatic background updates, set `"autoUpdate": true` in your DCP config. +When a new version is available, DCP will show a toast notification. Update by changing the version number in your config. + +> **Note:** Using `@latest` (e.g. `@tarquinen/opencode-dcp@latest`) does not reliably force the latest update in Opencode. Please use specific version numbers (e.g. `@0.4.2`). Restart OpenCode. The plugin will automatically start optimizing your sessions. @@ -63,15 +65,14 @@ DCP uses its own config file (`~/.config/opencode/dcp.jsonc` or `.opencode/dcp.j | `model` | (session) | Model for analysis (e.g., `"anthropic/claude-haiku-4-5"`) | | `showModelErrorToasts` | `true` | Show notifications on model fallback | | `showUpdateToasts` | `true` | Show notifications when a new version is available | -| `autoUpdate` | `false` | Automatically download new versions (restart to apply) | | `strictModelSelection` | `false` | Only run AI analysis with session or configured model (disables fallback models) | | `pruning_summary` | `"detailed"` | `"off"`, `"minimal"`, or `"detailed"` | | `nudge_freq` | `10` | How often to remind AI to prune (lower = more frequent) | -| `protectedTools` | `["task", "todowrite", "todoread", "prune", "batch", "edit", "write"]` | Tools that are never pruned | +| `protectedTools` | `["task", "todowrite", "todoread", "prune"]` | Tools that are never pruned | | `strategies.onIdle` | `["ai-analysis"]` | Strategies for automatic pruning | | `strategies.onTool` | `["ai-analysis"]` | Strategies when AI calls `prune` | -**Strategies:** `"ai-analysis"` uses LLM to identify prunable outputs. Empty array disables that trigger. Deduplication always runs automatically. More strategies coming soon. +**Strategies:** `"ai-analysis"` uses LLM to identify prunable outputs. Empty array disables that trigger. Deduplication runs automatically on every request. ```jsonc { @@ -80,7 +81,7 @@ DCP uses its own config file (`~/.config/opencode/dcp.jsonc` or `.opencode/dcp.j "onIdle": ["ai-analysis"], "onTool": ["ai-analysis"] }, - "protectedTools": ["task", "todowrite", "todoread", "prune", "batch", "edit", "write"] + "protectedTools": ["task", "todowrite", "todoread", "prune"] } ``` diff --git a/index.ts b/index.ts index b7713e5..1328d3c 100644 --- a/index.ts +++ b/index.ts @@ -53,10 +53,7 @@ const plugin: Plugin = (async (ctx) => { // Check for updates after a delay setTimeout(() => { - checkForUpdates(ctx.client, logger, { - showToast: config.showUpdateToasts ?? true, - autoUpdate: config.autoUpdate ?? false - }).catch(() => { }) + checkForUpdates(ctx.client, logger, config.showUpdateToasts ?? true).catch(() => { }) }, 5000) // Show migration toast if there were config migrations diff --git a/lib/config.ts b/lib/config.ts index 02084d5..e1c87fb 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -14,7 +14,6 @@ export interface PluginConfig { model?: string showModelErrorToasts?: boolean showUpdateToasts?: boolean - autoUpdate?: boolean strictModelSelection?: boolean pruning_summary: "off" | "minimal" | "detailed" nudge_freq: number @@ -32,10 +31,9 @@ export interface ConfigResult { const defaultConfig: PluginConfig = { enabled: true, debug: false, - protectedTools: ['task', 'todowrite', 'todoread', 'prune', 'batch', 'edit', 'write'], + protectedTools: ['task', 'todowrite', 'todoread', 'prune', 'batch'], showModelErrorToasts: true, showUpdateToasts: true, - autoUpdate: false, strictModelSelection: false, pruning_summary: 'detailed', nudge_freq: 10, @@ -52,7 +50,6 @@ const VALID_CONFIG_KEYS = new Set([ 'model', 'showModelErrorToasts', 'showUpdateToasts', - 'autoUpdate', 'strictModelSelection', 'pruning_summary', 'nudge_freq', @@ -118,8 +115,6 @@ function createDefaultConfig(): void { "showModelErrorToasts": true, // Show toast notifications when a new version is available "showUpdateToasts": true, - // Automatically update to new versions (restart required to apply) - "autoUpdate": false, // Only run AI analysis with session model or configured model (disables fallback models) "strictModelSelection": false, // AI analysis strategies (deduplication runs automatically on every request) @@ -210,7 +205,6 @@ export function getConfig(ctx?: PluginInput): ConfigResult { model: globalConfig.model ?? config.model, showModelErrorToasts: globalConfig.showModelErrorToasts ?? config.showModelErrorToasts, showUpdateToasts: globalConfig.showUpdateToasts ?? config.showUpdateToasts, - autoUpdate: globalConfig.autoUpdate ?? config.autoUpdate, strictModelSelection: globalConfig.strictModelSelection ?? config.strictModelSelection, strategies: mergeStrategies(config.strategies, globalConfig.strategies as any), pruning_summary: globalConfig.pruning_summary ?? config.pruning_summary, @@ -243,7 +237,6 @@ export function getConfig(ctx?: PluginInput): ConfigResult { model: projectConfig.model ?? config.model, showModelErrorToasts: projectConfig.showModelErrorToasts ?? config.showModelErrorToasts, showUpdateToasts: projectConfig.showUpdateToasts ?? config.showUpdateToasts, - autoUpdate: projectConfig.autoUpdate ?? config.autoUpdate, strictModelSelection: projectConfig.strictModelSelection ?? config.strictModelSelection, strategies: mergeStrategies(config.strategies, projectConfig.strategies as any), pruning_summary: projectConfig.pruning_summary ?? config.pruning_summary, diff --git a/lib/version-checker.ts b/lib/version-checker.ts index 7e2bf71..f1d0df0 100644 --- a/lib/version-checker.ts +++ b/lib/version-checker.ts @@ -1,7 +1,6 @@ -import { readFileSync, writeFileSync, existsSync } from 'fs' +import { readFileSync } from 'fs' import { join, dirname } from 'path' import { fileURLToPath } from 'url' -import { homedir } from 'os' export const PACKAGE_NAME = '@tarquinen/opencode-dcp' export const NPM_REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest` @@ -11,20 +10,9 @@ const __dirname = dirname(__filename) export function getLocalVersion(): string { try { - let dir = __dirname - for (let i = 0; i < 5; i++) { - const pkgPath = join(dir, 'package.json') - try { - const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) - if (pkg.name === PACKAGE_NAME) { - return pkg.version - } - } catch { - // Not found at this level, go up - } - dir = join(dir, '..') - } - return '0.0.0' + const pkgPath = join(__dirname, '../../package.json') + const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) + return pkg.version } catch { return '0.0.0' } @@ -62,58 +50,7 @@ export function isOutdated(local: string, remote: string): boolean { return false } -/** - * Updates config files to pin the new version. - * Checks both global and local project configs. - * Handles: "@tarquinen/opencode-dcp", "@tarquinen/opencode-dcp@latest", "@tarquinen/opencode-dcp@1.2.3" - */ -export function updateConfigVersion(newVersion: string, logger?: { info: (component: string, message: string, data?: any) => void }): boolean { - const configs = [ - join(homedir(), '.config', 'opencode', 'opencode.jsonc'), // Global - join(process.cwd(), '.opencode', 'opencode.jsonc') // Local project - ] - - let anyUpdated = false - - for (const configPath of configs) { - try { - if (!existsSync(configPath)) continue - - const content = readFileSync(configPath, 'utf-8') - - // Match @tarquinen/opencode-dcp with optional version suffix (latest, 1.2.3, etc) - // The regex matches: " @tarquinen/opencode-dcp (optional @anything) " - const regex = new RegExp(`"${PACKAGE_NAME}(@[^"]*)?"`,'g') - const newEntry = `"${PACKAGE_NAME}@${newVersion}"` - - if (!regex.test(content)) { - continue - } - - // Reset regex state - regex.lastIndex = 0 - const updatedContent = content.replace(regex, newEntry) - - if (updatedContent !== content) { - writeFileSync(configPath, updatedContent, 'utf-8') - logger?.info("version", "Config updated", { configPath, newVersion }) - anyUpdated = true - } - } catch (err) { - logger?.info("version", "Failed to update config", { configPath, error: (err as Error).message }) - } - } - - return anyUpdated -} - -export async function checkForUpdates( - client: any, - logger?: { info: (component: string, message: string, data?: any) => void }, - options: { showToast?: boolean; autoUpdate?: boolean } = {} -): Promise { - const { showToast = true, autoUpdate = false } = options - +export async function checkForUpdates(client: any, logger?: { info: (component: string, message: string, data?: any) => void }, showToast: boolean = true): Promise { try { const local = getLocalVersion() const npm = await getNpmVersion() @@ -128,43 +65,20 @@ export async function checkForUpdates( return } - logger?.info("version", "Update available", { local, npm, autoUpdate }) + logger?.info("version", "Update available", { local, npm }) - if (autoUpdate) { - // Attempt config update - const updated = updateConfigVersion(npm, logger) + if (!showToast) { + return + } - if (updated && showToast) { - await client.tui.showToast({ - body: { - title: "DCP: Updated!", - message: `v${local} → v${npm}\nRestart OpenCode to apply`, - variant: "success", - duration: 6000 - } - }) - } else if (!updated && showToast) { - // Config update failed or plugin not found in config, show manual instructions - await client.tui.showToast({ - body: { - title: "DCP: Update available", - message: `v${local} → v${npm}\nUpdate opencode.jsonc:\n"${PACKAGE_NAME}@${npm}"`, - variant: "info", - duration: 8000 - } - }) + await client.tui.showToast({ + body: { + title: "DCP: Update available", + message: `v${local} → v${npm}\nUpdate opencode.jsonc: ${PACKAGE_NAME}@${npm}`, + variant: "info", + duration: 6000 } - } else if (showToast) { - await client.tui.showToast({ - body: { - title: "DCP: Update available", - message: `v${local} → v${npm}\nUpdate opencode.jsonc:\n"${PACKAGE_NAME}@${npm}"`, - variant: "info", - duration: 8000 - } - }) - } + }) } catch { - // Silently fail version checks } }