UNPKG

@phren/cli

Version:

Knowledge layer for AI agents - CLI, MCP server, and data layer

75 lines (74 loc) • 3.32 kB
import { type Json, type Target } from "./protocol.js"; /** Claude Code's `/btw` side questions from the phone. * * `/btw <question>` asks a quick question while Claude keeps working. Claude * 2.1.280 draws the answer in a panel under the conversation and writes * nothing to the session JSONL, so the Hook reads the settled panel from the * pane, scrolls it for a long answer, closes it with Escape and hands the text * to the phone as a `side-answer` frame. Nothing of it enters the transcript. */ export type SideState = "pending" | "answer" | "error" | "cancelled"; export interface SideAnswer { id: string; question: string; state: SideState; answer?: string; } /** The panel as the pane draws it: the asked questions (this session's * history, newest last and cut to the pane's width), the body lines, and * whether Claude is still answering. */ export interface SidePanel { questions: string[]; body: string[]; answering: boolean; settled: boolean; } /** The question in a `/btw ` prompt for Claude, or undefined for any other text. */ export declare function sideQuestionText(source: string, text: string): string | undefined; /** Parse the `/btw` panel out of visible pane text, or undefined when none is open. */ export declare function sidePanel(text: string): SidePanel | undefined; /** Whether the newest question in the panel is the one the Hook asked: the * panel cuts a long question to the pane's width with an ellipsis. */ export declare function panelAsks(panel: SidePanel, question: string): boolean; /** The lines `next` adds below `previous` after the panel scrolled, or * undefined when it did not move. Without an overlap the whole window is new. */ export declare function scrolledLines(previous: string[], next: string[]): string[] | undefined; /** Remove the panel's indentation and the wrap-trailing spaces. */ export declare function answerText(lines: string[]): string; export declare class SideQuestions { private readonly options; private records; private open; constructor(options?: { timeoutMs?: number; intervalMs?: number; openMs?: number; keepMs?: number; }); private paneKey; private read; /** While a side question owns the pane, typed input would land in its panel * (`x` clears the history, `f` forks), so every other input waits. */ assertAvailable(target: Target): void; /** The side answers the phone has not dismissed for this conversation. */ list(target: Target): (SideAnswer & { revision: number; })[]; private prune; /** Type `/btw <question>` into the Claude pane and watch for its answer. * Returns at once; the answer arrives on the transcript stream. */ ask(target: Target, pane: Json, text: string): Promise<{ id: string; }>; /** The phone dismissed the card: cancel a pending question (closing its * panel) and stop delivering it. */ dismiss(target: Target, id: string): { ok: true; }; private settle; /** Close the panel only while it is still drawn: Escape anywhere else * would interrupt Claude's running turn. */ private close; /** Scroll a long answer to its end, stitching the windows by their overlap. */ private collect; private watch; }