diff --git a/README.md b/README.md index e48a5e0..56dd98c 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,6 @@ DCP uses multiple tools and strategies to reduce context size: **Purge Errors** — Prunes tool inputs for tools that returned errors after a configurable number of turns (default: 4). Error messages are preserved for context, but the potentially large input content is removed. Runs automatically on every request with zero LLM cost. -**On Idle Analysis** — Uses a language model to semantically analyze conversation context during idle periods and identify tool outputs that are no longer relevant. Disabled by default (legacy behavior). - Your session history is never modified—DCP replaces pruned content with placeholders before sending requests to your LLM. ## Impact on Prompt Caching @@ -118,18 +116,6 @@ DCP uses its own config file: // Additional tools to protect from pruning "protectedTools": [], }, - // (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, - }, }, } ``` diff --git a/index.ts b/index.ts index 0c2906a..d7a7a14 100644 --- a/index.ts +++ b/index.ts @@ -1,10 +1,10 @@ import type { Plugin } from "@opencode-ai/plugin" import { getConfig } from "./lib/config" import { Logger } from "./lib/logger" -import { loadPrompt } from "./lib/prompt" +import { loadPrompt } from "./lib/prompts" import { createSessionState } from "./lib/state" import { createDiscardTool, createExtractTool } from "./lib/strategies" -import { createChatMessageTransformHandler, createEventHandler } from "./lib/hooks" +import { createChatMessageTransformHandler } from "./lib/hooks" const plugin: Plugin = (async (ctx) => { const config = getConfig(ctx) @@ -91,7 +91,6 @@ const plugin: Plugin = (async (ctx) => { ) } }, - event: createEventHandler(ctx.client, config, state, logger, ctx.directory), } }) satisfies Plugin diff --git a/lib/config.ts b/lib/config.ts index 15fe647..c865eda 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -9,14 +9,6 @@ export interface Deduplication { protectedTools: string[] } -export interface OnIdle { - enabled: boolean - model?: string - showModelErrorToasts?: boolean - strictModelSelection?: boolean - protectedTools: string[] -} - export interface DiscardTool { enabled: boolean } @@ -63,7 +55,6 @@ export interface PluginConfig { deduplication: Deduplication supersedeWrites: SupersedeWrites purgeErrors: PurgeErrors - onIdle: OnIdle } } @@ -102,13 +93,6 @@ export const VALID_CONFIG_KEYS = new Set([ "strategies.purgeErrors.enabled", "strategies.purgeErrors.turns", "strategies.purgeErrors.protectedTools", - // strategies.onIdle - "strategies.onIdle", - "strategies.onIdle.enabled", - "strategies.onIdle.model", - "strategies.onIdle.showModelErrorToasts", - "strategies.onIdle.strictModelSelection", - "strategies.onIdle.protectedTools", ]) // Extract all key paths from a config object for validation @@ -272,60 +256,6 @@ function validateConfigTypes(config: Record): ValidationError[] { }) } - // onIdle - if (strategies.onIdle) { - if ( - strategies.onIdle.enabled !== undefined && - typeof strategies.onIdle.enabled !== "boolean" - ) { - errors.push({ - key: "strategies.onIdle.enabled", - expected: "boolean", - actual: typeof strategies.onIdle.enabled, - }) - } - if ( - strategies.onIdle.model !== undefined && - typeof strategies.onIdle.model !== "string" - ) { - errors.push({ - key: "strategies.onIdle.model", - expected: "string", - actual: typeof strategies.onIdle.model, - }) - } - if ( - strategies.onIdle.showModelErrorToasts !== undefined && - typeof strategies.onIdle.showModelErrorToasts !== "boolean" - ) { - errors.push({ - key: "strategies.onIdle.showModelErrorToasts", - expected: "boolean", - actual: typeof strategies.onIdle.showModelErrorToasts, - }) - } - if ( - strategies.onIdle.strictModelSelection !== undefined && - typeof strategies.onIdle.strictModelSelection !== "boolean" - ) { - errors.push({ - key: "strategies.onIdle.strictModelSelection", - expected: "boolean", - actual: typeof strategies.onIdle.strictModelSelection, - }) - } - if ( - strategies.onIdle.protectedTools !== undefined && - !Array.isArray(strategies.onIdle.protectedTools) - ) { - errors.push({ - key: "strategies.onIdle.protectedTools", - expected: "string[]", - actual: typeof strategies.onIdle.protectedTools, - }) - } - } - // supersedeWrites if (strategies.supersedeWrites) { if ( @@ -459,12 +389,6 @@ const defaultConfig: PluginConfig = { turns: 4, protectedTools: [...DEFAULT_PROTECTED_TOOLS], }, - onIdle: { - enabled: false, - protectedTools: [...DEFAULT_PROTECTED_TOOLS], - showModelErrorToasts: true, - strictModelSelection: false, - }, }, } @@ -587,18 +511,6 @@ function createDefaultConfig(): void { "turns": 4, // Additional tools to protect from pruning "protectedTools": [] - }, - // (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 } } } @@ -660,20 +572,6 @@ function mergeStrategies( ]), ], }, - onIdle: { - enabled: override.onIdle?.enabled ?? base.onIdle.enabled, - model: override.onIdle?.model ?? base.onIdle.model, - showModelErrorToasts: - override.onIdle?.showModelErrorToasts ?? base.onIdle.showModelErrorToasts, - strictModelSelection: - override.onIdle?.strictModelSelection ?? base.onIdle.strictModelSelection, - protectedTools: [ - ...new Set([ - ...base.onIdle.protectedTools, - ...(override.onIdle?.protectedTools ?? []), - ]), - ], - }, } } @@ -728,10 +626,6 @@ function deepCloneConfig(config: PluginConfig): PluginConfig { ...config.strategies.purgeErrors, protectedTools: [...config.strategies.purgeErrors.protectedTools], }, - onIdle: { - ...config.strategies.onIdle, - protectedTools: [...config.strategies.onIdle.protectedTools], - }, }, } } diff --git a/lib/hooks.ts b/lib/hooks.ts index d3ec6d2..e70c892 100644 --- a/lib/hooks.ts +++ b/lib/hooks.ts @@ -5,7 +5,6 @@ import { syncToolCache } from "./state/tool-cache" import { deduplicate, supersedeWrites, purgeErrors } from "./strategies" import { prune, insertPruneToolContext } from "./messages" import { checkSession } from "./state" -import { runOnIdle } from "./strategies/on-idle" export function createChatMessageTransformHandler( client: any, @@ -35,33 +34,3 @@ export function createChatMessageTransformHandler( } } } - -export function createEventHandler( - client: any, - config: PluginConfig, - state: SessionState, - logger: Logger, - workingDirectory?: string, -) { - return async ({ event }: { event: any }) => { - if (state.sessionId === null || state.isSubAgent) { - return - } - - if (event.type === "session.status" && event.properties.status.type === "idle") { - if (!config.strategies.onIdle.enabled) { - return - } - if (state.lastToolPrune) { - logger.info("Skipping OnIdle pruning - last tool was prune") - return - } - - try { - await runOnIdle(client, state, logger, config, workingDirectory) - } catch (err: any) { - logger.error("OnIdle pruning failed", { error: err.message }) - } - } - } -} diff --git a/lib/messages/inject.ts b/lib/messages/inject.ts index 03b75ec..debfd83 100644 --- a/lib/messages/inject.ts +++ b/lib/messages/inject.ts @@ -1,7 +1,7 @@ import type { SessionState, WithParts } from "../state" import type { Logger } from "../logger" import type { PluginConfig } from "../config" -import { loadPrompt } from "../prompt" +import { loadPrompt } from "../prompts" import { extractParameterKey, buildToolIdList, createSyntheticUserMessage } from "./utils" import { getLastUserMessage } from "../shared-utils" diff --git a/lib/prompt.ts b/lib/prompt.ts deleted file mode 100644 index 0f2bad3..0000000 --- a/lib/prompt.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { readFileSync } from "fs" -import { join } from "path" - -export function loadPrompt(name: string, vars?: Record): string { - const filePath = join(__dirname, "prompts", `${name}.txt`) - let content = readFileSync(filePath, "utf8").trim() - if (vars) { - for (const [key, value] of Object.entries(vars)) { - content = content.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value) - } - } - return content -} - -function minimizeMessages( - messages: any[], - alreadyPrunedIds?: string[], - protectedToolCallIds?: string[], -): any[] { - const prunedIdsSet = alreadyPrunedIds - ? new Set(alreadyPrunedIds.map((id) => id.toLowerCase())) - : new Set() - const protectedIdsSet = protectedToolCallIds - ? new Set(protectedToolCallIds.map((id) => id.toLowerCase())) - : new Set() - - return messages - .map((msg) => { - const minimized: any = { - role: msg.info?.role, - } - - if (msg.parts) { - minimized.parts = msg.parts - .filter((part: any) => { - if (part.type === "step-start" || part.type === "step-finish") { - return false - } - return true - }) - .map((part: any) => { - if (part.type === "text") { - if (part.ignored) { - return null - } - return { - type: "text", - text: part.text, - } - } - - // TODO: This should use the opencode normalized system instead of per provider settings - if (part.type === "reasoning") { - // Calculate encrypted content size if present - let encryptedContentLength = 0 - if (part.metadata?.openai?.reasoningEncryptedContent) { - encryptedContentLength = - part.metadata.openai.reasoningEncryptedContent.length - } else if (part.metadata?.anthropic?.signature) { - encryptedContentLength = part.metadata.anthropic.signature.length - } else if (part.metadata?.google?.thoughtSignature) { - encryptedContentLength = - part.metadata.google.thoughtSignature.length - } - - return { - type: "reasoning", - text: part.text, - textLength: part.text?.length || 0, - encryptedContentLength, - ...(part.time && { time: part.time }), - ...(part.metadata && { metadataKeys: Object.keys(part.metadata) }), - } - } - - if (part.type === "tool") { - const callIDLower = part.callID?.toLowerCase() - const isAlreadyPruned = prunedIdsSet.has(callIDLower) - const isProtected = protectedIdsSet.has(callIDLower) - - let displayCallID = part.callID - if (isAlreadyPruned) { - displayCallID = "" - } else if (isProtected) { - displayCallID = "" - } - - const toolPart: any = { - type: "tool", - toolCallID: displayCallID, - tool: part.tool, - } - - if (part.state?.output) { - toolPart.output = part.state.output - } - - if (part.state?.input) { - const input = part.state.input - - if ( - input.filePath && - (part.tool === "write" || - part.tool === "edit" || - part.tool === "multiedit" || - part.tool === "patch") - ) { - toolPart.input = input - } else if (input.filePath) { - toolPart.input = { filePath: input.filePath } - } else if (input.tool_calls && Array.isArray(input.tool_calls)) { - toolPart.input = { - batch_summary: `${input.tool_calls.length} tool calls`, - tools: input.tool_calls.map((tc: any) => tc.tool), - } - } else { - toolPart.input = input - } - } - - return toolPart - } - - return null - }) - .filter(Boolean) - } - - return minimized - }) - .filter((msg) => { - return msg.parts && msg.parts.length > 0 - }) -} - -export function buildAnalysisPrompt( - unprunedToolCallIds: string[], - messages: any[], - alreadyPrunedIds?: string[], - protectedToolCallIds?: string[], -): string { - const minimizedMessages = minimizeMessages(messages, alreadyPrunedIds, protectedToolCallIds) - const messagesJson = JSON.stringify(minimizedMessages, null, 2).replace(/\\n/g, "\n") - - return loadPrompt("on-idle-analysis", { - available_tool_call_ids: unprunedToolCallIds.join(", "), - session_history: messagesJson, - }) -} diff --git a/lib/prompts/index.ts b/lib/prompts/index.ts new file mode 100644 index 0000000..dbec5dd --- /dev/null +++ b/lib/prompts/index.ts @@ -0,0 +1,13 @@ +import { readFileSync } from "fs" +import { join } from "path" + +export function loadPrompt(name: string, vars?: Record): string { + const filePath = join(__dirname, `${name}.txt`) + let content = readFileSync(filePath, "utf8").trim() + if (vars) { + for (const [key, value] of Object.entries(vars)) { + content = content.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), value) + } + } + return content +} diff --git a/lib/prompts/on-idle-analysis.txt b/lib/prompts/on-idle-analysis.txt deleted file mode 100644 index 62045c3..0000000 --- a/lib/prompts/on-idle-analysis.txt +++ /dev/null @@ -1,30 +0,0 @@ -You are a conversation analyzer that identifies obsolete tool outputs in a coding session. - -Your task: Analyze the session history and identify tool call IDs whose outputs are NO LONGER RELEVANT to the current conversation context. - -Guidelines for identifying obsolete tool calls: -1. Exploratory reads that didn't lead to actual edits or meaningful discussion AND were not explicitly requested to be retained -2. Tool outputs from debugging/fixing an error that has now been resolved -3. Failed or incorrect tool attempts that were immediately corrected (e.g., reading a file from the wrong path, then reading from the correct path) - -DO NOT prune: -- Tool calls whose outputs are actively being discussed -- Tool calls that produced errors still being debugged -- Tool calls that are the MOST RECENT activity in the conversation (these may be intended for future use) - -IMPORTANT: Available tool call IDs for analysis: {{available_tool_call_ids}} - -The session history below may contain tool calls with IDs not in the available list above, these cannot be pruned. These are either: -1. Protected tools (marked with toolCallID "") -2. Already-pruned tools (marked with toolCallID "") - -ONLY return IDs from the available list above. - -Session history (each tool call has a "toolCallID" field): -{{session_history}} - -You MUST respond with valid JSON matching this exact schema: -{ - "pruned_tool_call_ids": ["id1", "id2", ...], - "reasoning": "explanation of why these IDs were selected" -} diff --git a/lib/strategies/index.ts b/lib/strategies/index.ts index 1d0659e..5444964 100644 --- a/lib/strategies/index.ts +++ b/lib/strategies/index.ts @@ -1,5 +1,4 @@ export { deduplicate } from "./deduplication" -export { runOnIdle } from "./on-idle" export { createDiscardTool, createExtractTool } from "./tools" export { supersedeWrites } from "./supersede-writes" export { purgeErrors } from "./purge-errors" diff --git a/lib/strategies/on-idle.ts b/lib/strategies/on-idle.ts deleted file mode 100644 index 43f481c..0000000 --- a/lib/strategies/on-idle.ts +++ /dev/null @@ -1,327 +0,0 @@ -import { z } from "zod" -import type { SessionState, WithParts, ToolParameterEntry } from "../state" -import type { Logger } from "../logger" -import type { PluginConfig } from "../config" -import { buildAnalysisPrompt } from "../prompt" -import { selectModel, ModelInfo } from "../model-selector" -import { saveSessionState } from "../state/persistence" -import { sendUnifiedNotification } from "../ui/notification" -import { calculateTokensSaved, getCurrentParams } from "./utils" -import { isMessageCompacted } from "../shared-utils" - -export interface OnIdleResult { - prunedCount: number - tokensSaved: number - prunedIds: string[] -} - -/** - * Parse messages to extract tool information. - */ -function parseMessages( - state: SessionState, - messages: WithParts[], - toolParametersCache: Map, -): { - toolCallIds: string[] - toolMetadata: Map -} { - const toolCallIds: string[] = [] - const toolMetadata = new Map() - - for (const msg of messages) { - if (isMessageCompacted(state, msg)) { - continue - } - if (msg.parts) { - for (const part of msg.parts) { - if (part.type === "tool" && part.callID) { - toolCallIds.push(part.callID) - - const cachedData = toolParametersCache.get(part.callID) - const parameters = cachedData?.parameters ?? part.state?.input ?? {} - - toolMetadata.set(part.callID, { - tool: part.tool, - parameters: parameters, - status: part.state?.status, - error: part.state?.status === "error" ? part.state.error : undefined, - turn: cachedData?.turn ?? 0, - }) - } - } - } - } - - return { toolCallIds, toolMetadata } -} - -/** - * Replace pruned tool outputs in messages for LLM analysis. - */ -function replacePrunedToolOutputs(messages: WithParts[], prunedIds: string[]): WithParts[] { - if (prunedIds.length === 0) return messages - - const prunedIdsSet = new Set(prunedIds) - - return messages.map((msg) => { - if (!msg.parts) return msg - - return { - ...msg, - parts: msg.parts.map((part: any) => { - if ( - part.type === "tool" && - part.callID && - prunedIdsSet.has(part.callID) && - part.state?.output - ) { - return { - ...part, - state: { - ...part.state, - output: "[Output removed to save context - information superseded or no longer needed]", - }, - } - } - return part - }), - } - }) as WithParts[] -} - -/** - * Run LLM analysis to determine which tool calls can be pruned. - */ -async function runLlmAnalysis( - client: any, - state: SessionState, - logger: Logger, - config: PluginConfig, - messages: WithParts[], - unprunedToolCallIds: string[], - alreadyPrunedIds: string[], - toolMetadata: Map, - workingDirectory?: string, -): Promise { - const protectedToolCallIds: string[] = [] - const prunableToolCallIds = unprunedToolCallIds.filter((id) => { - const metadata = toolMetadata.get(id) - if (metadata && config.strategies.onIdle.protectedTools.includes(metadata.tool)) { - protectedToolCallIds.push(id) - return false - } - return true - }) - - if (prunableToolCallIds.length === 0) { - return [] - } - - // Get model info from messages - let validModelInfo: ModelInfo | undefined = undefined - if (messages.length > 0) { - const lastMessage = messages[messages.length - 1] - const model = (lastMessage.info as any)?.model - if (model?.providerID && model?.modelID) { - validModelInfo = { - providerID: model.providerID, - modelID: model.modelID, - } - } - } - - const modelSelection = await selectModel( - validModelInfo, - logger, - config.strategies.onIdle.model, - workingDirectory, - ) - - logger.info( - `OnIdle Model: ${modelSelection.modelInfo.providerID}/${modelSelection.modelInfo.modelID}`, - { - source: modelSelection.source, - }, - ) - - if (modelSelection.failedModel && config.strategies.onIdle.showModelErrorToasts) { - const skipAi = - modelSelection.source === "fallback" && config.strategies.onIdle.strictModelSelection - try { - await client.tui.showToast({ - body: { - title: skipAi ? "DCP: AI analysis skipped" : "DCP: Model fallback", - message: skipAi - ? `${modelSelection.failedModel.providerID}/${modelSelection.failedModel.modelID} failed\nAI analysis skipped (strictModelSelection enabled)` - : `${modelSelection.failedModel.providerID}/${modelSelection.failedModel.modelID} failed\nUsing ${modelSelection.modelInfo.providerID}/${modelSelection.modelInfo.modelID}`, - variant: "info", - duration: 5000, - }, - }) - } catch { - // Ignore toast errors - } - } - - if (modelSelection.source === "fallback" && config.strategies.onIdle.strictModelSelection) { - logger.info("Skipping AI analysis (fallback model, strictModelSelection enabled)") - return [] - } - - const { generateObject } = await import("ai") - - const sanitizedMessages = replacePrunedToolOutputs(messages, alreadyPrunedIds) - - const analysisPrompt = buildAnalysisPrompt( - prunableToolCallIds, - sanitizedMessages, - alreadyPrunedIds, - protectedToolCallIds, - ) - - const result = await generateObject({ - model: modelSelection.model, - schema: z.object({ - pruned_tool_call_ids: z.array(z.string()), - reasoning: z.string(), - }), - prompt: analysisPrompt, - }) - - const rawLlmPrunedIds = result.object.pruned_tool_call_ids - const llmPrunedIds = rawLlmPrunedIds.filter((id) => prunableToolCallIds.includes(id)) - - // Always log LLM output as debug - const reasoning = result.object.reasoning.replace(/\n+/g, " ").replace(/\s+/g, " ").trim() - logger.debug(`OnIdle LLM output`, { - pruned_tool_call_ids: rawLlmPrunedIds, - reasoning: reasoning, - }) - - return llmPrunedIds -} - -/** - * Run the onIdle pruning strategy. - * This is called when the session transitions to idle state. - */ -export async function runOnIdle( - client: any, - state: SessionState, - logger: Logger, - config: PluginConfig, - workingDirectory?: string, -): Promise { - try { - if (!state.sessionId) { - return null - } - - const sessionId = state.sessionId - - // Fetch session info and messages - const [sessionInfoResponse, messagesResponse] = await Promise.all([ - client.session.get({ path: { id: sessionId } }), - client.session.messages({ path: { id: sessionId } }), - ]) - - const sessionInfo = sessionInfoResponse.data - const messages: WithParts[] = messagesResponse.data || messagesResponse - - if (!messages || messages.length < 3) { - return null - } - - const currentParams = getCurrentParams(messages, logger) - const { toolCallIds, toolMetadata } = parseMessages(state, messages, state.toolParameters) - - const alreadyPrunedIds = state.prune.toolIds - const unprunedToolCallIds = toolCallIds.filter((id) => !alreadyPrunedIds.includes(id)) - - if (unprunedToolCallIds.length === 0) { - return null - } - - // Count prunable tools (excluding protected) - const candidateCount = unprunedToolCallIds.filter((id) => { - const metadata = toolMetadata.get(id) - return !metadata || !config.strategies.onIdle.protectedTools.includes(metadata.tool) - }).length - - if (candidateCount === 0) { - return null - } - - // Run LLM analysis - const llmPrunedIds = await runLlmAnalysis( - client, - state, - logger, - config, - messages, - unprunedToolCallIds, - alreadyPrunedIds, - toolMetadata, - workingDirectory, - ) - - const newlyPrunedIds = llmPrunedIds.filter((id) => !alreadyPrunedIds.includes(id)) - - if (newlyPrunedIds.length === 0) { - return null - } - - // Log the tool IDs being pruned with their tool names - for (const id of newlyPrunedIds) { - const metadata = toolMetadata.get(id) - const toolName = metadata?.tool || "unknown" - logger.info(`OnIdle pruning tool: ${toolName}`, { callID: id }) - } - - // Update state - const allPrunedIds = [...new Set([...alreadyPrunedIds, ...newlyPrunedIds])] - state.prune.toolIds = allPrunedIds - - state.stats.pruneTokenCounter += calculateTokensSaved(state, messages, newlyPrunedIds) - - // Build tool metadata map for notification - const prunedToolMetadata = new Map() - for (const id of newlyPrunedIds) { - const metadata = toolMetadata.get(id) - if (metadata) { - prunedToolMetadata.set(id, metadata) - } - } - - // Send notification - await sendUnifiedNotification( - client, - logger, - config, - state, - sessionId, - newlyPrunedIds, - prunedToolMetadata, - undefined, // reason - currentParams, - workingDirectory || "", - ) - - state.stats.totalPruneTokens += state.stats.pruneTokenCounter - state.stats.pruneTokenCounter = 0 - state.nudgeCounter = 0 - state.lastToolPrune = true - - // Persist state - const sessionName = sessionInfo?.title - saveSessionState(state, logger, sessionName).catch((err) => { - logger.error("Failed to persist state", { error: err.message }) - }) - - logger.info(`OnIdle: Pruned ${newlyPrunedIds.length}/${candidateCount} tools`) - } catch (error: any) { - logger.error("OnIdle analysis failed", { error: error.message }) - return null - } -} diff --git a/lib/strategies/tools.ts b/lib/strategies/tools.ts index 220553d..370c5d1 100644 --- a/lib/strategies/tools.ts +++ b/lib/strategies/tools.ts @@ -7,7 +7,7 @@ import { formatPruningResultForTool } from "../ui/utils" import { ensureSessionInitialized } from "../state" import { saveSessionState } from "../state/persistence" import type { Logger } from "../logger" -import { loadPrompt } from "../prompt" +import { loadPrompt } from "../prompts" import { calculateTokensSaved, getCurrentParams } from "./utils" const DISCARD_TOOL_DESCRIPTION = loadPrompt("discard-tool-spec") diff --git a/package.json b/package.json index a6e8ec7..89e0475 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "types": "./dist/index.d.ts", "scripts": { "clean": "rm -rf dist", - "build": "npm run clean && tsc && cp -r lib/prompts dist/lib/prompts", + "build": "npm run clean && tsc && cp -r lib/prompts/*.txt lib/prompts/user dist/lib/prompts/", "postbuild": "rm -rf dist/logs", "prepublishOnly": "npm run build", "dev": "opencode plugin dev",