/** * QwenClaw Agent for Qwen Code CLI * * This agent intercepts all Qwen Code CLI interactions * and ensures QwenClaw daemon is always active. */ import { spawn } from "child_process"; import { join } from "path"; import { existsSync, readFileSync, writeFile } from "fs"; const QWENCLAW_DIR = process.env.QWENCLAW_DIR || join(process.env.HOME || process.env.USERPROFILE || "", "qwenclaw"); const QWEN_DIR = join(process.env.HOME || process.env.USERPROFILE || "", ".qwen"); const PID_FILE = join(QWEN_DIR, "qwenclaw", "daemon.pid"); /** * Check if QwenClaw daemon is running */ function isDaemonRunning() { if (!existsSync(PID_FILE)) return false; try { const pid = parseInt(readFileSync(PID_FILE, "utf-8").trim(), 10); if (isNaN(pid)) return false; process.kill(pid, 0); return true; } catch { return false; } } /** * Start QwenClaw daemon */ function startDaemon() { if (isDaemonRunning()) { console.log("[QwenClaw] Daemon already running"); return; } console.log("[QwenClaw] Starting daemon..."); const proc = spawn("bun", ["run", "start", "--web"], { cwd: QWENCLAW_DIR, detached: true, stdio: "ignore", windowsHide: true, }); proc.unref(); setTimeout(() => { if (isDaemonRunning()) { console.log("[QwenClaw] āœ… Daemon started"); console.log("[QwenClaw] Web Dashboard: http://127.0.0.1:4632"); } }, 3000); } /** * Intercept Qwen Code CLI input and prepend QwenClaw context */ function interceptInput(input) { // If input starts with /qwenclaw, handle as daemon command if (input.startsWith("/qwenclaw")) { return handleQwenClawCommand(input); } // Otherwise, pass through to Qwen Code with QwenClaw context return { context: "QwenClaw daemon is active. All interactions are logged and can be automated.", input: input, }; } /** * Handle QwenClaw commands */ function handleQwenClawCommand(command) { const args = command.slice("/qwenclaw".length).trim().split(/\s+/); const subcommand = args[0]; switch (subcommand) { case "start": startDaemon(); return "Starting QwenClaw daemon..."; case "status": if (isDaemonRunning()) { const pid = parseInt(readFileSync(PID_FILE, "utf-8").trim(), 10); return `āœ… QwenClaw daemon is running (PID: ${pid})\n Web Dashboard: http://127.0.0.1:4632`; } else { return "āŒ QwenClaw daemon is not running\n Run /qwenclaw start to start it"; } case "stop": if (isDaemonRunning()) { const pid = parseInt(readFileSync(PID_FILE, "utf-8").trim(), 10); try { process.kill(pid, "SIGTERM"); return "Stopping QwenClaw daemon..."; } catch { return "Failed to stop daemon"; } } else { return "Daemon is not running"; } case "send": const message = args.slice(1).join(" "); if (!message) { return "Usage: /qwenclaw send "; } return sendToDaemon(message); case "help": return ` QwenClaw Commands: /qwenclaw start - Start daemon /qwenclaw status - Check daemon status /qwenclaw stop - Stop daemon /qwenclaw send - Send message to daemon /qwenclaw skills - List available skills /qwenclaw help - Show this help Web Dashboard: http://127.0.0.1:4632 `; default: return `Unknown command: ${subcommand}\nRun /qwenclaw help for usage`; } } /** * Send message to daemon */ function sendToDaemon(message) { return new Promise((resolve) => { const proc = spawn("bun", ["run", "send", message], { cwd: QWENCLAW_DIR, stdio: ["pipe", "pipe", "pipe"], }); let output = ""; proc.stdout.on("data", (data) => { output += data.toString(); }); proc.on("close", () => { resolve(output || "Message sent to daemon"); }); }); } /** * Initialize QwenClaw agent */ export function init() { console.log("\n🐾 QwenClaw Agent initialized"); console.log(" QwenClaw is now your ALWAYS-ON agent"); console.log(" Use /qwenclaw help for commands\n"); // Auto-start daemon startDaemon(); return { name: "QwenClaw", version: "1.0.0", interceptInput, commands: { qwenclaw: handleQwenClawCommand, }, }; } // Auto-initialize when loaded init();