import { spawn } from "node:child_process"; import { existsSync } from "node:fs"; import { mkdir, writeFile } from "node:fs/promises"; import { homedir, platform } from "node:os"; import path from "node:path"; import { tool } from "@opencode-ai/plugin"; const TASK_KINDS = ["auto", "website", "business_document", "business_suite", "app", "code_project", "repo_patch", "coding"]; const defaultRuntimeScript = () => path.join(homedir(), ".config", "opencode", "kaiju-coder-7-runtime", "scripts", "run_kaiju_router.py"); const resolveSafeFile = (directory, filePath) => { const base = path.resolve(directory || process.cwd()); const resolved = path.resolve(base, filePath); if (resolved !== base && !resolved.startsWith(`${base}${path.sep}`)) { throw new Error(`Refusing to write outside the OpenCode directory: ${filePath}`); } return resolved; }; const runProcess = (command, args, options = {}) => new Promise((resolve, reject) => { const child = spawn(command, args, { ...options, env: { ...process.env, ...(options.env || {}), }, }); let stdout = ""; let stderr = ""; child.stdout?.on("data", (chunk) => { stdout += chunk.toString(); }); child.stderr?.on("data", (chunk) => { stderr += chunk.toString(); }); child.on("error", reject); child.on("close", (code) => { if (code === 0) { resolve({ stdout, stderr }); return; } reject(new Error([`Command failed with exit ${code}: ${command}`, stdout, stderr].filter(Boolean).join("\n"))); }); }); const shouldOpenArtifact = (text) => /\b(open it|open the (?:html|file|site|website|page)|launch it|show it)\b/i.test(text || ""); const extractArtifactPath = (stdout) => { const artifact = stdout.match(/^Artifact:\s*(.+)$/m); if (artifact) { return artifact[1].trim(); } const project = stdout.match(/^Project\/repo:\s*(.+)$/m); return project ? project[1].trim() : ""; }; const openArtifact = async (artifactPath) => { if (!artifactPath || !existsSync(artifactPath)) { return ""; } const system = platform(); if (system === "darwin") { await runProcess("open", [artifactPath]); } else if (system === "win32") { await runProcess("cmd", ["/c", "start", "", artifactPath]); } else { await runProcess("xdg-open", [artifactPath]); } return `Opened artifact: ${artifactPath}`; }; export const KaijuCoder7NoAutocontinuePlugin = async () => { const isKaijuCoder7 = (input) => { const payload = JSON.stringify({ agent: input?.agent, model: input?.model, provider: input?.provider, session: input?.session, }); return ( payload.includes("kaiju-coder-7") || payload.includes("Kaiju Coder 7") || payload.includes("kaiju/kaiju-coder-7") ); }; return { tool: { kaiju_write_file: tool({ description: "Write one exact small file in the current OpenCode directory. Use for exact one-file creation requests, not large generated websites or projects.", args: { file_path: tool.schema.string().describe("Relative path to write inside the current OpenCode directory."), content: tool.schema.string().describe("Exact file content to write."), }, async execute(args, context) { const target = resolveSafeFile(context.directory, args.file_path); await mkdir(path.dirname(target), { recursive: true }); await writeFile(target, args.content, "utf8"); return { output: `Wrote file: ${target}`, metadata: { file_path: target, bytes: Buffer.byteLength(args.content, "utf8") }, }; }, }), kaiju_artifact: tool({ description: "Create fast Kaiju Coder 7 websites, owner packs, apps, business documents, or code artifacts with the packaged local router. Use this instead of bash/write for large generated artifact tasks.", args: { prompt: tool.schema.string().describe("The full user request to build."), out_dir: tool.schema .string() .optional() .describe("Absolute output directory. For Desktop requests, use /Users//Desktop/."), kind: tool.schema .enum(TASK_KINDS) .optional() .describe("Artifact type. Use website for sites and business_suite for owner operating packs."), no_planner: tool.schema .boolean() .optional() .describe("Use deterministic rendering without a second model-planning call. Defaults to true."), }, async execute(args, context) { const runtimeScript = process.env.KAIJU_ROUTER_SCRIPT || defaultRuntimeScript(); if (!existsSync(runtimeScript)) { throw new Error(`Kaiju router runtime is missing: ${runtimeScript}`); } const outDir = args.out_dir || path.join(homedir(), "Desktop", "Kaiju-Coder-7-Artifacts"); const kind = args.kind || "auto"; const noPlanner = args.no_planner !== false; const procArgs = [runtimeScript, "--kind", kind, "--out-dir", outDir, "--prompt", args.prompt]; if (noPlanner) { procArgs.splice(1, 0, "--no-planner"); } context.metadata({ title: `Kaiju artifact: ${kind}`, metadata: { out_dir: outDir, kind, no_planner: noPlanner }, }); const result = await runProcess(process.env.KAIJU_PYTHON || "python3", procArgs, { cwd: context.directory || process.cwd(), }); let output = result.stdout.trim() || "Kaiju artifact command completed."; if (shouldOpenArtifact(args.prompt)) { const artifactPath = extractArtifactPath(output); try { const opened = await openArtifact(artifactPath); output += opened ? `\n${opened}` : "\nOpen requested, but no generated artifact path was available to open."; } catch (error) { output += `\nOpen requested, but could not open artifact: ${error.message}`; } } return { output, metadata: { stderr: result.stderr.trim(), out_dir: outDir, kind, no_planner: noPlanner }, }; }, }), }, async "chat.params"(input, output) { if (!isKaijuCoder7(input)) { return; } output.temperature = 0; output.maxOutputTokens = Math.min(output.maxOutputTokens || 768, 768); }, async "experimental.compaction.autocontinue"(input, output) { if (isKaijuCoder7(input)) { output.enabled = false; } }, async "experimental.session.compacting"(input, output) { if (!isKaijuCoder7(input)) { return; } output.context.push( [ "For Kaiju Coder 7 sessions, summarize only facts proven by tool output.", "Do not say a file was created unless a write/edit/bash/read result confirms it exists.", "If work is incomplete or unverified, mark it incomplete instead of complete.", "Never convert a maximum-step, compaction, or length-limit stop into a successful completion claim.", ].join("\n"), ); }, }; }; export default KaijuCoder7NoAutocontinuePlugin;