diff --git a/extensions/remote-control/index.ts b/extensions/remote-control/index.ts index afed32e..b4e074b 100644 --- a/extensions/remote-control/index.ts +++ b/extensions/remote-control/index.ts @@ -24,7 +24,6 @@ import { } from "./config.js"; import { type RawMessage, serializeMessage } from "./messages.js"; import { type RemoteServer, startServer } from "./server.js"; -import { registerSpikeCommand } from "./spike.js"; // ── Extension entry point ──────────────────────────────────────────────────── @@ -34,9 +33,6 @@ const QRCode = _require("qrcode") as { }; export default function remoteControl(pi: ExtensionAPI) { - // Register spike command for Phase 0 PoC - registerSpikeCommand(pi); - let server: RemoteServer | undefined; let pendingSyncTimer: ReturnType | undefined; diff --git a/extensions/remote-control/spike.ts b/extensions/remote-control/spike.ts index bed2661..8797c81 100644 --- a/extensions/remote-control/spike.ts +++ b/extensions/remote-control/spike.ts @@ -14,9 +14,8 @@ import * as fs from "node:fs"; import * as path from "node:path"; import * as os from "node:os"; -import { spawn, execSync } from "node:child_process"; +import { execSync } from "node:child_process"; import { WebSocketServer } from "ws"; -import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent"; const SPIKE_SESSION = "pi-spike"; const WS_PORT = 7799; @@ -113,23 +112,24 @@ function startWebSocketServer(fifoPath: string): { wss: WebSocketServer, cleanup } /** - * Attach to the tmux session in the current terminal + * Print instructions for connecting to the session */ -function attachToSession(sessionName: string): void { - console.log(`[spike] Attaching to tmux session: ${sessionName}`); - console.log(`[spike] To detach: Ctrl+B, then D`); - console.log(`[spike] WebSocket available at: ws://127.0.0.1:${WS_PORT}/spike`); +function printInstructions(sessionName: string): void { + console.log(""); + console.log("=== Spike Server Running ==="); + console.log(""); + console.log("To attach to the tmux session (in another terminal):"); + console.log(` tmux attach -t ${sessionName}`); + console.log(""); + console.log("WebSocket endpoint:"); + console.log(` ws://127.0.0.1:${WS_PORT}/spike`); + console.log(""); + console.log("To test with the HTML client:"); + const clientPath = path.join(path.dirname(new URL(import.meta.url).pathname), "spike-client.html"); + console.log(` open ${clientPath}`); + console.log(""); + console.log("To stop: Ctrl+C in this terminal"); console.log(""); - - // Spawn tmux attach in the foreground - // This will take over the terminal until the user detaches - const attach = spawn("tmux", ["attach", "-t", sessionName], { - stdio: "inherit", - }); - - attach.on("exit", (code) => { - console.log(`\n[spike] Detached from session (exit code: ${code})`); - }); } /** @@ -159,14 +159,15 @@ function cleanup(cleanupFn: (() => void) | null): void { /** * Main spike entry point */ -export async function runSpike(_ctx: ExtensionContext): Promise { +export async function runSpike(): Promise { console.log("=== Phase 0 Spike: tmux Stream PoC ===\n"); let cleanupFn: (() => void) | null = null; // Setup cleanup handlers - process.on("SIGINT", () => cleanup(cleanupFn)); - process.on("SIGTERM", () => cleanup(cleanupFn)); + const cleanupHandler = () => cleanup(cleanupFn); + process.on("SIGINT", cleanupHandler); + process.on("SIGTERM", cleanupHandler); try { // Step 1: Create or reuse tmux session @@ -186,8 +187,12 @@ export async function runSpike(_ctx: ExtensionContext): Promise { // Give the server a moment to start await new Promise(resolve => setTimeout(resolve, 500)); - // Step 4: Attach to session - attachToSession(SPIKE_SESSION); + // Step 4: Print instructions + printInstructions(SPIKE_SESSION); + + // Keep the process alive + // User can Ctrl+C to stop + await new Promise(() => {}); // Never resolves } catch (err) { console.error("[spike] Error:", err); @@ -195,14 +200,7 @@ export async function runSpike(_ctx: ExtensionContext): Promise { } } -/** - * Register the spike command with pi - */ -export function registerSpikeCommand(pi: ExtensionAPI): void { - pi.registerCommand("spike", { - description: "Phase 0 Spike: Start tmux stream PoC (ws://127.0.0.1:7799/spike)", - handler: async (_args, ctx) => { - await runSpike(ctx); - }, - }); +// Run if invoked directly +if (import.meta.url === `file://${process.argv[1]}`) { + runSpike(); } diff --git a/package.json b/package.json index 480c749..114cfa3 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@earendil-works/pi-tui": "*" }, "scripts": { + "spike": "./run-spike.sh", "lint": "biome check --write .", "lint:check": "biome check .", "prepare": "node .husky/install.mjs" diff --git a/run-spike.sh b/run-spike.sh new file mode 100755 index 0000000..b873a31 --- /dev/null +++ b/run-spike.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# Phase 0 Spike runner +# Transpiles and runs the spike PoC + +set -e + +cd "$(dirname "$0")" + +echo "=== Building spike.ts ===" +npx --yes tsx extensions/remote-control/spike.ts