spb/khaelor Public
KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.
TypeScript 82.9%
HTML 14.9%
CSS 1.1%
JavaScript 0.7%
1/**2 * KHAELOR3 * File: prototypes/tui-spike/driver/debug-ink-input.tsx4 * Description: Diagnostic harness — traces stdin arrival vs Ink useInput delivery vs echo-frame5 * write time, to attribute candidate B's measured input latency (skew vs real stall).6 *7 * Author: Simon-Pierre Boucher8 * Contact: contact@spboucher.ai9 */1011import fs from "node:fs";12import React, { useEffect, useReducer, useState } from "react";13import { Box, Static, Text, render, useApp, useInput } from "ink";14import { DemoModel } from "../shared/demo";1516const log: string[] = [];17const t0 = process.hrtime.bigint();18const ms = () => Number(process.hrtime.bigint() - t0) / 1e6;1920const model = new DemoModel();2122process.stdin.on("data", (b: Buffer) => {23 log.push(`${ms().toFixed(1)} DATA ${JSON.stringify(b.toString("utf8"))}`);24});2526const origWrite = process.stdout.write.bind(process.stdout);27let lastComposer = "";28(process.stdout as unknown as { write: unknown }).write = (chunk: unknown, ...rest: unknown[]) => {29 const s = typeof chunk === "string" ? chunk : Buffer.from(chunk as Uint8Array).toString("utf8");30 if (lastComposer.length > 0 && s.includes("❯ " + lastComposer)) {31 log.push(`${ms().toFixed(1)} ECHO ${JSON.stringify(lastComposer.slice(-8))}`);32 lastComposer = "";33 }34 return (origWrite as (c: unknown, ...r: unknown[]) => boolean)(chunk, ...rest);35};3637function App(): React.JSX.Element {38 const { exit } = useApp();39 const [settled, setSettled] = useState<{ id: number; text: string }[]>([]);40 const [, force] = useReducer((x: number) => x + 1, 0);4142 useEffect(() => {43 model.on("settled", (lines: string[]) =>44 setSettled((s) => [...s, { id: s.length, text: lines.join("\n") }]),45 );46 model.on("dirty", () => force());47 const ticker = setInterval(() => {48 if (model.status !== null) force();49 }, 100);50 model.start();51 return () => clearInterval(ticker);52 }, []);5354 useInput((input, key) => {55 log.push(56 `${ms().toFixed(1)} USEINPUT ${JSON.stringify(input)} esc=${key.escape} ret=${key.return}`,57 );58 if (key.escape) {59 exit();60 return;61 }62 if (key.return) {63 model.handleKey({ type: "enter" });64 log.push(`${ms().toFixed(1)} ENTER composer_now=${JSON.stringify(model.composer)}`);65 return;66 }67 for (const ch of input) model.handleKey({ type: "char", ch });68 lastComposer = model.composer;69 log.push(`${ms().toFixed(1)} COMPOSER ${JSON.stringify(model.composer.slice(-12))}`);70 });7172 const snap = model.snapshot();73 const tail = snap.tail.length > 0 ? snap.tail.split("\n").slice(-12) : [];74 return (75 <>76 <Static items={settled}>{(i) => <Text key={i.id}>{i.text}</Text>}</Static>77 <Box flexDirection="column">78 {tail.length > 0 && <Text>{tail.join("\n")}</Text>}79 <Text>{"❯ " + snap.composer}</Text>80 </Box>81 </>82 );83}8485const inst = render(<App />, { exitOnCtrlC: true, patchConsole: false });86setTimeout(() => inst.unmount(), Number(process.env.DURATION_MS ?? 6000)).unref();87await inst.waitUntilExit();88model.stop();89fs.writeFileSync(process.env.DEBUG_OUT ?? "out/debug-ink.log", log.join("\n") + "\n");90process.exit(0);91