import { tool } from "@opencode-ai/plugin" import { readFileSync, existsSync } from "fs" import { join } from "path" import { resolveWorktree, debugLog } from "../state-io.ts" function stripComments(src: string): string { let out = src.replace(/([^:])\/\/[^\n]*/g, "$1") out = out.replace(/\/\*[\s\S]*?\*\//g, "") out = out.replace(/\n{3,}/g, "\n\n") return out.trim() } function extractSignatures(src: string): string { const patterns = [ /^export\s+(default\s+)?(async\s+)?function\s+/, /^export\s+(default\s+)?class\s+/, /^export\s+(type|interface)\s+/, /^export\s+(const|let|var)\s+\w+/, /^(async\s+)?function\s+\w+/, /^class\s+\w+/, /^interface\s+\w+/, /^type\s+\w+\s*=/, /^(const|let|var)\s+\w+\s*[:=]/, /^import\s+/, ] return src .split("\n") .filter((line) => patterns.some((p) => p.test(line.trimStart()))) .join("\n") } function extractMinimal(src: string): string { const lines = src.split("\n") const head = lines.slice(0, 10) const exports = lines.filter((l) => /^export\s/.test(l.trimStart())) const seen = new Set() return [...head, "", "// ...", ...exports] .filter((l) => { if (seen.has(l)) return false seen.add(l) return true }) .join("\n") } export default tool({ description: "Compress a source file for agent context. 0 LLM tokens. " + "Modes: full (strip comments), signatures (declarations only), minimal (header + exports).", args: { file: tool.schema.string().describe("File path relative to project root or absolute"), mode: tool.schema.enum(["full", "signatures", "minimal"]).describe("Compression level"), }, async execute(args, context) { const wt = resolveWorktree(context.worktree) const abs = args.file.startsWith("/") ? args.file : join(wt, args.file) if (!existsSync(abs)) throw new Error(`File not found: ${args.file}`) const src = readFileSync(abs, "utf-8") const originalLines = src.split("\n").length let compressed: string switch (args.mode) { case "full": compressed = stripComments(src) break case "signatures": compressed = extractSignatures(src) break case "minimal": compressed = extractMinimal(src) break default: compressed = src } const compressedLines = compressed.split("\n").length const savedLines = originalLines - compressedLines const ratio = originalLines > 0 ? Math.round((1 - compressedLines / originalLines) * 100) : 0 debugLog("tool", "compress_executed", { file: args.file, mode: args.mode, original_lines: originalLines, compressed_lines: compressedLines, saved_lines: savedLines, reduction_percent: ratio, }) return JSON.stringify({ file: args.file, mode: args.mode, content: compressed, original_lines: originalLines, compressed_lines: compressedLines, saved_lines: savedLines, reduction: `${ratio}%`, }) }, })