diff --git a/package-lock.json b/package-lock.json index 8ab75fb..23ec4e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "token-optimizer", - "version": "0.1.8", + "version": "0.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "token-optimizer", - "version": "0.1.8", + "version": "0.2.1", "license": "MIT", "dependencies": { "@opencode-ai/plugin": "latest" diff --git a/package.json b/package.json index 084037b..acb08e1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "token-optimizer", "type": "module", - "version": "0.1.8", + "version": "0.2.1", "description": "60-75% token reduction for all AI coding agents (OpenCode, Codex, Cursor, Claude Desktop) via output compression, schema slimming, and read compaction. Runs as a persistent MCP server.", "main": "dist/src/plugin.js", "types": "dist/src/plugin.d.ts", diff --git a/scripts/setup.ts b/scripts/setup.ts index 02204c8..e2214d7 100644 --- a/scripts/setup.ts +++ b/scripts/setup.ts @@ -19,7 +19,6 @@ const HOME = os.homedir(); const IS_WINDOWS = process.platform === "win32"; const PKG_NAME = "token-optimizer"; const SERVER_ENTRY = "dist/src/mcp-server.js"; -const PLUGIN_ENTRY = "dist/src/plugin.js"; // ── URL pathname → filesystem path (handles Windows /C:/... prefix) ─────────── @@ -180,12 +179,20 @@ function detectAgents(): AgentConfig[] { // ── Patchers ────────────────────────────────────────────────────────────────── +// OpenCode MCP entry format (matches opencode.json schema) +const OPENCODE_MCP_ENTRY = (serverPath: string) => ({ + enabled: true, + type: "local", + command: ["node", serverPath], +}); + +// Other agents (Cursor, Claude Desktop, Windsurf) use the flat { command, args } shape const MCP_ENTRY = (serverPath: string) => ({ command: "node", args: [serverPath], }); -function patchOpenCodeJson(configPath: string, serverPath: string, pluginPath: string, remove: boolean): boolean { +function patchOpenCodeJson(configPath: string, serverPath: string, remove: boolean): boolean { let raw = "{}"; if (fs.existsSync(configPath)) raw = fs.readFileSync(configPath, "utf8"); @@ -198,37 +205,37 @@ function patchOpenCodeJson(configPath: string, serverPath: string, pluginPath: s } // ── MCP server entry ──────────────────────────────────────────────────────── + // OpenCode schema: mcp entries sit directly under the "mcp" key, NOT mcp.servers. if (!cfg.mcp || typeof cfg.mcp !== "object") { cfg.mcp = {}; } const mcp = cfg.mcp as Record; - if (!mcp.servers || typeof mcp.servers !== "object") { - mcp.servers = {}; + + // Clean up stale mcp.servers entries written by older versions + if (mcp.servers && typeof mcp.servers === "object") { + const servers = mcp.servers as Record; + delete servers[PKG_NAME]; + if (Object.keys(servers).length === 0) delete mcp.servers; } - const servers = mcp.servers as Record; if (remove) { - delete servers[PKG_NAME]; + delete mcp[PKG_NAME]; } else { - servers[PKG_NAME] = MCP_ENTRY(serverPath); + mcp[PKG_NAME] = OPENCODE_MCP_ENTRY(serverPath); } - // ── Plugin entry (absolute path to plugin.js in the "plugin" array) ───────── - // OpenCode resolves plugin entries as absolute paths — package names only work - // if the package is locally installed, which it won't be for global installs. - if (!Array.isArray(cfg.plugin)) { - cfg.plugin = []; - } - const plugins = cfg.plugin as string[]; - // Remove any stale entries (old package-name form or old path) - const filtered = plugins.filter( - (p) => p !== PKG_NAME && p !== pluginPath + // ── Plugin entry ───────────────────────────────────────────────────────────── + // OpenCode installs npm plugins via Bun at startup. Use the package name. + // Clean up any stale absolute-path entries written by older versions first. + if (!Array.isArray(cfg.plugin)) cfg.plugin = []; + const plugins = (cfg.plugin as string[]).filter( + (p) => p !== PKG_NAME && !p.includes(path.join(PKG_NAME, "dist")) ); - if (remove) { - cfg.plugin = filtered; + cfg.plugin = plugins; + if (plugins.length === 0) delete cfg.plugin; } else { - cfg.plugin = [...filtered, pluginPath]; + cfg.plugin = [...plugins, PKG_NAME]; } fs.mkdirSync(path.dirname(configPath), { recursive: true }); @@ -321,14 +328,9 @@ function patchAgentsMd(configPath: string, serverPath: string, remove: boolean): function patchAgent(agent: AgentConfig, serverPath: string, remove: boolean): void { let ok = false; - // Derive plugin path from server path: same package root, different entry file. - const pluginPath = serverPath.replace( - SERVER_ENTRY.replace(/\//g, path.sep), - PLUGIN_ENTRY.replace(/\//g, path.sep), - ); switch (agent.type) { case "opencode-json": - ok = patchOpenCodeJson(agent.configPath, serverPath, pluginPath, remove); + ok = patchOpenCodeJson(agent.configPath, serverPath, remove); break; case "mcp-json": ok = patchMcpJson(agent.configPath, serverPath, remove); diff --git a/src/plugin.ts b/src/plugin.ts index 3185c37..caf37af 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -374,7 +374,7 @@ function compressGrepOutput(raw: string, workingDirectory: string): string { // ─── Plugin definition ──────────────────────────────────────────────────────── -export const TokenOptimizerPlugin: Plugin = async ({ directory, client }) => { +export const server: Plugin = async ({ directory, client }) => { const stats = createStats() /** @@ -924,4 +924,4 @@ export const TokenOptimizerPlugin: Plugin = async ({ directory, client }) => { } } -export default TokenOptimizerPlugin +export default server