@noir-ai/cli 1.14.0 → 1.15.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/bin.d.ts +10 -3
- package/dist/bin.js +1240 -365
- package/dist/bin.js.map +1 -1
- package/dist/index.d.ts +39 -12
- package/dist/index.js +1258 -383
- package/dist/index.js.map +1 -1
- package/dist/{output-BNE9htEm.d.ts → output-2T2lGR7a.d.ts} +3 -3
- package/dist/tui/{chunk-6LO4LXEH.js → chunk-5KWETEMF.js} +1191 -352
- package/dist/tui/chunk-5KWETEMF.js.map +1 -0
- package/dist/tui/{chunk-ESZTOQEC.js → chunk-KETMM3BA.js} +25 -3
- package/dist/tui/chunk-KETMM3BA.js.map +1 -0
- package/dist/tui/{chunk-2RK6AFZQ.js → chunk-TPNJEN4B.js} +2 -2
- package/dist/tui/chunk-TPNJEN4B.js.map +1 -0
- package/dist/tui/{create-FD4E626Z.js → create-6KDIFPUL.js} +10 -6
- package/dist/tui/create-6KDIFPUL.js.map +1 -0
- package/dist/tui/index.js +994 -120
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/{install-YGTUSLRO.js → install-KA35ONQP.js} +2 -2
- package/dist/tui/{sync-N633SQSS.js → sync-AII5WCRO.js} +8 -4
- package/dist/tui/sync-AII5WCRO.js.map +1 -0
- package/dist/tui/{update-LET5BWOU.js → update-XGKJ4KBR.js} +3 -3
- package/package.json +11 -11
- package/dist/tui/chunk-2RK6AFZQ.js.map +0 -1
- package/dist/tui/chunk-6LO4LXEH.js.map +0 -1
- package/dist/tui/chunk-ESZTOQEC.js.map +0 -1
- package/dist/tui/create-FD4E626Z.js.map +0 -1
- package/dist/tui/sync-N633SQSS.js.map +0 -1
- /package/dist/tui/{install-YGTUSLRO.js.map → install-KA35ONQP.js.map} +0 -0
- /package/dist/tui/{update-LET5BWOU.js.map → update-XGKJ4KBR.js.map} +0 -0
package/dist/tui/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tui/index.tsx","../../src/tui/App.tsx","../../src/tui/CommandInput.tsx","../../src/tui/capture.ts","../../src/tui/Footer.tsx","../../src/tui/hints.ts","../../src/tui/format.ts","../../src/tui/Header.tsx","../../src/tui/hooks/useInputBuffer.ts","../../src/tui/OutputPane.tsx","../../src/tui/overlays/ConfirmOverlay.tsx","../../src/tui/Panel.tsx","../../src/tui/palette/fuzzyMatch.ts","../../src/tui/palette/matcher.ts","../../src/tui/palette/Palette.tsx","../../src/tui/palette/output.ts","../../src/tui/palette/rows.ts","../../src/tui/StatusBar.tsx","../../src/tui/palette/history.ts"],"sourcesContent":["// `noir tui` entry — mounts the Ink dashboard. LAZY-loaded: bin.ts does\n// `await import('./tui/index.js')` inside the `tui` action only, so React +\n// Ink never enter the main CLI startup path (`noir status`, `noir doctor`, a\n// bare `noir` all stay React-free). The entry wires the default\n// {@link TuiDeps.fetchStatus} (a wrapper around `gatherStatusPayload` that\n// folds any failure to `null`) and the SAME dispatch seam `home(opts, deps)`\n// uses, then renders the App and resolves on exit.\n//\n// Stream discipline: while the dashboard runs, Ink owns the terminal (raw-mode\n// stdin + ANSI render to stdout). On `q` / Esc / Ctrl+C, Ink restores the\n// terminal before unmounting. Dispatched commands write through the captured\n// stream shim, so the host terminal only ever sees Ink's frames.\n\nimport { loadProjectInfo } from '@noir-ai/core';\nimport { render } from 'ink';\nimport { buildPaletteCommandsForTui } from '../bin.js';\nimport { gatherStatusPayload, type StatusOptions, type StatusPayload } from '../commands/status.js';\nimport type { CliOptions } from '../output.js';\nimport { App, type TuiDeps } from './App.js';\nimport { loadRecent, recordRecent } from './palette/history.js';\n\nexport type { TuiDeps } from './App.js';\n\n/**\n * Default status fetcher: wraps {@link gatherStatusPayload} so any failure (an\n * uninitialized project, a probe hiccup, a daemon-down that threw) folds to\n * `null` and the dashboard degrades cleanly. `gatherStatusPayload` itself is\n * probe-only and never auto-starts a daemon, so this is safe to poll.\n */\nfunction defaultFetchStatus(opts: CliOptions): () => Promise<StatusPayload | null> {\n return async () => {\n try {\n return await gatherStatusPayload(opts as StatusOptions);\n } catch {\n return null;\n }\n };\n}\n\n/**\n * Mount the dashboard and resolve when the user exits. The caller (bin.ts)\n * supplies the dispatch seam — the same shape `home(opts, deps).dispatch`\n * uses — so command routing is owned by the bin, not reimplemented here.\n */\nexport async function runTui(opts: CliOptions, dispatch: TuiDeps['dispatch']): Promise<void> {\n process.stdout.write('\\x1b[2J\\x1b[H');\n const deps = await buildTuiDeps(opts, dispatch);\n const instance = render(<App deps={deps} />);\n await instance.waitUntilExit();\n}\n\n/**\n * `noir palette` — mount the App palette-first (S3). Reuses the SAME\n * `runTui` deps (dispatch seam, commands, recents) so the fuzzy command\n * palette is identical to the dashboard's Ctrl+K palette, just opened\n * directly. `dispatch` is the same shape `runTui` receives from bin.ts.\n */\nexport async function runPalette(opts: CliOptions, dispatch: TuiDeps['dispatch']): Promise<void> {\n process.stdout.write('\\x1b[2J\\x1b[H');\n // Reuse runTui's deps wiring wholesale, then render palette-first.\n // Factor the shared deps build out of runTui into a helper to avoid\n // duplicating the projectId/commands/recents logic.\n const deps = await buildTuiDeps(opts, dispatch);\n const instance = render(\n <App deps={deps} initialMode={{ kind: 'palette', corpus: 'commands' }} />,\n );\n await instance.waitUntilExit();\n}\n\n/**\n * Build the shared {@link TuiDeps} for both `runTui` and `runPalette`.\n * Extracted so the two entry points cannot drift (projectId-keyed recents,\n * the palette source, and the dispatch seam are identical).\n */\nasync function buildTuiDeps(opts: CliOptions, dispatch: TuiDeps['dispatch']): Promise<TuiDeps> {\n // ProjectId-keyed recent-commands persistence (C3): resolve the canonical id\n // once at launch so recents are isolated per project (respects the .noir/\n // single-source-of-truth invariant). An uninitialized project (loadProjectInfo\n // throws) degrades to empty recents — the palette still works with the full\n // command list.\n let projectId: string | null = null;\n try {\n projectId = loadProjectInfo(process.cwd()).id;\n } catch {\n projectId = null;\n }\n const deps: TuiDeps = {\n dispatch,\n fetchStatus: defaultFetchStatus(opts),\n // The palette source, derived from a fresh commander program at launch (B3).\n commands: buildPaletteCommandsForTui(),\n // C3 — persistent recent commands (projectId-keyed). recordRecent is async\n // only to match the TuiDeps seam; it never rejects.\n record: (argv) => {\n if (projectId) recordRecent(projectId, argv);\n return Promise.resolve();\n },\n loadRecent: async () => {\n if (!projectId) return [];\n // Hydrate the bare {argv,id} entries against the live palette commands so\n // the palette renders real labels/descriptions; drop stale entries whose\n // argv no longer exists in the current build.\n const byId = new Map(deps.commands?.map((c) => [c.id, c]) ?? []);\n return loadRecent(projectId)\n .map((e) => byId.get(e.id))\n .filter((c): c is NonNullable<typeof c> => c !== undefined);\n },\n };\n return deps;\n}\n","// Dashboard root component. Owns the interactive state (input buffer, scroll\n// offset, dispatched output, snapshot, palette query/active, the active screen)\n// and the SINGLE keybinding dispatcher (v2 — the palette, home, help, and search\n// surfaces all collapsed into one corpus-aware palette, so keyboard routing\n// lives here alone). The snapshot is fetched via {@link TuiDeps.fetchStatus} on\n// a short interval (paused while a `/<command>` is dispatching, so a StatusBar\n// refresh can never race the stream capture). Dispatch goes through the EXISTING\n// {@link TuiDeps.dispatch} seam — the same `home(opts, deps).dispatch` shape —\n// so command routing is NOT reimplemented here.\n//\n// Screens (the {@link Mode} union): `dashboard` (default), `palette` (the single\n// command surface — corpus `commands` | `output` | `help`, opened by Ctrl+K /\n// Ctrl+F / h / ? and cycled with Tab), and `confirm` (the `y/N` gate before a\n// destructive dispatch — now covering EVERY dispatch path, typed /command\n// included). Every selection funnels through {@link handleRun}, so destructive\n// commands always pause at the confirm overlay.\n\nimport { Box, type Key, Text, useApp, useInput } from 'ink';\nimport { type ReactElement, useEffect, useMemo, useState } from 'react';\nimport type { StatusPayload } from '../commands/status.js';\nimport { c, divider } from '../theme.js';\nimport { CommandInput } from './CommandInput.js';\nimport { captureProcessOutput } from './capture.js';\nimport { isDestructive } from './commands/registry.js';\nimport { type HomeSection, resolveSections } from './commands/sections.js';\nimport { Footer } from './Footer.js';\nimport { formatStatusPayload } from './format.js';\nimport { Header } from './Header.js';\nimport { useInputBuffer } from './hooks/useInputBuffer.js';\nimport { OutputPane } from './OutputPane.js';\nimport { ConfirmOverlay } from './overlays/ConfirmOverlay.js';\nimport { Panel } from './Panel.js';\nimport { type FuzzyMatcher, handRolledMatcher } from './palette/matcher.js';\nimport { Palette } from './palette/Palette.js';\nimport { buildPaletteRows, CORPORA, type Corpus } from './palette/rows.js';\nimport type { PaletteCommand } from './palette/types.js';\nimport { StatusBar } from './StatusBar.js';\n\n/** Injected dependencies (mirrors the `home(opts, deps)` seam). */\nexport interface TuiDeps {\n /**\n * Run a Noir sub-command by user-form argv on a fresh commander program.\n * Same contract as {@link HomeDeps.dispatch}: must not throw — it owns its\n * own error → exit-code mapping and leaves the outcome on `process.exitCode`.\n */\n dispatch: (argv: readonly string[]) => Promise<void>;\n /**\n * Fetch the live status snapshot. Defaults to a wrapper around\n * `gatherStatusPayload` that folds ANY failure to `null` so the dashboard\n * degrades cleanly (uninitialized project, daemon down, probe hiccup).\n */\n fetchStatus: () => Promise<StatusPayload | null>;\n /**\n * The palette source — one {@link PaletteCommand} per leaf `noir` subcommand,\n * derived from the commander tree at `noir tui` launch (NOT re-walked per\n * keystroke). Optional for backward-compat (B1 tests omit it); when absent the\n * palette falls back to an empty command list.\n */\n readonly commands?: readonly PaletteCommand[];\n /** The fuzzy matcher. Optional — defaults to {@link handRolledMatcher}. */\n readonly matcher?: FuzzyMatcher;\n /**\n * Record a dispatched command into the persisted recent-commands list\n * (C3). Optional for backward-compat — when absent, recents are not recorded.\n */\n record?: (argv: readonly string[]) => Promise<void>;\n /**\n * Load the persisted recent commands (newest first) for the palette's recent\n * section. Optional — when absent, defaults to the on-disk history loader.\n */\n loadRecent?: () => Promise<readonly PaletteCommand[]>;\n}\n\n/** Optional initial state for tests (the live entry leaves these at defaults). */\nexport interface AppProps {\n deps: TuiDeps;\n /** Initial snapshot — skips the first fetch when provided (tests). */\n initialPayload?: StatusPayload | null;\n /** Refresh interval in ms (default 5000). Set large in tests. */\n refreshMs?: number;\n /**\n * The mode the App starts in. Defaults to the dashboard. `noir palette`\n * mounts the app palette-first via `{ kind: 'palette', corpus: 'commands' }`.\n */\n initialMode?: Mode;\n}\n\n/**\n * The dashboard's discriminated screen union. Exactly one screen is active at a\n * time; the App keys its render + keybinding handler off `mode.kind`.\n * - `dashboard` — the default screen: live snapshot, command input, output pane.\n * - `palette` — the single command surface, with a `corpus` selecting what the\n * query filters (commands / output / help).\n * - `confirm` — an in-TUI confirmation prompt for a destructive dispatch.\n */\nexport type Mode =\n | { kind: 'dashboard' }\n | { kind: 'palette'; corpus: Corpus }\n | { kind: 'confirm'; argv: string[] };\n\ninterface DispatchedOutput {\n title: string;\n lines: string[];\n}\n\nexport function App({\n deps,\n initialPayload = null,\n refreshMs = 5000,\n initialMode = { kind: 'dashboard' },\n}: AppProps): ReactElement {\n const { exit } = useApp();\n const { buffer, setBuffer, pushHistory, recall, clear, seed } = useInputBuffer();\n const [mode, setMode] = useState<Mode>(initialMode);\n const [payload, setPayload] = useState<StatusPayload | null>(initialPayload);\n const [loading, setLoading] = useState(initialPayload === null);\n const [output, setOutput] = useState<DispatchedOutput | null>(null);\n // The output lines the `output` corpus searches. Captured from the CURRENT\n // dispatched output when the corpus opens, so a new dispatch landing mid-search\n // cannot change the corpus mid-filter.\n const [searchLines, setSearchLines] = useState<readonly string[]>([]);\n const [scrollOffset, setScrollOffset] = useState(0);\n const [running, setRunning] = useState(false);\n const [notice, setNotice] = useState<string | null>(null);\n // The argv of a dispatch the user just submitted. An effect watches this so\n // the dispatch runs AFTER React flushes the \"running…\" frame.\n const [pending, setPending] = useState<string[] | null>(null);\n // The persisted recent-commands list for the palette, loaded once on mount.\n const [recent, setRecent] = useState<readonly PaletteCommand[]>([]);\n // The curated home sections, resolved against the live registry once.\n const [homeSections, setHomeSections] = useState<readonly HomeSection[]>([]);\n // v2 — the single palette's query + active row (owned here so the App's one\n // `useInput` routes palette keys and can resolve the active row on Enter).\n const [paletteQuery, setPaletteQuery] = useState('');\n const [paletteActive, setPaletteActive] = useState(0);\n\n // ----- load the palette's recent commands once on mount ------------------\n useEffect(() => {\n if (!deps.loadRecent) return;\n const loader = deps.loadRecent;\n let cancelled = false;\n void loader()\n .then((entries) => {\n if (!cancelled) {\n setRecent(entries);\n // Seed the shell-recall overlay from the SAME persisted recents the\n // palette shows, so ↑/↓ recall and the palette recents never diverge.\n seed(entries.map((c) => `/${c.argv.join(' ')}`));\n }\n })\n .catch(() => {\n // Recents are a nice-to-have — a read failure leaves the full list.\n });\n return () => {\n cancelled = true;\n };\n }, [deps.loadRecent, seed]);\n\n // ----- resolve the curated home sections once ---------------------------\n useEffect(() => {\n let cancelled = false;\n void resolveSections(deps.commands ?? []).then((sections) => {\n if (!cancelled) setHomeSections(sections);\n });\n return () => {\n cancelled = true;\n };\n }, [deps.commands]);\n\n // ----- snapshot refresh: paused while a dispatch is in flight ----------\n useEffect(() => {\n // Pause polling while a dispatch runs OR the palette/confirm overlay is\n // open (the dashboard snapshot is off-screen then, so polling only wastes\n // a fetch + a re-render).\n if (running || mode.kind !== 'dashboard') return;\n let cancelled = false;\n let inFlight = false;\n const fetched = (p: StatusPayload | null): void => {\n inFlight = false;\n if (cancelled) return;\n setPayload(p);\n setLoading(false);\n };\n setLoading(true);\n inFlight = true;\n void deps\n .fetchStatus()\n .then(fetched)\n .catch(() => fetched(null));\n const id = setInterval(() => {\n // Skip the tick while a fetch is still pending — a slow/restarting daemon\n // (gatherStatusPayload does a probe + MCP round-trips) must not stack\n // concurrent fetches.\n if (inFlight) return;\n inFlight = true;\n void deps\n .fetchStatus()\n .then(fetched)\n .catch(() => {\n // Keep the last-good payload on a transient probe/daemon failure — a\n // poll-time hiccup must not wipe the dashboard to \"down\".\n inFlight = false;\n });\n }, refreshMs);\n return () => {\n cancelled = true;\n clearInterval(id);\n };\n }, [running, mode.kind, refreshMs, deps.fetchStatus]);\n\n // ----- dispatch runner: fires after the \"running\" frame has flushed ------\n useEffect(() => {\n if (pending === null) return;\n const argv = pending;\n let active = true;\n const run = async (): Promise<void> => {\n const captured = await captureProcessOutput(() => deps.dispatch(argv));\n if (!active) return;\n const combined = `${captured.stdout}${captured.stderr}`;\n const lines = combined.split('\\n');\n while (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();\n const title = `/${argv.join(' ')} → exit ${captured.exitCode}`;\n setOutput({ title, lines });\n setNotice(`ran /${argv.join(' ')} → exit ${captured.exitCode}`);\n setRunning(false);\n setPending(null);\n setScrollOffset(0);\n };\n void run();\n return () => {\n active = false;\n };\n }, [pending, deps.dispatch]);\n\n // ----- shared dispatch helper --------------------------------------------\n function dispatchCmd(argv: readonly string[]): void {\n setOutput(null);\n setRunning(true);\n setPending([...argv]);\n }\n\n // ----- unified run seam: every selection (palette / home / confirm) ------\n function handleRun(argv: readonly string[], destructive: boolean): void {\n if (destructive) {\n setMode({ kind: 'confirm', argv: [...argv] });\n return;\n }\n dispatchCmd([...argv]);\n void recordRuns([...argv]);\n pushHistory(`/${argv.join(' ')}`);\n setMode({ kind: 'dashboard' });\n }\n\n function recordRuns(argv: readonly string[]): void {\n const recorder = deps.record;\n if (recorder) void recorder(argv);\n }\n\n // ----- palette open / cycle helpers --------------------------------------\n function openPalette(corpus: Corpus): void {\n setPaletteQuery('');\n setPaletteActive(0);\n setMode({ kind: 'palette', corpus });\n setNotice(null);\n }\n\n function openOutputSearch(): void {\n if (output === null) return;\n setSearchLines(output.lines);\n openPalette('output');\n }\n\n function nextCorpus(current: Corpus): Corpus {\n const idx = CORPORA.indexOf(current);\n const candidates = [...CORPORA.slice(idx + 1), ...CORPORA.slice(0, idx + 1)];\n for (const corpus of candidates) {\n if (corpus === 'output' && output === null) continue; // no output to search\n return corpus;\n }\n return 'commands';\n }\n\n // ----- dashboard-mode keybinding handler -------------------------------\n function handleDashboardInput(input: string, key: Key): void {\n if (running) return;\n setNotice(null);\n\n if (key.return) {\n submit();\n return;\n }\n if (key.escape) {\n if (buffer.length > 0) clear();\n else if (output !== null) setOutput(null);\n else exit();\n return;\n }\n if (key.backspace || key.delete) {\n setBuffer((b) => b.slice(0, -1));\n return;\n }\n if (key.upArrow) {\n if (buffer === '/') {\n const entry = recall('up');\n if (entry !== null) {\n setBuffer(() => entry);\n return;\n }\n }\n setScrollOffset((o) => Math.max(0, o - 1));\n return;\n }\n if (key.downArrow) {\n if (buffer === '/') {\n const entry = recall('down');\n if (entry !== null) {\n setBuffer(() => entry);\n return;\n }\n }\n setScrollOffset((o) => o + 1);\n return;\n }\n if (key.ctrl) {\n if (input === 'k') {\n openPalette('commands');\n return;\n }\n if (input === 'f' && output !== null) {\n openOutputSearch();\n return;\n }\n return;\n }\n if (input.length === 1) {\n if (buffer.length === 0) {\n if (input === 'q') {\n exit();\n return;\n }\n if (input === '?') {\n openPalette('help');\n return;\n }\n if (input === 'h') {\n openPalette('commands');\n return;\n }\n }\n setBuffer((b) => b + input);\n return;\n }\n if (input.length > 1) {\n setBuffer((b) => b + input);\n }\n }\n\n // ----- palette-mode keybinding handler (single router) -------------------\n // Clamp the active cursor to the FULL row list. The renderer slides a\n // VISIBLE_ROWS window over the list, so the cursor (and Enter) can reach\n // every row, not just the first ten.\n function moveActive(delta: number): void {\n const max = Math.max(0, paletteRows.length - 1);\n setPaletteActive((a) => Math.min(max, Math.max(0, a + delta)));\n }\n\n function handlePaletteInput(input: string, key: Key): void {\n if (mode.kind !== 'palette') return;\n if (key.escape) {\n setPaletteQuery('');\n setPaletteActive(0);\n setMode({ kind: 'dashboard' });\n return;\n }\n if (key.tab) {\n setMode({ kind: 'palette', corpus: nextCorpus(mode.corpus) });\n setPaletteQuery('');\n setPaletteActive(0);\n return;\n }\n if (mode.corpus === 'help') {\n if (key.upArrow) moveActive(-1);\n else if (key.downArrow) moveActive(1);\n return; // help corpus takes no typing\n }\n if (key.return) {\n if (mode.corpus === 'commands') {\n const row = paletteRows[paletteActive];\n if (row?.argv) handleRun(row.argv, row.destructive);\n } else {\n moveActive(1); // output corpus: next match\n }\n return;\n }\n if (key.upArrow) {\n moveActive(-1);\n return;\n }\n if (key.downArrow) {\n moveActive(1);\n return;\n }\n if (key.backspace || key.delete) {\n setPaletteQuery((q) => q.slice(0, -1));\n setPaletteActive(0);\n return;\n }\n if (key.ctrl) return;\n if (input.length > 0) {\n setPaletteQuery((q) => q + input);\n setPaletteActive(0);\n }\n }\n\n // ----- confirm-mode keybinding handler ----------------------------------\n function handleConfirmInput(input: string, key: Key): void {\n if (key.escape || input === 'n' || input === 'N') {\n // Back to the palette with a clean query so the full list is visible.\n setPaletteQuery('');\n setPaletteActive(0);\n setMode({ kind: 'palette', corpus: 'commands' });\n return;\n }\n if (input === 'y' || input === 'Y') {\n if (mode.kind !== 'confirm') return;\n dispatchCmd([...mode.argv]);\n void recordRuns([...mode.argv]);\n pushHistory(`/${mode.argv.join(' ')}`);\n setMode({ kind: 'dashboard' });\n }\n }\n\n // ----- keybinding dispatcher (single `useInput` for the whole App) ------\n useInput((input, key) => {\n switch (mode.kind) {\n case 'dashboard':\n handleDashboardInput(input, key);\n return;\n case 'palette':\n handlePaletteInput(input, key);\n return;\n case 'confirm':\n handleConfirmInput(input, key);\n return;\n }\n });\n\n function submit(): void {\n const text = buffer;\n if (text.length === 0) return;\n clear();\n if (!text.startsWith('/')) {\n setNotice('type a /command to run it (bare text is just a hint)');\n return;\n }\n const argv = text\n .slice(1)\n .split(/\\s+/)\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n if (argv.length === 0) {\n setNotice('empty /command');\n return;\n }\n if (isDestructive(argv)) {\n // Destructive typed /commands go through the SAME confirm gate the\n // palette uses (v2 — closes the old typed-path bypass).\n setMode({ kind: 'confirm', argv });\n return;\n }\n pushHistory(text);\n dispatchCmd(argv);\n void recordRuns(argv);\n }\n\n // The rows for the active palette corpus, memoized so the dashboard hot path\n // (every keystroke + snapshot poll) does NOT rebuild ~60 row objects. Only\n // recompute when a palette input actually changes.\n const paletteRows = useMemo(\n () =>\n buildPaletteRows({\n corpus: mode.kind === 'palette' ? mode.corpus : 'commands',\n query: paletteQuery,\n commands: deps.commands ?? [],\n matcher: deps.matcher ?? handRolledMatcher,\n recent,\n homeSections,\n outputLines: searchLines,\n }),\n [mode, paletteQuery, deps.commands, deps.matcher, recent, homeSections, searchLines],\n );\n\n // ----- render -----------------------------------------------------------\n if (mode.kind === 'palette') {\n return (\n <Box flexDirection=\"column\">\n <Header tagline={`palette · ${mode.corpus}`} />\n <Palette\n corpus={mode.corpus}\n query={paletteQuery}\n active={paletteActive}\n rows={paletteRows}\n />\n <Footer running={false} />\n </Box>\n );\n }\n\n if (mode.kind === 'confirm') {\n return (\n <Box flexDirection=\"column\">\n <Header tagline=\"confirm\" />\n <ConfirmOverlay argv={mode.argv} />\n <Footer running={false} />\n </Box>\n );\n }\n\n const snapshotLines = formatStatusPayload(payload);\n const paneLines = output?.lines ?? snapshotLines ?? [];\n const paneTitle = output?.title ?? 'snapshot';\n\n return (\n <Box flexDirection=\"column\">\n <Header tagline=\"dashboard\" />\n <Panel>\n <Box paddingX={1}>\n <StatusBar payload={payload} loading={loading} />\n </Box>\n <Box paddingX={1}>\n <Text>{divider()}</Text>\n </Box>\n <Box flexDirection=\"column\" paddingX={1}>\n <OutputPane lines={paneLines} scrollOffset={scrollOffset} title={paneTitle} />\n </Box>\n </Panel>\n <CommandInput buffer={buffer} running={running} />\n {notice !== null ? <Text>{c.dim(notice)}</Text> : null}\n <Footer running={running} />\n </Box>\n );\n}\n","// Command input display. The input buffer + cursor live in the App (so is the\n// keybinding dispatch); this component is purely presentational — it renders\n// the prefix convention (`/` invokes a native Noir subcommand, bare text is a\n// hint) and shows a block cursor at the end of the buffer.\n//\n// The hint rule: when the buffer is empty, show a dim placeholder describing\n// the convention; once the user types, render the buffer verbatim with an\n// accent prefix when the first char is `/` (a recognized dispatch) or a dim\n// prefix otherwise (a hint that this input would NOT dispatch on Enter).\n//\n// The input is drawn inside a rounded border so the interactive field is\n// visually distinct from the static output above it (TUI redesign: rounded\n// outlines on every interactive surface).\n\nimport { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c, contentWidth } from '../theme.js';\n\ninterface CommandInputProps {\n buffer: string;\n running?: boolean;\n}\n\nexport function CommandInput({ buffer, running }: CommandInputProps): ReactElement {\n if (running) {\n return (\n <Box borderStyle=\"round\" borderColor=\"gray\" paddingX={1} width={contentWidth() + 4}>\n <Text>{c.dim('…')}</Text>\n </Box>\n );\n }\n let body: ReactElement;\n if (buffer.length === 0) {\n body = (\n <Text>\n <Text>{c.dim('▌')}</Text>{' '}\n <Text>{c.dim('type a /command (e.g. /status, /sync, /task next), or q to quit')}</Text>\n </Text>\n );\n } else {\n const isCommand = buffer.startsWith('/');\n body = (\n <Text>\n <Text>{isCommand ? c.accent('▸') : c.dim('▸')}</Text> {buffer}\n <Text>{c.dim('▌')}</Text>\n </Text>\n );\n }\n return (\n <Box borderStyle=\"round\" borderColor=\"gray\" paddingX={1} width={contentWidth() + 4}>\n {body}\n </Box>\n );\n}\n","// Stream capture for the dashboard's command dispatch.\n//\n// While the dashboard runs, Ink owns the terminal (raw-mode stdin + ANSI render\n// writes to stdout). When the user submits a `/<command>`, the EXISTING command\n// dispatch seam is reused verbatim — the dispatched command writes to\n// `process.stdout` / `process.stderr` via the same centralized output helpers\n// (`log`, `json`, `table`, `definitionList`, …) every other invocation uses.\n// To keep that contract AND render the result inside the dashboard's output\n// pane (instead of corrupting Ink's render), the dispatched command's writes\n// are captured for the duration of the dispatch and restored after.\n//\n// Invariant: Ink does not rerender mid-dispatch, because the App does not\n// `setState` between the stream swap and its restore (the only state change is\n// the single `setOutput` AFTER the await resolves). So the captured text is\n// exactly the dispatched command's bytes — no Ink frames leak in.\n\n/** Capture everything written to stdout/stderr during `fn`, plus the exit code. */\nexport interface CapturedOutput {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Run `fn` with `process.stdout.write` / `process.stderr.write` temporarily\n * replaced by string collectors. Restores the original writers in a `finally`\n * (so an unexpected throw still restores them). `process.exitCode` set by the\n * dispatched command is captured and then RESET to its pre-call value, so a\n * failing sub-command does not poison the dashboard's own process exit.\n */\nexport async function captureProcessOutput(fn: () => Promise<void>): Promise<CapturedOutput> {\n const outChunks: string[] = [];\n const errChunks: string[] = [];\n const origOut = process.stdout.write.bind(process.stdout);\n const origErr = process.stderr.write.bind(process.stderr);\n const prevExitCode = process.exitCode;\n\n // Type narrower: Node's Write result is `boolean`. The captured writers\n // always succeed (they just buffer), so return `true`.\n process.stdout.write = ((chunk: unknown) => {\n outChunks.push(typeof chunk === 'string' ? chunk : String(chunk));\n return true;\n }) as typeof process.stdout.write;\n process.stderr.write = ((chunk: unknown) => {\n errChunks.push(typeof chunk === 'string' ? chunk : String(chunk));\n return true;\n }) as typeof process.stderr.write;\n\n try {\n await fn();\n } finally {\n process.stdout.write = origOut;\n process.stderr.write = origErr;\n }\n\n const exitCode = typeof process.exitCode === 'number' ? process.exitCode : 0;\n // Reset so the dashboard's own process exit is not affected by a sub-command.\n process.exitCode = prevExitCode;\n return { stdout: outChunks.join(''), stderr: errChunks.join(''), exitCode };\n}\n","// Footer with the shortcut hints. Kept as a pure function of the running state\n// so the App can swap it without re-wiring keybindings. The hint copy lives in\n// `hints.ts` (single source of truth) — the bindings themselves live in App.tsx.\n\nimport { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c } from '../theme.js';\nimport { FOOTER_HINT, RUNNING_HINT } from './hints.js';\n\ninterface FooterProps {\n /** When true, a dispatched command is in flight — show the running hint. */\n running?: boolean;\n}\n\nexport function Footer({ running }: FooterProps): ReactElement {\n return <Text>{c.dim(running ? RUNNING_HINT : FOOTER_HINT)}</Text>;\n}\n","// Shared hint strings + the keybinding manifest (single source of truth).\n//\n// Before v2, the same \"navigate / run / close\" hint was copy-pasted into the\n// (now-retired) home menu and palette, and the `?` help screen re-listed every\n// binding as a second encoding. This module owns the hint copy once so the\n// footer, the palette footer line, and the `help` corpus all render the same\n// text and cannot drift from the actual bindings (which live in App.tsx).\n\n/** The dashboard's idle footer hint (the App's keybinding manifest as text). */\nexport const FOOTER_HINT =\n '?/h/Ctrl+K palette · Ctrl+F find · ↑/↓ scroll · Enter run · q/Esc quit · Ctrl+C exit';\n\n/** The hint shown while a dispatched command is in flight. */\nexport const RUNNING_HINT = 'running… (Ctrl+C to force exit)';\n\n/** The palette/home list footer hint (navigate · run · close). */\nexport const LIST_NAV_HINT = '↑/↓ navigate · Enter run · Esc close · Tab view';\n\n/** A single keybinding row for the `help` corpus. */\nexport interface HelpEntry {\n readonly keys: string;\n readonly desc: string;\n}\n\n/**\n * The keybinding manifest, rendered by the palette's `help` corpus. Keep this in\n * sync with the App's single `useInput` dispatcher (the same file that owns the\n * bindings) — a drift here is a doc bug, not a behavior bug.\n */\nexport const HELP_ENTRIES: readonly HelpEntry[] = [\n { keys: '/command', desc: 'run a Noir sub-command (e.g. /status, /sync, /task next)' },\n { keys: 'Enter', desc: 'run the typed /command' },\n { keys: 'Esc', desc: 'back: clear input → dismiss output → quit' },\n { keys: 'q', desc: 'quit (when the input is empty)' },\n { keys: '↑ / ↓', desc: 'scroll the output pane (dashboard) · navigate the list (palette)' },\n { keys: '?', desc: 'palette — help & keybindings (this list)' },\n { keys: 'h', desc: 'palette — commands (quick actions + all commands)' },\n { keys: 'Ctrl+K', desc: 'palette — all commands (fuzzy search)' },\n { keys: 'Ctrl+F', desc: 'palette — find in the dispatched output' },\n { keys: 'Tab', desc: 'switch between commands, output, and help' },\n { keys: 'y / n', desc: 'approve / decline a destructive command prompt' },\n { keys: 'Ctrl+C', desc: 'force exit' },\n];\n","// Format the live {@link StatusPayload} as plain text lines for the dashboard's\n// output pane. Reuses the SAME describe helpers `noir status` renders with, so\n// the dashboard's snapshot and `noir status`'s human table never drift apart.\n\nimport {\n describeContext,\n describeDaemon,\n describeMemory,\n describeStore,\n describeWorkflow,\n type StatusPayload,\n} from '../commands/status.js';\n\n/**\n * Render a status snapshot as `Label: value` lines. Returns `null` when the\n * payload is absent (the dashboard shows a placeholder in that case).\n */\nexport function formatStatusPayload(p: StatusPayload | null): string[] | null {\n if (p === null) return null;\n return [\n `Project: ${p.project.name} (${p.project.id})`,\n `Host: ${p.host}`,\n `Noir: ${p.noir}`,\n `Daemon: ${describeDaemon(p.daemon)}`,\n `Store: ${describeStore(p.store)}`,\n `Context: ${describeContext(p.context)}`,\n `Workflow: ${describeWorkflow(p.workflow)}`,\n `Memory: ${describeMemory(p.memory)}`,\n ];\n}\n","// Branded header. The diamond mark uses the Midnight Cobalt brand role\n// (`c.accent`, solid blue), which is the single-line form of the banner's\n// gradient — it strips under NO_COLOR and stays solid under NOIR_ACCESSIBLE.\n// The full block wordmark lives in the banner module; a dashboard header is a\n// one-line surface, so the compact mark is the right altitude.\n\nimport { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c } from '../theme.js';\n\nexport function Header({ tagline }: { tagline?: string }): ReactElement {\n return (\n <Text>\n {c.accent('◆')} {c.bold('noir')} <Text>{c.dim(tagline ?? 'dashboard')}</Text>\n </Text>\n );\n}\n","// B1 — the dashboard input-buffer hook. Extracted from `App.tsx` so the\n// command-history + recall-cursor state can live with the keybindings that use\n// it instead of dangling inside the App's single `useInput` closure. Owns:\n// - `buffer` — the current text being composed at the prompt.\n// - `history` — in-memory command history (newest first), with move-to-front\n// dedup (matches the persisted recents store).\n// - `recall` — Up/Down history walk via an index cursor (`-1` = not\n// recalling).\n// - `seed` — replace the session history with the persisted recents.\n//\n// Deliberately tiny + framework-shaped like the rest of the TUI state: `buffer`\n// is a React state string so keystrokes rerender the CommandInput, while\n// `history` + the cursor are refs (they are recalled on demand, never painted).\n//\n// IMPORTANT: every returned function is wrapped in `useCallback` so its identity\n// is STABLE across renders. The App passes `seed` into a `useEffect` dependency\n// array (`[deps.loadRecent, seed]`); a plain function re-created each render\n// would make that effect re-run every render — an infinite setState loop that\n// leaks memory and OOMs the TUI after ~a minute of idle. All these functions\n// only touch refs (+ the stable `setBuffer`), so `useCallback(…, [])` is safe.\n\nimport { useCallback, useRef, useState } from 'react';\n\n/** Cap on the in-memory session history (mirrors the persisted recents cap). */\nconst MAX_HISTORY = 50;\n\n/** The input-buffer + command-history surface the App's keybindings consume. */\nexport interface InputBuffer {\n /** The text currently being composed (never `undefined`). */\n readonly buffer: string;\n /** Apply an updater to the current buffer (React `useState` setter shape). */\n setBuffer: (updater: (prev: string) => string) => void;\n /** Record a submitted command into history (newest first, move-to-front dedup). */\n pushHistory: (text: string) => void;\n /**\n * Walk the command history from the recall cursor. `up` returns the next\n * older entry (or the newest when not recalling); `down` returns the next\n * newer entry. Either direction clamps at its boundary — past the newest it\n * returns `null` (back on the edit line).\n */\n recall: (dir: 'up' | 'down') => string | null;\n /** Empty the buffer and reset the recall cursor to \"not recalling\". */\n clear: () => void;\n /**\n * Replace the session history with the persisted recents (the source of\n * truth). Called once after `loadRecent` resolves so shell recall and the\n * palette recents read the same list instead of diverging.\n */\n seed: (entries: readonly string[]) => void;\n}\n\n/**\n * Mount the dashboard's input buffer + in-memory history. `clear` is what a\n * successful submit / an Escape-back invokes: it empties the buffer AND parks\n * the recall cursor so the next Up starts over from the newest entry, while\n * leaving `history` intact for the session.\n */\nexport function useInputBuffer(): InputBuffer {\n const [buffer, setBuffer] = useState('');\n // Newest command first. Refs, never state: history is only read on demand\n // by `recall`, so storing it in state would rerender the App for no benefit.\n const historyRef = useRef<string[]>([]);\n // Index into `historyRef.current`; -1 = not recalling (editing fresh text).\n const cursorRef = useRef(-1);\n\n const pushHistory = useCallback((text: string): void => {\n if (text.length === 0) return;\n // Move-to-front dedup (matches `recordRecent` in palette/history.ts): an\n // exact re-run moves the entry to the head instead of duplicating, so the\n // shell-recall overlay and the persisted recents apply one dedup rule.\n const history = historyRef.current.filter((e) => e !== text);\n // Cap at MAX_HISTORY so a long session running many distinct commands does\n // not grow the in-memory overlay without bound (the persisted store is\n // already capped at 50).\n historyRef.current = [text, ...history].slice(0, MAX_HISTORY);\n }, []);\n\n const recall = useCallback((dir: 'up' | 'down'): string | null => {\n const history = historyRef.current;\n if (history.length === 0) return null;\n let cursor = cursorRef.current;\n if (dir === 'up') {\n // -1 (edit line) steps to the newest; otherwise walk one older, clamped\n // at the oldest entry so Up can never walk off the front of the list.\n cursor = cursor === -1 ? 0 : Math.min(cursor + 1, history.length - 1);\n } else {\n // Walk newer; stepping past the newest returns to the edit line.\n cursor = cursor === -1 ? -1 : cursor - 1;\n }\n cursorRef.current = cursor;\n return cursor === -1 ? null : (history[cursor] ?? null);\n }, []);\n\n const clear = useCallback((): void => {\n setBuffer(() => '');\n cursorRef.current = -1;\n }, []);\n\n const seed = useCallback((entries: readonly string[]): void => {\n historyRef.current = [...entries];\n cursorRef.current = -1;\n }, []);\n\n return { buffer, setBuffer, pushHistory, recall, clear, seed };\n}\n","// Scrollable output pane. Renders a windowed slice of `lines` based on the\n// current scroll offset (controlled by ArrowUp/ArrowDown at the App level).\n// Two sources of text share this pane: (a) the live project snapshot (the same\n// payload `noir status` shows), and (b) the captured stdout/stderr of a\n// dispatched `/<command>`. When dispatched output is present it takes\n// precedence (the user just asked for it); the App returns to the snapshot view\n// when the output is dismissed.\n\nimport { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c, contentWidth } from '../theme.js';\n\ninterface OutputPaneProps {\n lines: readonly string[];\n /** Index of the FIRST line scrolled to the top of the viewport. */\n scrollOffset: number;\n /** Visible row height of the pane. Defaults to a comfortable 12 rows. */\n height?: number;\n /** Optional title for the pane (shown as a dim header line). */\n title?: string;\n}\n\ninterface Row {\n /** Stable, content-derived React key (deduplicated so duplicate lines don't collide). */\n key: string;\n /** The text to render for this row. */\n text: string;\n}\n\n/**\n * Build stable React keys from line content. Duplicate lines get a `#N` suffix\n * so each row's key is unique WITHOUT using the array index — the lint rule\n * flags index keys, and content keys are also correct here (the pane is read-\n * only, but content keys keep reconciliation honest across scroll refreshes).\n */\nfunction toRows(visible: readonly string[]): Row[] {\n const rows: Row[] = [];\n const seen = new Map<string, number>();\n for (let i = 0; i < visible.length; i++) {\n const text = visible[i] ?? '';\n const n = (seen.get(text) ?? 0) + 1;\n seen.set(text, n);\n rows.push({ key: n === 1 ? text || '_blank' : `${text || '_blank'}#${n}`, text });\n }\n return rows;\n}\n\nexport function OutputPane({\n lines,\n scrollOffset,\n height = 12,\n title,\n}: OutputPaneProps): ReactElement {\n // Content width already accounts for the parent panel's border + padding\n // (see contentWidth()). Truncating to this (not the full terminal width)\n // keeps long lines — like the `noir status` table — inside the rounded box.\n const width = contentWidth();\n\n if (lines.length === 0) {\n return <Text>{c.dim('(no output — type a /command, or ? for help)')}</Text>;\n }\n\n // Clamp the offset into range so a stale value (e.g. after the content\n // shrinks) never produces a blank pane.\n const maxOffset = Math.max(0, lines.length - height);\n const offset = Math.min(Math.max(0, scrollOffset), maxOffset);\n const rows = toRows(lines.slice(offset, offset + height));\n\n return (\n <>\n {title !== undefined ? <Text wrap=\"truncate-end\">{c.dim(`── ${title} ──`)}</Text> : null}\n {rows.map((row) => {\n const truncated =\n row.text.length > width ? `${row.text.slice(0, Math.max(1, width - 1))}…` : row.text;\n // wrap=\"truncate-end\" guarantees a long line never wraps inside the\n // bordered panel — the manual truncate above is the first line of\n // defense; this is the second (Ink will hard-truncate if the panel is\n // narrower than contentWidth() reported, e.g. under a tiny COLUMNS).\n return (\n <Text key={row.key} wrap=\"truncate-end\">\n {truncated}\n </Text>\n );\n })}\n </>\n );\n}\n","// C1 — in-TUI confirmation prompt for a destructive palette dispatch.\n//\n// Presentational only: it renders the `y/N` prompt for the argv the App wants\n// to approve. The App owns the input routing (its single `useInput` handles `y`\n// / `n` / Esc in confirm mode) and the dispatch seam, so this component has no\n// knowledge of what the command does or how to run it.\n//\n// Drawn inside a rounded, warn-colored border so a destructive gate is visually\n// unmistakable (TUI redesign: rounded outlines on every interactive surface).\n\nimport { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c } from '../../theme.js';\nimport { Panel } from '../Panel.js';\n\n/** The confirm overlay's props — supplied by the App. */\nexport interface ConfirmOverlayProps {\n /** The argv being approved, e.g. `['context','index']`. */\n readonly argv: readonly string[];\n}\n\n/**\n * Render the `y/N` confirmation prompt. Capital `N` marks the default (decline),\n * matching the CLI's prompt convention.\n */\nexport function ConfirmOverlay({ argv }: ConfirmOverlayProps): ReactElement {\n return (\n <Panel>\n <Box paddingX={1}>\n <Text>\n {c.warn(`! run /${argv.join(' ')}? (y/N)`)}\n <Text>{c.dim(' [y approve · n/Esc back to palette]')}</Text>\n </Text>\n </Box>\n </Panel>\n );\n}\n","// Shared container — every TUI surface uses this one component for rounded\n// borders + width accounting so no caller duplicates `borderStyle round` /\n// `borderColor gray` / `width` math.\n//\n// When `maxWidth` is set the panel caps at that column ceiling — used by the\n// command palette for the fixed-width overlay look (like Raycast / VS Code).\n// Defaults to full terminal width via contentWidth().\n\nimport { Box } from 'ink';\nimport type { ReactElement, ReactNode } from 'react';\nimport { contentWidth, useColor } from '../theme.js';\n\nexport interface PanelProps {\n readonly children: ReactNode;\n /** Optional max-width ceiling in columns (e.g. 64 for the command palette). */\n readonly maxWidth?: number;\n}\n\nexport function Panel({ children, maxWidth }: PanelProps): ReactElement {\n const full = contentWidth() + 4; // border (2) + inner padding (2)\n const width = maxWidth != null ? Math.min(full, maxWidth) : full;\n // The border color rides the SAME color authority as every other surface:\n // when colors are off (NO_COLOR / non-TTY), pass `undefined` so Ink renders\n // the border in the default terminal color with no ANSI — a hardcoded\n // borderColor=\"gray\" bypassed useColor() and left colored borders around\n // plain content (the documented NO_COLOR contract).\n const borderColor = useColor() ? ('gray' as const) : undefined;\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={borderColor}\n paddingX={1}\n width={width}\n >\n {children}\n </Box>\n );\n}\n","// Hand-rolled fuzzy scorer for the command palette. This is the swap seam:\n// `fuzzyScore` + `matchCommand` are pure functions a future fzf/ItsFuzzy-style\n// matcher can replace without touching the TUI; `FuzzyMatch` is the shared\n// result shape both the matcher and the rendering layer speak.\n//\n// Design notes:\n// - Subsequence matcher (not substring): \"sts\" matches \"status\" (s, t, s).\n// - Case-insensitive — both sides lowercased before comparison.\n// - Gap penalty (-0.1 per skipped char) makes tight matches beat scattered ones.\n// - Consecutive bonus (+8) rewards runs like a real fuzzy finder.\n// - Prefix bonus (+4 while text-index < query.length) makes a leading match\n// dominate a trailing one, so \"sta\" ranks above a \"status\"-tail hit.\n\nimport type { PaletteCommand } from './types.js';\n\n/** A scored palette hit returned by the matcher. */\nexport interface FuzzyMatch {\n item: PaletteCommand;\n score: number;\n /** Text indices of the best-scoring field that matched (for highlight). */\n matchedIndices: number[];\n}\n\n/** Field weights — label dominates so titles win ties over help text. */\nconst WEIGHTS = {\n label: 1.0,\n keyword: 0.7,\n description: 0.5,\n} as const;\n\n/** Flat bonus added to the label field so it wins cross-field ties. */\nconst LABEL_TIE_BONUS = 50;\n\n/**\n * Score `query` against `text` as a (case-insensitive) subsequence. Returns\n * `{score, indices}` when every query char is matched in order, else `null`.\n * An empty query scores 0 with no indices.\n */\nexport function fuzzyScore(\n query: string,\n text: string,\n): { score: number; indices: number[] } | null {\n if (query.length === 0) return { score: 0, indices: [] };\n\n const q = query.toLowerCase();\n const t = text.toLowerCase();\n\n const indices: number[] = [];\n let score = 0;\n let prevMatchedIndex = -1;\n\n let qi = 0;\n for (let ti = 0; ti < t.length && qi < q.length; ti++) {\n if (t[ti] === q[qi]) {\n indices.push(ti);\n // consecutive-match bonus: only when this char directly follows the\n // previous matched char (a real fuzzy-finder \"run\" reward).\n const consecutive = prevMatchedIndex !== -1 && ti === prevMatchedIndex + 1;\n if (consecutive) score += 8;\n if (ti < q.length) score += 4; // prefix-region bonus\n // gap penalty for chars skipped between matches\n if (prevMatchedIndex !== -1) {\n const skipped = ti - prevMatchedIndex - 1;\n if (skipped > 0) score -= 0.1 * skipped;\n }\n prevMatchedIndex = ti;\n qi++;\n }\n }\n\n if (qi < q.length) return null; // query not a full subsequence\n return { score, indices };\n}\n\n/**\n * Score a single command across its label / keywords / description, returning\n * the best field's weighted result (plus the flat label tie-break bonus so a\n * label hit always wins a tie against a keyword or description hit). Returns\n * `null` when the query matches no field.\n */\nexport function matchCommand(query: string, item: PaletteCommand): FuzzyMatch | null {\n if (query.length === 0) {\n return { item, score: 0, matchedIndices: [] };\n }\n\n // Track the best field hit. Kept as a mutable holder (rather than a\n // `| null` union) so the `consider` closure can read the running high score\n // without TypeScript narrowing it to `never` across the conditional updates.\n const best: { score: number; indices: number[] } = {\n score: Number.NEGATIVE_INFINITY,\n indices: [],\n };\n let matched = false;\n\n // Replace the running best if `candidate` scores higher.\n const consider = (score: number, indices: number[]): void => {\n if (!matched || score > best.score) {\n best.score = score;\n best.indices = indices;\n matched = true;\n }\n };\n\n // Label — weighted highest, plus the flat tie-break bonus.\n const labelHit = fuzzyScore(query, item.label);\n if (labelHit) {\n consider(labelHit.score * WEIGHTS.label + LABEL_TIE_BONUS, labelHit.indices);\n }\n\n // Keywords — each scored independently, best keyword wins the field.\n if (item.keywords) {\n let bestKw: { score: number; indices: number[] } | null = null;\n for (const kw of item.keywords) {\n const kwHit = fuzzyScore(query, kw);\n if (kwHit && (bestKw === null || kwHit.score > bestKw.score)) bestKw = kwHit;\n }\n if (bestKw) {\n consider(bestKw.score * WEIGHTS.keyword, bestKw.indices);\n }\n }\n\n // Description — lowest weight, last resort.\n if (item.description) {\n const descHit = fuzzyScore(query, item.description);\n if (descHit) {\n consider(descHit.score * WEIGHTS.description, descHit.indices);\n }\n }\n\n if (!matched) return null;\n return { item, score: best.score, matchedIndices: best.indices };\n}\n","// The matcher swap seam. `FuzzyMatcher` is the interface the TUI depends on;\n// `handRolledMatcher` is the default, dependency-free implementation built on\n// `matchCommand`. Swapping in a native fzf/ItsFuzzy binding later means shipping\n// a second `FuzzyMatcher` — the palette code never changes.\n\nimport { type FuzzyMatch, matchCommand } from './fuzzyMatch.js';\nimport type { PaletteCommand } from './types.js';\n\n/**\n * What the palette asks for: \"rank these items against this query, capped at\n * `limit`\". Implementations must be deterministic and side-effect free.\n */\nexport interface FuzzyMatcher {\n search(query: string, items: readonly PaletteCommand[], limit: number): FuzzyMatch[];\n}\n\n/**\n * Default matcher. Empty query returns every item (score 0, empty indices) so\n * the palette can render the full list before the user types; otherwise each\n * item is scored, filtered to matches, sorted by descending score, and sliced\n * to `limit`. Sort is stable (ties keep input order) via a Array.prototype.sort\n * that only reorders on a strict score difference.\n */\nexport const handRolledMatcher: FuzzyMatcher = {\n search(query: string, items: readonly PaletteCommand[], limit: number): FuzzyMatch[] {\n if (query.length === 0) {\n return items.slice(0, Math.max(0, limit)).map((item) => ({\n item,\n score: 0,\n matchedIndices: [],\n }));\n }\n\n const matched: FuzzyMatch[] = [];\n for (const item of items) {\n const m = matchCommand(query, item);\n if (m) matched.push(m);\n }\n\n matched.sort((a, b) => b.score - a.score);\n return matched.slice(0, Math.max(0, limit));\n },\n};\n","// The single command surface (v2) — presentational renderer.\n//\n// The App owns the query + active row + the single `useInput` dispatcher; this\n// component only paints the rows {@link buildPaletteRows} produced for the\n// active corpus (commands / output / help). No `useInput`, no dispatch, no\n// history — the App is the sole decision-maker, so home, palette, and typed\n// /command all funnel through one seam.\n//\n// Layout (two-column, per the v2 design): a fixed label column (bold) on the\n// left, a dim hint column on the right. For command rows the hint is the\n// command's argv (short + unambiguous); the FULL description is shown as a\n// wrapped detail line on the ACTIVE row only, so it is never truncated. The\n// active row is highlighted with reverse video (SGR 7) — visible on any\n// terminal, not just color ones.\n\nimport { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c } from '../../theme.js';\nimport { LIST_NAV_HINT } from '../hints.js';\nimport { Panel } from '../Panel.js';\nimport { type Corpus, type PaletteRow, VISIBLE_ROWS } from './rows.js';\n\nconst PALETTE_WIDTH = 64;\n/** Border + padding overhead between the panel width and a row's text budget:\n * round border (2) + Panel paddingX (2) + row paddingX (2) = 6. A row's\n * label(26) + hint must never exceed the remaining width or Ink wraps the\n * hint tail onto a flush-left second line (the v1.11.2 two-column bug). */\nconst ROW_TEXT_WIDTH = PALETTE_WIDTH - 6; // 58\n/** Fixed label column: the `▸ `/` ` prefix (2) + the label. */\nconst LABEL_WIDTH = 26;\n/** Max hint column width — derived from the row budget so the two-column row\n * can never overflow again (was a hardcoded 34, off by 2). */\nconst HINT_WIDTH = ROW_TEXT_WIDTH - LABEL_WIDTH; // 32\n\n/** Word-boundary wrap so a long description never truncates or overflows. */\nfunction wrap(text: string, width: number): string[] {\n const words = text.split(/\\s+/).filter((w) => w.length > 0);\n const lines: string[] = [];\n let cur = '';\n for (const w of words) {\n if (cur.length === 0) cur = w;\n else if (`${cur} ${w}`.length <= width) cur = `${cur} ${w}`;\n else {\n lines.push(cur);\n cur = w;\n }\n }\n if (cur) lines.push(cur);\n return lines;\n}\n\n/** Truncate a label to fit the fixed label column. */\nfunction truncateLabel(label: string): string {\n const max = LABEL_WIDTH - 2;\n return label.length > max ? `${label.slice(0, max - 1)}…` : label;\n}\n\n/** Placeholder hint for the query row, per corpus. */\nfunction placeholder(corpus: Corpus): string {\n if (corpus === 'commands') return 'type to search commands';\n if (corpus === 'output') return 'type to filter output';\n return 'keybindings';\n}\n\n/** The palette's props — supplied by the App (which owns input + dispatch). */\nexport interface PaletteProps {\n readonly corpus: Corpus;\n readonly query: string;\n readonly active: number;\n readonly rows: readonly PaletteRow[];\n}\n\n/**\n * Render the palette overlay: an input row + the corpus list. Presentational —\n * the App owns query/active/input and passes the rows in.\n */\nexport function Palette({ corpus, query, active, rows }: PaletteProps): ReactElement {\n const total = rows.length;\n // Sliding window: the cursor spans the FULL row list; slide a VISIBLE_ROWS\n // window so the active row is always on screen (sticking to the bottom once\n // scrolled). Without this, only the first 10 rows of a ~60-row command list\n // would be reachable.\n const scroll = Math.max(0, Math.min(active - (VISIBLE_ROWS - 1), total - VISIBLE_ROWS));\n const visible = rows.slice(scroll, scroll + VISIBLE_ROWS);\n const activeIndex = Math.min(Math.max(active - scroll, 0), Math.max(0, visible.length - 1));\n\n const elements: ReactElement[] = [\n <Box key=\"header\" paddingX={1}>\n <Text>\n {c.bold('▸ palette ')}\n <Text>{c.dim(`· ${corpus}`)}</Text>\n </Text>\n </Box>,\n <Box key=\"query\" paddingX={1}>\n <Text>\n {c.dim('> ')}\n {query.length > 0 ? query : c.dim(placeholder(corpus))}\n <Text>{c.dim('▌')}</Text>\n </Text>\n </Box>,\n ];\n\n let lastGroup: string | null = null;\n for (let i = 0; i < visible.length; i++) {\n const row = visible[i];\n if (!row) continue;\n if (row.group !== null && row.group !== lastGroup) {\n // A blank spacer before every section (except the first) so the group\n // headers read as distinct, scannable blocks rather than a wall of rows.\n if (lastGroup !== null) {\n elements.push(\n <Box key={`gap:${row.group}`}>\n <Text> </Text>\n </Box>,\n );\n }\n elements.push(\n <Box key={`cat:${row.group}`} paddingX={1}>\n <Text>{c.bold(`── ${row.group} ──`)}</Text>\n </Box>,\n );\n lastGroup = row.group;\n }\n const isActive = i === activeIndex;\n const prefix = isActive ? '▸ ' : ' ';\n const label = truncateLabel(row.primary);\n const labelCol = (prefix + label).padEnd(LABEL_WIDTH);\n // Hint (right column): the argv for command rows, prefixed with `/` so it\n // reads as \"the command that runs on Enter\" (matching the dashboard's\n // `/command` convention). Non-command rows use the secondary line. Clipped\n // to the hint column so a long keybinding description (help corpus) never\n // overflows the panel.\n const hintRaw = row.argv ? `/${row.argv.join(' ')}` : row.secondary;\n const hint = hintRaw.length > HINT_WIDTH ? `${hintRaw.slice(0, HINT_WIDTH - 1)}…` : hintRaw;\n\n if (isActive) {\n // Reverse video highlights the whole row — no inner color needed.\n elements.push(\n <Box key={row.key} paddingX={1}>\n <Text>{c.inverse(`${labelCol}${hint}`)}</Text>\n </Box>,\n );\n } else {\n elements.push(\n <Box key={row.key} paddingX={1}>\n <Text>\n {c.bold(labelCol)}\n {hint.length > 0 ? c.dim(hint) : null}\n </Text>\n </Box>,\n );\n }\n\n // Detail line: the FULL description on the active row, wrapped so it is\n // never truncated. Command rows always show it; help rows show it too\n // (matching command-row UX — the hint column only truncates). Output-corpus\n // rows are excluded: their secondary is a line number / empty-state text,\n // not a description. A leading `↳` marks the detail as belonging to the row\n // above (not a new selectable item); continuation lines align under the\n // text so they read as one wrapped block.\n if (isActive && row.secondary && (row.argv || corpus === 'help')) {\n const detailLines = wrap(row.secondary, ROW_TEXT_WIDTH - 4);\n for (let di = 0; di < detailLines.length; di++) {\n const line = detailLines[di] ?? '';\n const prefix = di === 0 ? ' ↳ ' : ' ';\n elements.push(\n <Box key={`${row.key}:d:${di}`} paddingX={1}>\n <Text>{c.dim(`${prefix}${line}`)}</Text>\n </Box>,\n );\n }\n }\n }\n\n return (\n <Box flexDirection=\"column\">\n <Panel maxWidth={PALETTE_WIDTH}>{elements}</Panel>\n <Text>{c.dim(LIST_NAV_HINT)}</Text>\n </Box>\n );\n}\n","// Output-search primitives (moved from the retired `overlays/SearchMode.ts`).\n//\n// Pure, framework-free functions the palette's `output` corpus consumes:\n// `computeMatches` turns a query into the line indices that contain it. The\n// `/` dispatch prefix makes '/' unusable as a search key, so Ctrl+F (or the\n// palette `Tab` switch) is the entry into this corpus.\n\n/**\n * Line indices whose (case-insensitive) text contains `query`, in line order.\n * An empty query matches nothing (`[]`) — the search only starts once the user\n * types, and an empty query must never be treated as \"matches every line\".\n */\nexport function computeMatches(lines: readonly string[], query: string): number[] {\n if (query.length === 0) return [];\n const needle = query.toLowerCase();\n const out: number[] = [];\n for (let i = 0; i < lines.length; i++) {\n if ((lines[i] ?? '').toLowerCase().includes(needle)) out.push(i);\n }\n return out;\n}\n","// Pure row-building for the single command surface (v2). The App owns the\n// query + active row + key routing; this module derives the visible rows for a\n// corpus so the App can resolve the active row into a dispatch (Enter) while\n// the presentational `Palette` renders the same rows — one source of truth for\n// what the palette shows and what a selection runs.\n\nimport type { HomeAction, HomeSection } from '../commands/sections.js';\nimport { HELP_ENTRIES } from '../hints.js';\nimport type { FuzzyMatcher } from './matcher.js';\nimport { computeMatches } from './output.js';\nimport type { PaletteCommand } from './types.js';\n\n/** The palette's active corpus. */\nexport type Corpus = 'commands' | 'output' | 'help';\n\n/** All corpora, in `Tab`-cycle order. */\nexport const CORPORA: readonly Corpus[] = ['commands', 'output', 'help'];\n\nconst MATCH_LIMIT = 20;\n\n/** How many rows the palette renders at once (the sliding window size). */\nexport const VISIBLE_ROWS = 10;\n\n/** A rendered palette row. `argv` is null for non-dispatchable rows. */\nexport interface PaletteRow {\n key: string;\n primary: string;\n secondary: string;\n argv: readonly string[] | null;\n destructive: boolean;\n group: string | null;\n}\n\n/** Inputs to {@link buildPaletteRows}. */\nexport interface BuildRowsInput {\n corpus: Corpus;\n query: string;\n commands: readonly PaletteCommand[];\n matcher: FuzzyMatcher;\n recent: readonly PaletteCommand[];\n homeSections: readonly HomeSection[];\n outputLines: readonly string[];\n}\n\n/** Flatten curated home sections into `{ group, action }` rows. */\nfunction flattenHome(sections: readonly HomeSection[]): { group: string; action: HomeAction }[] {\n const out: { group: string; action: HomeAction }[] = [];\n for (const section of sections) {\n for (const action of section.items) out.push({ group: section.label, action });\n }\n return out;\n}\n\n/** `recent` entries that still exist in `commands`, in recent order. */\nfunction recentDeduped(\n recent: readonly PaletteCommand[],\n commands: readonly PaletteCommand[],\n): PaletteCommand[] {\n const known = new Set(commands.map((cmd) => cmd.id));\n return recent.filter((cmd) => known.has(cmd.id));\n}\n\n/** Build the visible rows for the active corpus + query. */\nexport function buildPaletteRows(input: BuildRowsInput): PaletteRow[] {\n const { corpus, query, commands, matcher, recent, homeSections, outputLines } = input;\n\n if (corpus === 'help') {\n return HELP_ENTRIES.map((e) => ({\n key: `help:${e.keys}`,\n primary: e.keys,\n secondary: e.desc,\n argv: null,\n destructive: false,\n group: 'keybindings',\n }));\n }\n\n if (corpus === 'output') {\n const matches = computeMatches(outputLines, query);\n if (query.length > 0 && matches.length === 0) {\n // A typed filter with zero hits is an explicit empty state — never list\n // the full output under a misleading \"matches\" header.\n return [\n {\n key: 'empty',\n primary: '(no matches)',\n secondary: `nothing matches \"${query}\"`,\n argv: null,\n destructive: false,\n group: null,\n },\n ];\n }\n if (matches.length === 0) {\n if (outputLines.length === 0) {\n return [\n {\n key: 'empty',\n primary: '(no output)',\n secondary: 'run a /command first',\n argv: null,\n destructive: false,\n group: null,\n },\n ];\n }\n // Empty query: browse the full output.\n return outputLines.map((line, i) => ({\n key: `out:${i}`,\n primary: line,\n secondary: String(i + 1),\n argv: null,\n destructive: false,\n group: null,\n }));\n }\n return matches.map((i) => ({\n key: `out:${i}`,\n primary: outputLines[i] ?? '',\n secondary: String(i + 1),\n argv: null,\n destructive: false,\n group: 'matches',\n }));\n }\n\n // commands corpus.\n if (query.length === 0) {\n const rows: PaletteRow[] = [];\n const recents = recentDeduped(recent, commands);\n const recentIds = new Set(recents.map((r) => r.id));\n for (const cmd of recents) {\n rows.push({\n key: `recent:${cmd.id}`,\n primary: cmd.label,\n secondary: cmd.description,\n argv: cmd.argv,\n destructive: cmd.destructive,\n group: 'recent',\n });\n }\n const homeIds = new Set<string>();\n for (const { group, action } of flattenHome(homeSections)) {\n // Skip a curated action already shown in the recents section, so a command\n // run recently never appears twice (recents + its section) on one screen.\n if (recentIds.has(action.id)) continue;\n const argv = action.dispatch ?? [action.id];\n homeIds.add(action.id);\n rows.push({\n key: `home:${action.id}`,\n primary: action.label,\n secondary: action.hint,\n argv,\n destructive: action.destructive ?? false,\n group,\n });\n }\n for (const cmd of commands) {\n // Skip commands already shown as a recent or as a curated quick action —\n // the curated label wins over the raw leaf label (one row per command).\n if (recentIds.has(cmd.id) || homeIds.has(cmd.id)) continue;\n rows.push({\n key: `cmd:${cmd.id}`,\n primary: cmd.label,\n secondary: cmd.description,\n argv: cmd.argv,\n destructive: cmd.destructive,\n group: cmd.category,\n });\n }\n return rows;\n }\n\n // Non-empty query: fuzzy-rank the full command list.\n return matcher.search(query, commands, MATCH_LIMIT).map((m) => ({\n key: `cmd:${m.item.id}`,\n primary: m.item.label,\n secondary: m.item.description,\n argv: m.item.argv,\n destructive: m.item.destructive,\n group: null,\n }));\n}\n","// Top status bar. Renders a one-line summary of the live snapshot: host, mode\n// (from the active workflow task, if any), phase (or \"idle\"), and daemon\n// health. Degrades cleanly — when the payload is null (project uninitialized,\n// snapshot in flight, or the daemon fully down), every field shows a dash and\n// the daemon cell reads \"down\" so the user is never looking at empty space.\n\nimport { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { StatusPayload } from '../commands/status.js';\nimport { c } from '../theme.js';\n\ninterface StatusBarProps {\n payload: StatusPayload | null;\n /** When true, the snapshot is being (re)loaded — shown as a dim hint. */\n loading?: boolean;\n}\n\nfunction Cell({\n label,\n value,\n accent,\n}: {\n label: string;\n value: string;\n accent?: boolean;\n}): ReactElement {\n return (\n <Text>\n <Text>{c.dim(`${label}:`)}</Text> <Text>{accent ? c.accent(value) : value}</Text>\n </Text>\n );\n}\n\nexport function StatusBar({ payload, loading }: StatusBarProps): ReactElement {\n const host = payload?.host ?? '—';\n const mode = payload?.workflow?.mode ?? '—';\n const phase = payload?.workflow?.phase ?? (loading ? '…' : 'idle');\n const daemonRunning = payload?.daemon.running === true;\n const daemon = payload === null && loading ? '…' : daemonRunning ? 'up' : 'down';\n\n return (\n <Text>\n <Cell label=\"host\" value={host} accent />\n <Text>{c.dim(' · ')}</Text>\n <Cell label=\"mode\" value={mode} />\n <Text>{c.dim(' · ')}</Text>\n <Cell label=\"phase\" value={phase} />\n <Text>{c.dim(' · ')}</Text>\n <Text>\n {c.dim('daemon:')} {daemonRunning ? c.ok(daemon) : c.warn(daemon)}\n </Text>\n </Text>\n );\n}\n","// C3 — palette command-history persistence.\n//\n// The palette shows the user's recently-run commands above the full list on an\n// empty query. That recency data lives on disk so it survives sessions. The file\n// is keyed by canonical ProjectId — `~/.noir/<projectId>/tui-history.json` (NOT a\n// filesystem path) — so it respects the `.noir/` single-source-of-truth invariant\n// (CLAUDE.md): a project's recents are isolated from every other project. The\n// file is a simple newest-first JSON array of `{ argv, id }` (id is the\n// space-joined argv — the same stable dispatch key the registry uses).\n//\n// Opt-out: set `NOIR_DISABLE_TUI_HISTORY=1` to make persistence in-memory only\n// (loadRecent/recordRecent become no-ops). Useful for ephemeral/CI shells.\n//\n// Bounds: `MAX_ENTRIES` (50) caps the file so it never grows without bound, and\n// every write is atomic via the shared `atomicWriteFile` (@noir-ai/core — the\n// same helper daemon/lifecycle.ts and update-check.ts use) so a crash mid-write\n// never leaves a half-written JSON blob. A corrupt file (manual edit, truncated\n// write from an older build) degrades to `[]` rather than throwing — recency is\n// a nice-to-have, never a crash.\n//\n// Test seam: `__setNoirHome` overrides the home dir so the offline suite can\n// point at a tmpdir instead of the real `~/.noir` (mirrors `NOIR_DAEMON_JSON`).\n\nimport { existsSync, mkdirSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { atomicWriteFile } from '@noir-ai/core';\n\n/** A single entry in the persisted recent-commands list. */\nexport interface RecentEntry {\n /** The raw argv the user selected (e.g. `['context','search','foo']`). */\n readonly argv: readonly string[];\n /** Space-joined argv — the stable dispatch key (e.g. `context search foo`). */\n readonly id: string;\n}\n\n/** Bounded history size — the palette only needs a handful of recents. */\nconst MAX_ENTRIES = 50;\n\n/** The env var that disables palette-history persistence (in-memory only). */\nexport const TUI_HISTORY_DISABLE_ENV = 'NOIR_DISABLE_TUI_HISTORY';\n\nlet testHome: string | null = null;\n\n/**\n * Test override for the Noir home dir. Pass `null` to restore the real\n * `~/.noir` resolution. Never called from production code.\n */\nexport function __setNoirHome(home: string | null): void {\n testHome = home;\n}\n\n/** The user-global Noir home (test override wins when set). */\nfunction homeDir(): string {\n return testHome ?? join(homedir(), '.noir');\n}\n\n/**\n * Path of the palette history file for a project:\n * `<home>/<projectId>/tui-history.json`. ProjectId-keyed so recents are isolated\n * per project (respects the `.noir/` single-source-of-truth invariant).\n */\nfunction historyFile(projectId: string): string {\n return join(homeDir(), projectId, 'tui-history.json');\n}\n\n/**\n * Whether palette history persistence is enabled. Honors\n * `NOIR_DISABLE_TUI_HISTORY` (any non-empty value disables it → in-memory only).\n */\nexport function isHistoryEnabled(): boolean {\n const v = process.env[TUI_HISTORY_DISABLE_ENV];\n return v === undefined || v === '';\n}\n\n/**\n * Load the persisted recent commands for `projectId`, newest first. Returns `[]`\n * when persistence is disabled, the file is missing/empty/corrupt — recency is\n * best-effort and must never throw.\n */\nexport function loadRecent(projectId: string): RecentEntry[] {\n if (!isHistoryEnabled()) return [];\n const file = historyFile(projectId);\n try {\n if (!existsSync(file)) return [];\n const raw = readFileSync(file, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n const entries: RecentEntry[] = [];\n for (const item of parsed) {\n if (typeof item !== 'object' || item === null) continue;\n const argv = (item as { argv?: unknown }).argv;\n const id = (item as { id?: unknown }).id;\n if (!Array.isArray(argv) || typeof id !== 'string') continue;\n if (!argv.every((tok): tok is string => typeof tok === 'string')) continue;\n entries.push({ argv, id });\n }\n return entries;\n } catch {\n return [];\n }\n}\n\n/**\n * Record a dispatched command as the newest recent entry for `projectId`.\n * Adjacent duplicates are dropped (re-running the same command once is one\n * entry); re-running a command from deeper in the list moves it to the front.\n * The persisted list is capped at {@link MAX_ENTRIES}. No-op when persistence is\n * disabled. Best-effort: a failed write is swallowed (recency is a nice-to-have\n * — a read-only home or a full disk must not break command dispatch).\n */\nexport function recordRecent(projectId: string, argv: readonly string[]): void {\n if (argv.length === 0) return;\n if (!isHistoryEnabled()) return;\n const entry: RecentEntry = { argv, id: argv.join(' ') };\n const current = loadRecent(projectId).filter((e) => e.id !== entry.id);\n const next = [entry, ...current].slice(0, MAX_ENTRIES);\n try {\n const dir = join(homeDir(), projectId);\n mkdirSync(dir, { recursive: true });\n atomicWriteFile(historyFile(projectId), `${JSON.stringify(next, null, 2)}\\n`);\n } catch {\n // Recency is a nice-to-have — a read-only home (or a full disk) must not\n // break command dispatch.\n }\n}\n"],"mappings":"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","names":["Box","Text","useState","Text","jsx","Text","Text","jsx","jsxs","Text","Text","jsx","jsxs","Text","Box","Text","Box","jsx","Box","jsx","jsxs","Box","Text","Box","Text","jsx","jsxs","Box","Text","prefix","Text","jsx","jsxs","Text","jsx","jsxs","useState","c","Box","Text","jsx","c"]}
|
|
1
|
+
{"version":3,"sources":["../../src/tui/index.tsx","../../src/tui/App.tsx","../../src/tui/CommandInput.tsx","../../src/tui/capture.ts","../../src/tui/Footer.tsx","../../src/tui/hints.ts","../../src/tui/format.ts","../../src/tui/Header.tsx","../../src/tui/hooks/useInputBuffer.ts","../../src/tui/modes/run.tsx","../../src/tui/OutputPane.tsx","../../src/tui/overlays/PostRunOverlay.tsx","../../src/tui/Panel.tsx","../../src/tui/run-stream.ts","../../src/tui/StatusBar.tsx","../../src/tui/modes/transcripts.tsx","../../src/tui/overlays/ConfirmOverlay.tsx","../../src/tui/palette/fuzzyMatch.ts","../../src/tui/palette/matcher.ts","../../src/tui/palette/Palette.tsx","../../src/tui/palette/output.ts","../../src/tui/palette/rows.ts","../../src/tui/palette/history.ts","../../src/tui/transcripts.ts"],"sourcesContent":["// `noir tui` entry — mounts the Ink dashboard. LAZY-loaded: bin.ts does\n// `await import('./tui/index.js')` inside the `tui` action only, so React +\n// Ink never enter the main CLI startup path (`noir status`, `noir doctor`, a\n// bare `noir` all stay React-free). The entry wires the default\n// {@link TuiDeps.fetchStatus} (a wrapper around `gatherStatusPayload` that\n// folds any failure to `null`) and the SAME dispatch seam `home(opts, deps)`\n// uses, then renders the App and resolves on exit.\n//\n// Stream discipline: while the dashboard runs, Ink owns the terminal (raw-mode\n// stdin + ANSI render to stdout). On `q` / Esc / Ctrl+C, Ink restores the\n// terminal before unmounting. Dispatched commands write through the captured\n// stream shim, so the host terminal only ever sees Ink's frames.\n//\n// Ctrl+C is handled by the App rather than by Ink: with a host run in flight the\n// keystroke has to stop that child and name the transcript it left behind, and\n// Ink's own handling would unmount the frame from under it.\n\nimport { type HostId, SUPPORTED_HOSTS } from '@noir-ai/adapters';\nimport { loadProjectInfo, parseConfig } from '@noir-ai/core';\nimport { render } from 'ink';\nimport { buildPaletteCommandsForTui } from '../bin.js';\nimport { mergeEnv } from '../commands/run.js';\nimport { gatherStatusPayload, type StatusOptions, type StatusPayload } from '../commands/status.js';\nimport { runHost } from '../orchestrator.js';\nimport type { CliOptions } from '../output.js';\nimport { type PostRunActionInput, performPostRunAction, postRunActions } from '../run-actions.js';\nimport { loadRunConfig, resolveRunProfile } from '../run-profiles.js';\nimport { App, type TuiDeps } from './App.js';\nimport type { RunActionRequest, RunDeps, RunSession, RunStartHandlers } from './modes/run.js';\nimport { loadRecent, recordRecent } from './palette/history.js';\nimport { createTranscriptStore } from './transcripts.js';\n\nexport type { TuiDeps } from './App.js';\n\n/**\n * The host the dashboard drives. `noir tui` carries no host flag, so it drives\n * the same default `noir run` does; a `run.profiles` default (or `NOIR_PROFILE`)\n * can still point it at another binary.\n */\nconst DEFAULT_RUN_HOST: HostId = 'claude';\n\n/**\n * Default status fetcher: wraps {@link gatherStatusPayload} so any failure (an\n * uninitialized project, a probe hiccup, a daemon-down that threw) folds to\n * `null` and the dashboard degrades cleanly. `gatherStatusPayload` itself is\n * probe-only and never auto-starts a daemon, so this is safe to poll.\n */\nfunction defaultFetchStatus(opts: CliOptions): () => Promise<StatusPayload | null> {\n return async () => {\n try {\n return await gatherStatusPayload(opts as StatusOptions);\n } catch {\n return null;\n }\n };\n}\n\n/**\n * Resolve the profile, spawn the host, hand back the session handle. The\n * spawn is built here rather than in the run screen so that screen stays a\n * renderer: it is handed a `start` and only knows how to draw what comes back.\n *\n * Synchronous by design — the caller renders the pane it just asked for — so a\n * failure to even resolve a spawn (an unknown profile, an unusable host) is\n * thrown rather than exiting the process: the screen reports it in place\n * instead of the dashboard dying at the first keystroke. A continuation\n * appends `--resume <id>` after the profile's own args, so the profile's flags\n * stay in effect on the resumed turn too.\n */\nfunction startHostRun(\n prompt: string,\n handlers: RunStartHandlers,\n resumeSessionId?: string,\n): RunSession {\n const host = DEFAULT_RUN_HOST;\n if (!(SUPPORTED_HOSTS as readonly string[]).includes(host)) {\n throw new Error(`unknown host '${host}' (supported: ${SUPPORTED_HOSTS.join(', ')})`);\n }\n const profile = resolveProfile();\n const extraArgs =\n resumeSessionId === undefined\n ? profile.args\n : [...(profile.args ?? []), '--resume', resumeSessionId];\n const env = profile.env === undefined ? undefined : mergeEnv(process.env, profile.env);\n const binary = profile.binary !== undefined && profile.binary.length > 0 ? profile.binary : host;\n return {\n binary,\n done: runHost({\n host,\n prompt,\n ...(profile.binary === undefined ? {} : { customBinary: profile.binary }),\n ...(extraArgs === undefined ? {} : { extraArgs }),\n ...(env === undefined ? {} : { env }),\n onLine: handlers.onLine,\n onEvent: handlers.onEvent,\n signal: handlers.signal,\n ...(handlers.onChild === undefined ? {} : { onChild: handlers.onChild }),\n }),\n };\n}\n\n/**\n * The effective run profile. Config load is best-effort, exactly as it is for\n * `noir run`: outside an initialized project there are no profiles and the\n * built-in host behavior stands. A profile that cannot be used is reported as\n * a plain error, because the caller is a screen that shows it, not a CLI that\n * exits on it.\n */\nfunction resolveProfile(): {\n readonly binary?: string;\n readonly env?: Record<string, string | undefined>;\n readonly args?: readonly string[];\n} {\n const config = loadRunConfig(process.cwd()) ?? parseConfig({});\n const resolved = resolveRunProfile(undefined, config, process.env);\n if (!resolved.ok) throw new Error(resolved.message);\n return resolved.profile;\n}\n\n/**\n * The run seams the dashboard uses: the same spawn, the same post-run action\n * set, and the same transcript directory a shell invocation reaches. The action\n * set and its performer are the shared definitions the terminal prompt offers —\n * the overlay decides how a row is drawn, never what a choice does.\n */\nfunction defaultRunDeps(opts: CliOptions): RunDeps {\n const toInput = (request: RunActionRequest): PostRunActionInput => ({\n answer: request.answer,\n transcript: request.transcript,\n host: request.host,\n opts,\n ...(request.sessionId === undefined ? {} : { sessionId: request.sessionId }),\n ...(request.canContinue === undefined ? {} : { canContinue: request.canContinue }),\n });\n return {\n start: (prompt, handlers, resumeSessionId) => startHostRun(prompt, handlers, resumeSessionId),\n actions: (request) => postRunActions(toInput(request)),\n perform: (request, choice, value) => performPostRunAction(choice, toInput(request), value),\n // Named by host, exactly as the CLI names its own transcripts, so a run\n // started from the dashboard lands beside one started from a shell.\n transcripts: createTranscriptStore({ host: DEFAULT_RUN_HOST }),\n };\n}\n\n/**\n * Mount the dashboard and resolve when the user exits. The caller (bin.ts)\n * supplies the dispatch seam — the same shape `home(opts, deps).dispatch`\n * uses — so command routing is owned by the bin, not reimplemented here.\n */\nexport async function runTui(opts: CliOptions, dispatch: TuiDeps['dispatch']): Promise<void> {\n process.stdout.write('\\x1b[2J\\x1b[H');\n const deps = await buildTuiDeps(opts, dispatch);\n const instance = render(<App deps={deps} />, { exitOnCtrlC: false });\n await instance.waitUntilExit();\n}\n\n/**\n * `noir palette` — mount the App palette-first. Reuses the SAME\n * `runTui` deps (dispatch seam, commands, recents) so the fuzzy command\n * palette is identical to the dashboard's Ctrl+K palette, just opened\n * directly. `dispatch` is the same shape `runTui` receives from bin.ts.\n */\nexport async function runPalette(opts: CliOptions, dispatch: TuiDeps['dispatch']): Promise<void> {\n process.stdout.write('\\x1b[2J\\x1b[H');\n // Reuse runTui's deps wiring wholesale, then render palette-first.\n // Factor the shared deps build out of runTui into a helper to avoid\n // duplicating the projectId/commands/recents logic.\n const deps = await buildTuiDeps(opts, dispatch);\n const instance = render(\n <App deps={deps} initialMode={{ kind: 'palette', corpus: 'commands' }} />,\n {\n exitOnCtrlC: false,\n },\n );\n await instance.waitUntilExit();\n}\n\n/**\n * Build the shared {@link TuiDeps} for both `runTui` and `runPalette`.\n * Extracted so the two entry points cannot drift (projectId-keyed recents,\n * the palette source, and the dispatch seam are identical).\n */\nasync function buildTuiDeps(opts: CliOptions, dispatch: TuiDeps['dispatch']): Promise<TuiDeps> {\n // ProjectId-keyed recent-commands persistence: resolve the canonical id\n // once at launch so recents are isolated per project (respects the .noir/\n // single-source-of-truth invariant). An uninitialized project (loadProjectInfo\n // throws) degrades to empty recents — the palette still works with the full\n // command list.\n let projectId: string | null = null;\n try {\n projectId = loadProjectInfo(process.cwd()).id;\n } catch {\n projectId = null;\n }\n const deps: TuiDeps = {\n dispatch,\n fetchStatus: defaultFetchStatus(opts),\n // The live host-run screen. Present here (not optional in practice) is what\n // routes a `run <prompt>` selection to the screen instead of dispatching it\n // through the captured-command path — see `enterRunOrDispatch` in App.tsx.\n run: defaultRunDeps(opts),\n // The palette source, derived from a fresh commander program at launch.\n commands: buildPaletteCommandsForTui(),\n // Persistent recent commands (projectId-keyed). recordRecent is async\n // only to match the TuiDeps seam; it never rejects.\n record: (argv) => {\n if (projectId) recordRecent(projectId, argv);\n return Promise.resolve();\n },\n loadRecent: async () => {\n if (!projectId) return [];\n // Hydrate the bare {argv,id} entries against the live palette commands so\n // the palette renders real labels/descriptions; drop stale entries whose\n // argv no longer exists in the current build.\n const byId = new Map(deps.commands?.map((c) => [c.id, c]) ?? []);\n return loadRecent(projectId)\n .map((e) => byId.get(e.id))\n .filter((c): c is NonNullable<typeof c> => c !== undefined);\n },\n };\n return deps;\n}\n","// Dashboard root component. Owns the interactive state (input buffer, scroll\n// offset, dispatched output, snapshot, palette query/active, the active screen)\n// and the SINGLE keybinding dispatcher (v2 — the palette, home, help, and search\n// surfaces all collapsed into one corpus-aware palette, so keyboard routing\n// lives here alone). The snapshot is fetched via {@link TuiDeps.fetchStatus} on\n// a short interval (paused while a `/<command>` is dispatching, so a StatusBar\n// refresh can never race the stream capture). Dispatch goes through the EXISTING\n// {@link TuiDeps.dispatch} seam — the same `home(opts, deps).dispatch` shape —\n// so command routing is NOT reimplemented here.\n//\n// Screens (the {@link Mode} union): `dashboard` (default), `palette` (the single\n// command surface — corpus `commands` | `output` | `help`, opened by Ctrl+K /\n// Ctrl+F / h / ? and cycled with Tab), `confirm` (the `y/N` gate before a\n// destructive dispatch — now covering EVERY dispatch path, typed /command\n// included), `run` (the live host-run screen) and `transcripts` (the read-only\n// transcript picker). Every selection funnels through {@link handleRun}, so\n// destructive commands always pause at the confirm overlay — and the two\n// self-contained screens own their own keyboards rather than routing keys here.\n\nimport { Box, type Key, Text, useApp, useInput } from 'ink';\nimport { type ReactElement, useEffect, useMemo, useState } from 'react';\nimport type { StatusPayload } from '../commands/status.js';\nimport { c, divider } from '../theme.js';\nimport { CommandInput } from './CommandInput.js';\nimport { captureProcessOutput } from './capture.js';\nimport { isDestructive, runPromptFrom } from './commands/registry.js';\nimport { type HomeSection, resolveSections } from './commands/sections.js';\nimport { Footer } from './Footer.js';\nimport { formatStatusPayload } from './format.js';\nimport { Header } from './Header.js';\nimport { useInputBuffer } from './hooks/useInputBuffer.js';\nimport { type RunDeps, RunMode } from './modes/run.js';\nimport { TranscriptPicker } from './modes/transcripts.js';\nimport { OutputPane } from './OutputPane.js';\nimport { ConfirmOverlay } from './overlays/ConfirmOverlay.js';\nimport { Panel } from './Panel.js';\nimport { type FuzzyMatcher, handRolledMatcher } from './palette/matcher.js';\nimport { Palette } from './palette/Palette.js';\nimport { buildPaletteRows, CORPORA, type Corpus } from './palette/rows.js';\nimport type { PaletteCommand } from './palette/types.js';\nimport { StatusBar } from './StatusBar.js';\n\n/** Injected dependencies (mirrors the `home(opts, deps)` seam). */\nexport interface TuiDeps {\n /**\n * Run a Noir sub-command by user-form argv on a fresh commander program.\n * Same contract as {@link HomeDeps.dispatch}: must not throw — it owns its\n * own error → exit-code mapping and leaves the outcome on `process.exitCode`.\n */\n dispatch: (argv: readonly string[]) => Promise<void>;\n /**\n * Fetch the live status snapshot. Defaults to a wrapper around\n * `gatherStatusPayload` that folds ANY failure to `null` so the dashboard\n * degrades cleanly (uninitialized project, daemon down, probe hiccup).\n */\n fetchStatus: () => Promise<StatusPayload | null>;\n /**\n * The palette source — one {@link PaletteCommand} per leaf `noir` subcommand,\n * derived from the commander tree at `noir tui` launch (NOT re-walked per\n * keystroke). Optional for backward-compat (the unit tests omit it); when\n * absent the palette falls back to an empty command list.\n */\n readonly commands?: readonly PaletteCommand[];\n /** The fuzzy matcher. Optional — defaults to {@link handRolledMatcher}. */\n readonly matcher?: FuzzyMatcher;\n /**\n * Record a dispatched command into the persisted recent-commands list.\n * Optional for backward-compat — when absent, recents are not recorded.\n */\n record?: (argv: readonly string[]) => Promise<void>;\n /**\n * Load the persisted recent commands (newest first) for the palette's recent\n * section. Optional — when absent, defaults to the on-disk history loader.\n */\n loadRecent?: () => Promise<readonly PaletteCommand[]>;\n /**\n * The host-run seams (spawn, post-run actions, transcripts). Optional: with\n * it absent a `run` command dispatches like any other and its output is\n * captured into the pane, which is what every other caller and every older\n * test expects.\n */\n run?: RunDeps;\n}\n\n/** Optional initial state for tests (the live entry leaves these at defaults). */\nexport interface AppProps {\n deps: TuiDeps;\n /** Initial snapshot — skips the first fetch when provided (tests). */\n initialPayload?: StatusPayload | null;\n /** Refresh interval in ms (default 5000). Set large in tests. */\n refreshMs?: number;\n /**\n * The mode the App starts in. Defaults to the dashboard. `noir palette`\n * mounts the app palette-first via `{ kind: 'palette', corpus: 'commands' }`.\n */\n initialMode?: Mode;\n}\n\n/**\n * The dashboard's discriminated screen union. Exactly one screen is active at a\n * time; the App keys its render + keybinding handler off `mode.kind`.\n * - `dashboard` — the default screen: live snapshot, command input, output pane.\n * - `palette` — the single command surface, with a `corpus` selecting what the\n * query filters (commands / output / help). `collecting` is set while the\n * selected command's argument is being typed.\n * - `confirm` — an in-TUI confirmation prompt for a destructive dispatch.\n */\nexport type Mode =\n | { kind: 'dashboard' }\n | { kind: 'palette'; corpus: Corpus; collecting?: ArgCollection }\n | { kind: 'confirm'; argv: string[] }\n | { kind: 'run'; prompt: string }\n | { kind: 'transcripts' };\n\n/**\n * A command waiting for its argument: the argv to dispatch once one is typed,\n * the label naming that value, and whether the dispatch then needs confirming.\n */\ninterface ArgCollection {\n readonly argv: string[];\n readonly label: string;\n readonly destructive: boolean;\n}\n\ninterface DispatchedOutput {\n title: string;\n lines: string[];\n}\n\nexport function App({\n deps,\n initialPayload = null,\n refreshMs = 5000,\n initialMode = { kind: 'dashboard' },\n}: AppProps): ReactElement {\n const { exit } = useApp();\n const { buffer, setBuffer, pushHistory, recall, clear, seed } = useInputBuffer();\n const [mode, setMode] = useState<Mode>(initialMode);\n const [payload, setPayload] = useState<StatusPayload | null>(initialPayload);\n const [loading, setLoading] = useState(initialPayload === null);\n const [output, setOutput] = useState<DispatchedOutput | null>(null);\n // The output lines the `output` corpus searches. Captured from the CURRENT\n // dispatched output when the corpus opens, so a new dispatch landing mid-search\n // cannot change the corpus mid-filter.\n const [searchLines, setSearchLines] = useState<readonly string[]>([]);\n const [scrollOffset, setScrollOffset] = useState(0);\n const [running, setRunning] = useState(false);\n const [notice, setNotice] = useState<string | null>(null);\n // The argv of a dispatch the user just submitted. An effect watches this so\n // the dispatch runs AFTER React flushes the \"running…\" frame.\n const [pending, setPending] = useState<string[] | null>(null);\n // The persisted recent-commands list for the palette, loaded once on mount.\n const [recent, setRecent] = useState<readonly PaletteCommand[]>([]);\n // The curated home sections, resolved against the live registry once.\n const [homeSections, setHomeSections] = useState<readonly HomeSection[]>([]);\n // v2 — the single palette's query + active row (owned here so the App's one\n // `useInput` routes palette keys and can resolve the active row on Enter).\n const [paletteQuery, setPaletteQuery] = useState('');\n const [paletteActive, setPaletteActive] = useState(0);\n // The argument being typed for the selected command (only meaningful while\n // the palette is collecting one), plus the one-line notice shown when the\n // user submits an empty one.\n const [argBuffer, setArgBuffer] = useState('');\n const [argNotice, setArgNotice] = useState<string | null>(null);\n\n // ----- load the palette's recent commands once on mount ------------------\n useEffect(() => {\n if (!deps.loadRecent) return;\n const loader = deps.loadRecent;\n let cancelled = false;\n void loader()\n .then((entries) => {\n if (!cancelled) {\n setRecent(entries);\n // Seed the shell-recall overlay from the SAME persisted recents the\n // palette shows, so ↑/↓ recall and the palette recents never diverge.\n seed(entries.map((c) => `/${c.argv.join(' ')}`));\n }\n })\n .catch(() => {\n // Recents are a nice-to-have — a read failure leaves the full list.\n });\n return () => {\n cancelled = true;\n };\n }, [deps.loadRecent, seed]);\n\n // ----- resolve the curated home sections once ---------------------------\n useEffect(() => {\n let cancelled = false;\n void resolveSections(deps.commands ?? []).then((sections) => {\n if (!cancelled) setHomeSections(sections);\n });\n return () => {\n cancelled = true;\n };\n }, [deps.commands]);\n\n // ----- snapshot refresh: paused while a dispatch is in flight ----------\n useEffect(() => {\n // Pause polling while a dispatch runs OR the palette/confirm overlay is\n // open (the dashboard snapshot is off-screen then, so polling only wastes\n // a fetch + a re-render).\n if (running || mode.kind !== 'dashboard') return;\n let cancelled = false;\n let inFlight = false;\n const fetched = (p: StatusPayload | null): void => {\n inFlight = false;\n if (cancelled) return;\n setPayload(p);\n setLoading(false);\n };\n setLoading(true);\n inFlight = true;\n void deps\n .fetchStatus()\n .then(fetched)\n .catch(() => fetched(null));\n const id = setInterval(() => {\n // Skip the tick while a fetch is still pending — a slow/restarting daemon\n // (gatherStatusPayload does a probe + MCP round-trips) must not stack\n // concurrent fetches.\n if (inFlight) return;\n inFlight = true;\n void deps\n .fetchStatus()\n .then(fetched)\n .catch(() => {\n // Keep the last-good payload on a transient probe/daemon failure — a\n // poll-time hiccup must not wipe the dashboard to \"down\".\n inFlight = false;\n });\n }, refreshMs);\n return () => {\n cancelled = true;\n clearInterval(id);\n };\n }, [running, mode.kind, refreshMs, deps.fetchStatus]);\n\n // ----- dispatch runner: fires after the \"running\" frame has flushed ------\n useEffect(() => {\n if (pending === null) return;\n const argv = pending;\n let active = true;\n const run = async (): Promise<void> => {\n const captured = await captureProcessOutput(() => deps.dispatch(argv));\n if (!active) return;\n const combined = `${captured.stdout}${captured.stderr}`;\n const lines = combined.split('\\n');\n while (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();\n const title = `/${argv.join(' ')} → exit ${captured.exitCode}`;\n setOutput({ title, lines });\n setNotice(`ran /${argv.join(' ')} → exit ${captured.exitCode}`);\n setRunning(false);\n setPending(null);\n setScrollOffset(0);\n };\n void run();\n return () => {\n active = false;\n };\n }, [pending, deps.dispatch]);\n\n // ----- shared dispatch helper --------------------------------------------\n function dispatchCmd(argv: readonly string[]): void {\n setOutput(null);\n setRunning(true);\n setPending([...argv]);\n }\n\n // ----- unified run seam: every selection (palette / home / confirm) ------\n function handleRun(argv: readonly string[], destructive: boolean): void {\n if (destructive) {\n setMode({ kind: 'confirm', argv: [...argv] });\n return;\n }\n enterRunOrDispatch(argv);\n }\n\n /**\n * The `run <prompt>` command has two homes. When the run seams are wired, it\n * opens the live run screen (the host streams into the pane). Otherwise — and\n * for every other command, and for a `run` that carries flags — it is an\n * ordinary dispatch whose output is captured into the pane.\n */\n function enterRunOrDispatch(argv: readonly string[]): void {\n const prompt = deps.run === undefined ? null : runPromptFrom(argv);\n if (prompt !== null) {\n setMode({ kind: 'run', prompt });\n return;\n }\n dispatchCmd([...argv]);\n void recordRuns([...argv]);\n pushHistory(`/${argv.join(' ')}`);\n setMode({ kind: 'dashboard' });\n }\n\n function recordRuns(argv: readonly string[]): void {\n const recorder = deps.record;\n if (recorder) void recorder(argv);\n }\n\n // ----- palette open / cycle helpers --------------------------------------\n function openPalette(corpus: Corpus): void {\n setPaletteQuery('');\n setPaletteActive(0);\n setMode({ kind: 'palette', corpus });\n setNotice(null);\n }\n\n function openOutputSearch(): void {\n if (output === null) return;\n setSearchLines(output.lines);\n openPalette('output');\n }\n\n function nextCorpus(current: Corpus): Corpus {\n const idx = CORPORA.indexOf(current);\n const candidates = [...CORPORA.slice(idx + 1), ...CORPORA.slice(0, idx + 1)];\n for (const corpus of candidates) {\n if (corpus === 'output' && output === null) continue; // no output to search\n return corpus;\n }\n return 'commands';\n }\n\n // ----- dashboard-mode keybinding handler -------------------------------\n function handleDashboardInput(input: string, key: Key): void {\n if (running) return;\n setNotice(null);\n\n if (key.return) {\n submit();\n return;\n }\n if (key.escape) {\n if (buffer.length > 0) clear();\n else if (output !== null) setOutput(null);\n else exit();\n return;\n }\n if (key.backspace || key.delete) {\n setBuffer((b) => b.slice(0, -1));\n return;\n }\n if (key.upArrow) {\n if (buffer === '/') {\n const entry = recall('up');\n if (entry !== null) {\n setBuffer(() => entry);\n return;\n }\n }\n setScrollOffset((o) => Math.max(0, o - 1));\n return;\n }\n if (key.downArrow) {\n if (buffer === '/') {\n const entry = recall('down');\n if (entry !== null) {\n setBuffer(() => entry);\n return;\n }\n }\n setScrollOffset((o) => o + 1);\n return;\n }\n if (key.ctrl) {\n if (input === 'k') {\n openPalette('commands');\n return;\n }\n if (input === 'f' && output !== null) {\n openOutputSearch();\n return;\n }\n if (input === 't' && deps.run?.transcripts !== undefined) {\n setMode({ kind: 'transcripts' });\n return;\n }\n return;\n }\n if (input.length === 1) {\n if (buffer.length === 0) {\n if (input === 'q') {\n exit();\n return;\n }\n if (input === '?') {\n openPalette('help');\n return;\n }\n if (input === 'h') {\n openPalette('commands');\n return;\n }\n }\n setBuffer((b) => b + input);\n return;\n }\n if (input.length > 1) {\n setBuffer((b) => b + input);\n }\n }\n\n // ----- palette-mode keybinding handler (single router) -------------------\n // Clamp the active cursor to the FULL row list. The renderer slides a\n // VISIBLE_ROWS window over the list, so the cursor (and Enter) can reach\n // every row, not just the first ten.\n function moveActive(delta: number): void {\n const max = Math.max(0, paletteRows.length - 1);\n setPaletteActive((a) => Math.min(max, Math.max(0, a + delta)));\n }\n\n function handlePaletteInput(input: string, key: Key): void {\n if (mode.kind !== 'palette') return;\n if (mode.collecting !== undefined) {\n handleArgInput(input, key, mode.collecting);\n return;\n }\n if (key.escape) {\n setPaletteQuery('');\n setPaletteActive(0);\n setMode({ kind: 'dashboard' });\n return;\n }\n if (key.tab) {\n setMode({ kind: 'palette', corpus: nextCorpus(mode.corpus) });\n setPaletteQuery('');\n setPaletteActive(0);\n return;\n }\n if (mode.corpus === 'help') {\n if (key.upArrow) moveActive(-1);\n else if (key.downArrow) moveActive(1);\n return; // help corpus takes no typing\n }\n if (key.return) {\n if (mode.corpus === 'commands') {\n const row = paletteRows[paletteActive];\n if (!row?.argv) return;\n if (row.needsArg !== undefined) {\n // The command cannot run bare — collect its argument on the input\n // line before anything dispatches.\n setArgBuffer('');\n setArgNotice(null);\n setMode({\n kind: 'palette',\n corpus: mode.corpus,\n collecting: {\n argv: [...row.argv],\n label: row.needsArg,\n destructive: row.destructive,\n },\n });\n return;\n }\n handleRun(row.argv, row.destructive);\n } else {\n moveActive(1); // output corpus: next match\n }\n return;\n }\n if (key.upArrow) {\n moveActive(-1);\n return;\n }\n if (key.downArrow) {\n moveActive(1);\n return;\n }\n if (key.backspace || key.delete) {\n setPaletteQuery((q) => q.slice(0, -1));\n setPaletteActive(0);\n return;\n }\n if (key.ctrl) return;\n if (input.length > 0) {\n setPaletteQuery((q) => q + input);\n setPaletteActive(0);\n }\n }\n\n // ----- argument-collection keybinding handler ---------------------------\n // The input line now carries the command's argument: Enter dispatches the\n // command with it appended (through the same confirm gate as any other\n // selection), and Esc drops back to the filter with the query untouched.\n function handleArgInput(input: string, key: Key, collecting: ArgCollection): void {\n if (mode.kind !== 'palette') return;\n const corpus = mode.corpus;\n if (key.escape) {\n setArgBuffer('');\n setArgNotice(null);\n setMode({ kind: 'palette', corpus });\n return;\n }\n if (key.return) {\n const value = argBuffer.trim();\n if (value.length === 0) {\n // An empty value would dispatch the same bare argv the palette cannot\n // run — hold the step open and say why, so Enter is never a silent\n // no-op.\n setArgNotice(`a ${collecting.label} is required`);\n return;\n }\n setArgBuffer('');\n setArgNotice(null);\n handleRun([...collecting.argv, value], collecting.destructive);\n return;\n }\n if (key.backspace || key.delete) {\n setArgBuffer((b) => b.slice(0, -1));\n setArgNotice(null);\n return;\n }\n // Tab cycles the corpus in the filter; here it would only add a stray\n // character to the argument.\n if (key.ctrl || key.tab) return;\n if (input.length > 0) {\n setArgBuffer((b) => b + input);\n setArgNotice(null);\n }\n }\n\n // ----- confirm-mode keybinding handler ----------------------------------\n function handleConfirmInput(input: string, key: Key): void {\n if (key.escape || input === 'n' || input === 'N') {\n // Back to the palette with a clean query so the full list is visible.\n setPaletteQuery('');\n setPaletteActive(0);\n setMode({ kind: 'palette', corpus: 'commands' });\n return;\n }\n if (input === 'y' || input === 'Y') {\n if (mode.kind !== 'confirm') return;\n // The confirmed argv goes through the same run-or-dispatch split as an\n // unconfirmed one: `y` approves the run, it does not choose how it runs.\n enterRunOrDispatch(mode.argv);\n }\n }\n\n // ----- keybinding dispatcher (single `useInput` for the whole App) ------\n useInput((input, key) => {\n // Ctrl+C leaves the dashboard, as it always has — except while a host is\n // live, where the run screen takes it as \"stop the host\" (the same thing Esc\n // means there). Ink's own Ctrl+C exit is turned off at the entry point so\n // this is the one place that decides: a keystroke can never tear the frame\n // down from under a screen that still has a child process to stop.\n if (key.ctrl && input === 'c') {\n if (mode.kind !== 'run') exit();\n return;\n }\n switch (mode.kind) {\n case 'dashboard':\n handleDashboardInput(input, key);\n return;\n case 'palette':\n handlePaletteInput(input, key);\n return;\n case 'confirm':\n handleConfirmInput(input, key);\n return;\n case 'run':\n case 'transcripts':\n // These screens own their keyboard: the run screen takes Esc to cancel\n // a host, the picker takes arrows to browse. Routing their keys here as\n // well would give one keystroke two meanings.\n return;\n }\n });\n\n function submit(): void {\n const text = buffer;\n if (text.length === 0) return;\n clear();\n if (!text.startsWith('/')) {\n setNotice('type a /command to run it (bare text is just a hint)');\n return;\n }\n const argv = text\n .slice(1)\n .split(/\\s+/)\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n if (argv.length === 0) {\n setNotice('empty /command');\n return;\n }\n if (isDestructive(argv)) {\n // Destructive typed /commands go through the SAME confirm gate the\n // palette uses (v2 — closes the old typed-path bypass).\n setMode({ kind: 'confirm', argv });\n return;\n }\n pushHistory(text);\n dispatchCmd(argv);\n void recordRuns(argv);\n }\n\n // The rows for the active palette corpus, memoized so the dashboard hot path\n // (every keystroke + snapshot poll) does NOT rebuild ~60 row objects. Only\n // recompute when a palette input actually changes.\n const paletteRows = useMemo(\n () =>\n buildPaletteRows({\n corpus: mode.kind === 'palette' ? mode.corpus : 'commands',\n query: paletteQuery,\n commands: deps.commands ?? [],\n matcher: deps.matcher ?? handRolledMatcher,\n recent,\n homeSections,\n outputLines: searchLines,\n }),\n [mode, paletteQuery, deps.commands, deps.matcher, recent, homeSections, searchLines],\n );\n\n // ----- render -----------------------------------------------------------\n if (mode.kind === 'palette') {\n const collecting = mode.collecting;\n return (\n <Box flexDirection=\"column\">\n <Header tagline={`palette · ${mode.corpus}`} />\n <Palette\n corpus={mode.corpus}\n query={paletteQuery}\n active={paletteActive}\n rows={paletteRows}\n arg={\n collecting === undefined\n ? undefined\n : {\n value: argBuffer,\n placeholder: `${collecting.label} for ${collecting.argv.join(' ')}…`,\n hint: argNotice ?? undefined,\n }\n }\n />\n <Footer running={false} />\n </Box>\n );\n }\n\n if (mode.kind === 'confirm') {\n return (\n <Box flexDirection=\"column\">\n <Header tagline=\"confirm\" />\n <ConfirmOverlay argv={mode.argv} />\n <Footer running={false} />\n </Box>\n );\n }\n\n if (mode.kind === 'run' && deps.run !== undefined) {\n return (\n <RunMode\n prompt={mode.prompt}\n deps={deps.run}\n onQuit={exit}\n onExit={(notice) => {\n if (notice !== undefined) setNotice(notice);\n setMode({ kind: 'dashboard' });\n }}\n />\n );\n }\n\n const transcripts = deps.run?.transcripts;\n if (mode.kind === 'transcripts' && transcripts !== undefined) {\n return (\n <TranscriptPicker transcripts={transcripts} onExit={() => setMode({ kind: 'dashboard' })} />\n );\n }\n\n const snapshotLines = formatStatusPayload(payload);\n const paneLines = output?.lines ?? snapshotLines ?? [];\n const paneTitle = output?.title ?? 'snapshot';\n\n return (\n <Box flexDirection=\"column\">\n <Header tagline=\"dashboard\" />\n <Panel>\n <Box paddingX={1}>\n <StatusBar payload={payload} loading={loading} />\n </Box>\n <Box paddingX={1}>\n <Text>{divider()}</Text>\n </Box>\n <Box flexDirection=\"column\" paddingX={1}>\n <OutputPane lines={paneLines} scrollOffset={scrollOffset} title={paneTitle} />\n </Box>\n </Panel>\n <CommandInput buffer={buffer} running={running} />\n {notice !== null ? <Text>{c.dim(notice)}</Text> : null}\n <Footer running={running} />\n </Box>\n );\n}\n","// Command input display. The input buffer + cursor live in the App (so is the\n// keybinding dispatch); this component is purely presentational — it renders\n// the prefix convention (`/` invokes a native Noir subcommand, bare text is a\n// hint) and shows a block cursor at the end of the buffer.\n//\n// The hint rule: when the buffer is empty, show a dim placeholder describing\n// the convention; once the user types, render the buffer verbatim with an\n// accent prefix when the first char is `/` (a recognized dispatch) or a dim\n// prefix otherwise (a hint that this input would NOT dispatch on Enter).\n//\n// The input is drawn inside a rounded border so the interactive field is\n// visually distinct from the static output above it (TUI redesign: rounded\n// outlines on every interactive surface).\n\nimport { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c, contentWidth } from '../theme.js';\n\ninterface CommandInputProps {\n buffer: string;\n running?: boolean;\n}\n\nexport function CommandInput({ buffer, running }: CommandInputProps): ReactElement {\n if (running) {\n return (\n <Box borderStyle=\"round\" borderColor=\"gray\" paddingX={1} width={contentWidth() + 4}>\n <Text>{c.dim('…')}</Text>\n </Box>\n );\n }\n let body: ReactElement;\n if (buffer.length === 0) {\n body = (\n <Text>\n <Text>{c.dim('▌')}</Text>{' '}\n <Text>{c.dim('type a /command (e.g. /status, /sync, /task next), or q to quit')}</Text>\n </Text>\n );\n } else {\n const isCommand = buffer.startsWith('/');\n body = (\n <Text>\n <Text>{isCommand ? c.accent('▸') : c.dim('▸')}</Text> {buffer}\n <Text>{c.dim('▌')}</Text>\n </Text>\n );\n }\n return (\n <Box borderStyle=\"round\" borderColor=\"gray\" paddingX={1} width={contentWidth() + 4}>\n {body}\n </Box>\n );\n}\n","// Stream capture for the dashboard's command dispatch.\n//\n// While the dashboard runs, Ink owns the terminal (raw-mode stdin + ANSI render\n// writes to stdout). When the user submits a `/<command>`, the EXISTING command\n// dispatch seam is reused verbatim — the dispatched command writes to\n// `process.stdout` / `process.stderr` via the same centralized output helpers\n// (`log`, `json`, `table`, `definitionList`, …) every other invocation uses.\n// To keep that contract AND render the result inside the dashboard's output\n// pane (instead of corrupting Ink's render), the dispatched command's writes\n// are captured for the duration of the dispatch and restored after.\n//\n// Invariant: Ink does not rerender mid-dispatch, because the App does not\n// `setState` between the stream swap and its restore (the only state change is\n// the single `setOutput` AFTER the await resolves). So the captured text is\n// exactly the dispatched command's bytes — no Ink frames leak in.\n\n/** Capture everything written to stdout/stderr during `fn`, plus the exit code. */\nexport interface CapturedOutput {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\n/**\n * Run `fn` with `process.stdout.write` / `process.stderr.write` temporarily\n * replaced by string collectors. Restores the original writers in a `finally`\n * (so an unexpected throw still restores them). `process.exitCode` set by the\n * dispatched command is captured and then RESET to its pre-call value, so a\n * failing sub-command does not poison the dashboard's own process exit.\n */\nexport async function captureProcessOutput(fn: () => Promise<void>): Promise<CapturedOutput> {\n const outChunks: string[] = [];\n const errChunks: string[] = [];\n const origOut = process.stdout.write.bind(process.stdout);\n const origErr = process.stderr.write.bind(process.stderr);\n const prevExitCode = process.exitCode;\n\n // Type narrower: Node's Write result is `boolean`. The captured writers\n // always succeed (they just buffer), so return `true`.\n process.stdout.write = ((chunk: unknown) => {\n outChunks.push(typeof chunk === 'string' ? chunk : String(chunk));\n return true;\n }) as typeof process.stdout.write;\n process.stderr.write = ((chunk: unknown) => {\n errChunks.push(typeof chunk === 'string' ? chunk : String(chunk));\n return true;\n }) as typeof process.stderr.write;\n\n try {\n await fn();\n } finally {\n process.stdout.write = origOut;\n process.stderr.write = origErr;\n }\n\n const exitCode = typeof process.exitCode === 'number' ? process.exitCode : 0;\n // Reset so the dashboard's own process exit is not affected by a sub-command.\n process.exitCode = prevExitCode;\n return { stdout: outChunks.join(''), stderr: errChunks.join(''), exitCode };\n}\n","// Footer with the shortcut hints. Kept as a pure function of the running state\n// so the App can swap it without re-wiring keybindings. The hint copy lives in\n// `hints.ts` (single source of truth) — the bindings themselves live in App.tsx.\n\nimport { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c } from '../theme.js';\nimport { FOOTER_HINT, RUNNING_HINT } from './hints.js';\n\ninterface FooterProps {\n /** When true, a dispatched command is in flight — show the running hint. */\n running?: boolean;\n}\n\nexport function Footer({ running }: FooterProps): ReactElement {\n return <Text>{c.dim(running ? RUNNING_HINT : FOOTER_HINT)}</Text>;\n}\n","// Shared hint strings + the keybinding manifest (single source of truth).\n//\n// Before v2, the same \"navigate / run / close\" hint was copy-pasted into the\n// (now-retired) home menu and palette, and the `?` help screen re-listed every\n// binding as a second encoding. This module owns the hint copy once so the\n// footer, the palette footer line, and the `help` corpus all render the same\n// text and cannot drift from the actual bindings (which live in App.tsx).\n\n/** The dashboard's idle footer hint (the App's keybinding manifest as text). */\nexport const FOOTER_HINT =\n '?/h/Ctrl+K palette · Ctrl+F find · Ctrl+T transcripts · ↑/↓ scroll · Enter run · q/Esc quit · Ctrl+C exit';\n\n/** The hint shown while a dispatched command is in flight. */\nexport const RUNNING_HINT = 'running… (Ctrl+C to force exit)';\n\n/** The palette/home list footer hint (navigate · run · close). */\nexport const LIST_NAV_HINT = '↑/↓ navigate · Enter run · Esc close · Tab view';\n\n/** The run screen's hint while the host is streaming. */\nexport const RUN_HINT = 'streaming — Esc cancels the host';\n\n/** The run screen's hint once Esc has asked the host to stop. */\nexport const RUN_CANCEL_HINT = 'cancelling…';\n\n/** The transcript picker's list footer hint. */\nexport const TRANSCRIPT_LIST_HINT = '↑/↓ choose · Enter open · Esc back';\n\n/** The transcript picker's reader footer hint. */\nexport const TRANSCRIPT_READ_HINT = '↑/↓ scroll · Esc back to list · read-only';\n\n/** A single keybinding row for the `help` corpus. */\nexport interface HelpEntry {\n readonly keys: string;\n readonly desc: string;\n}\n\n/**\n * The keybinding manifest, rendered by the palette's `help` corpus. Keep this in\n * sync with the App's single `useInput` dispatcher (the same file that owns the\n * bindings) — a drift here is a doc bug, not a behavior bug.\n */\nexport const HELP_ENTRIES: readonly HelpEntry[] = [\n { keys: '/command', desc: 'run a Noir sub-command (e.g. /status, /sync, /task next)' },\n { keys: 'Enter', desc: 'run the typed /command' },\n { keys: 'Esc', desc: 'back: clear input → dismiss output → quit' },\n { keys: 'q', desc: 'quit (when the input is empty)' },\n { keys: '↑ / ↓', desc: 'scroll the output pane (dashboard) · navigate the list (palette)' },\n { keys: '?', desc: 'palette — help & keybindings (this list)' },\n { keys: 'h', desc: 'palette — commands (quick actions + all commands)' },\n { keys: 'Ctrl+K', desc: 'palette — all commands (fuzzy search)' },\n { keys: 'Ctrl+F', desc: 'palette — find in the dispatched output' },\n { keys: 'Ctrl+T', desc: 'transcripts — reopen a past run, read-only' },\n { keys: 'Tab', desc: 'switch between commands, output, and help' },\n { keys: 'y / n', desc: 'approve / decline a destructive command prompt' },\n { keys: 'Esc (in a run)', desc: 'cancel the host and return to the dashboard' },\n { keys: 'Ctrl+C', desc: 'force exit' },\n];\n","// Format the live {@link StatusPayload} as plain text lines for the dashboard's\n// output pane. Reuses the SAME describe helpers `noir status` renders with, so\n// the dashboard's snapshot and `noir status`'s human table never drift apart.\n\nimport {\n describeContext,\n describeDaemon,\n describeMemory,\n describeStore,\n describeWorkflow,\n type StatusPayload,\n} from '../commands/status.js';\n\n/**\n * Render a status snapshot as `Label: value` lines. Returns `null` when the\n * payload is absent (the dashboard shows a placeholder in that case).\n */\nexport function formatStatusPayload(p: StatusPayload | null): string[] | null {\n if (p === null) return null;\n return [\n `Project: ${p.project.name} (${p.project.id})`,\n `Host: ${p.host}`,\n `Noir: ${p.noir}`,\n `Daemon: ${describeDaemon(p.daemon)}`,\n `Store: ${describeStore(p.store)}`,\n `Context: ${describeContext(p.context)}`,\n `Workflow: ${describeWorkflow(p.workflow)}`,\n `Memory: ${describeMemory(p.memory)}`,\n ];\n}\n","// Branded header. The diamond mark uses the Midnight Cobalt brand role\n// (`c.accent`, solid blue), which is the single-line form of the banner's\n// gradient — it strips under NO_COLOR and stays solid under NOIR_ACCESSIBLE.\n// The full block wordmark lives in the banner module; a dashboard header is a\n// one-line surface, so the compact mark is the right altitude.\n\nimport { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c } from '../theme.js';\n\nexport function Header({ tagline }: { tagline?: string }): ReactElement {\n return (\n <Text>\n {c.accent('◆')} {c.bold('noir')} <Text>{c.dim(tagline ?? 'dashboard')}</Text>\n </Text>\n );\n}\n","// The dashboard input-buffer hook. Extracted from `App.tsx` so the\n// command-history + recall-cursor state can live with the keybindings that use\n// it instead of dangling inside the App's single `useInput` closure. Owns:\n// - `buffer` — the current text being composed at the prompt.\n// - `history` — in-memory command history (newest first), with move-to-front\n// dedup (matches the persisted recents store).\n// - `recall` — Up/Down history walk via an index cursor (`-1` = not\n// recalling).\n// - `seed` — replace the session history with the persisted recents.\n//\n// Deliberately tiny + framework-shaped like the rest of the TUI state: `buffer`\n// is a React state string so keystrokes rerender the CommandInput, while\n// `history` + the cursor are refs (they are recalled on demand, never painted).\n//\n// IMPORTANT: every returned function is wrapped in `useCallback` so its identity\n// is STABLE across renders. The App passes `seed` into a `useEffect` dependency\n// array (`[deps.loadRecent, seed]`); a plain function re-created each render\n// would make that effect re-run every render — an infinite setState loop that\n// leaks memory and OOMs the TUI after ~a minute of idle. All these functions\n// only touch refs (+ the stable `setBuffer`), so `useCallback(…, [])` is safe.\n\nimport { useCallback, useRef, useState } from 'react';\n\n/** Cap on the in-memory session history (mirrors the persisted recents cap). */\nconst MAX_HISTORY = 50;\n\n/** The input-buffer + command-history surface the App's keybindings consume. */\nexport interface InputBuffer {\n /** The text currently being composed (never `undefined`). */\n readonly buffer: string;\n /** Apply an updater to the current buffer (React `useState` setter shape). */\n setBuffer: (updater: (prev: string) => string) => void;\n /** Record a submitted command into history (newest first, move-to-front dedup). */\n pushHistory: (text: string) => void;\n /**\n * Walk the command history from the recall cursor. `up` returns the next\n * older entry (or the newest when not recalling); `down` returns the next\n * newer entry. Either direction clamps at its boundary — past the newest it\n * returns `null` (back on the edit line).\n */\n recall: (dir: 'up' | 'down') => string | null;\n /** Empty the buffer and reset the recall cursor to \"not recalling\". */\n clear: () => void;\n /**\n * Replace the session history with the persisted recents (the source of\n * truth). Called once after `loadRecent` resolves so shell recall and the\n * palette recents read the same list instead of diverging.\n */\n seed: (entries: readonly string[]) => void;\n}\n\n/**\n * Mount the dashboard's input buffer + in-memory history. `clear` is what a\n * successful submit / an Escape-back invokes: it empties the buffer AND parks\n * the recall cursor so the next Up starts over from the newest entry, while\n * leaving `history` intact for the session.\n */\nexport function useInputBuffer(): InputBuffer {\n const [buffer, setBuffer] = useState('');\n // Newest command first. Refs, never state: history is only read on demand\n // by `recall`, so storing it in state would rerender the App for no benefit.\n const historyRef = useRef<string[]>([]);\n // Index into `historyRef.current`; -1 = not recalling (editing fresh text).\n const cursorRef = useRef(-1);\n\n const pushHistory = useCallback((text: string): void => {\n if (text.length === 0) return;\n // Move-to-front dedup (matches `recordRecent` in palette/history.ts): an\n // exact re-run moves the entry to the head instead of duplicating, so the\n // shell-recall overlay and the persisted recents apply one dedup rule.\n const history = historyRef.current.filter((e) => e !== text);\n // Cap at MAX_HISTORY so a long session running many distinct commands does\n // not grow the in-memory overlay without bound (the persisted store is\n // already capped at 50).\n historyRef.current = [text, ...history].slice(0, MAX_HISTORY);\n }, []);\n\n const recall = useCallback((dir: 'up' | 'down'): string | null => {\n const history = historyRef.current;\n if (history.length === 0) return null;\n let cursor = cursorRef.current;\n if (dir === 'up') {\n // -1 (edit line) steps to the newest; otherwise walk one older, clamped\n // at the oldest entry so Up can never walk off the front of the list.\n cursor = cursor === -1 ? 0 : Math.min(cursor + 1, history.length - 1);\n } else {\n // Walk newer; stepping past the newest returns to the edit line.\n cursor = cursor === -1 ? -1 : cursor - 1;\n }\n cursorRef.current = cursor;\n return cursor === -1 ? null : (history[cursor] ?? null);\n }, []);\n\n const clear = useCallback((): void => {\n setBuffer(() => '');\n cursorRef.current = -1;\n }, []);\n\n const seed = useCallback((entries: readonly string[]): void => {\n historyRef.current = [...entries];\n cursorRef.current = -1;\n }, []);\n\n return { buffer, setBuffer, pushHistory, recall, clear, seed };\n}\n","// The run screen: drive the host and watch it work.\n//\n// This is the TUI's own host-driven mode. It does NOT dispatch a command and\n// capture its output — it spawns through the injected run seam, folds the\n// host's normalized events into state, and lets Ink render frames normally.\n// That distinction is load-bearing: the dispatch path swaps\n// `process.stdout.write` for a collector, and Ink draws by calling that same\n// function, so a captured run would swallow its own frames. Host output here is\n// data, never a write.\n//\n// Everything the screen shows is derived from one place: the run's stream\n// state, the clock, and the phase. The phase is a small machine — a host is\n// either running, asking what to do with an answer, waiting for the value an\n// action needs, working on it, or finished. Esc means \"get me out of here\" at\n// every step: it stops the host (asking first, forcing only if the host will\n// not listen), backs out of a value, and closes the screen once there is\n// nothing left to wait for. Ctrl+C means the same while a host is live — the\n// dashboard turns Ink's own Ctrl+C exit off so this screen can stop the child\n// and name the transcript, instead of vanishing mid-run and leaving a host\n// behind. A cancelled run does not wait for a second keystroke — it leaves as\n// soon as the host has actually stopped.\n\nimport { Box, Text, useInput } from 'ink';\nimport { type ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { HostChild, NoirEvent, RunHostResult } from '../../orchestrator.js';\nimport type { PostRunAction, PostRunMenuOption, PostRunOutcome } from '../../run-actions.js';\nimport { INTERRUPT_GRACE_MS, interruptedNotice, RunInterrupt } from '../../run-interrupt.js';\nimport { humanizeElapsed } from '../../run-status.js';\nimport { c, divider } from '../../theme.js';\nimport { captureProcessOutput } from '../capture.js';\nimport { Header } from '../Header.js';\nimport { RUN_CANCEL_HINT, RUN_HINT } from '../hints.js';\nimport { OutputPane } from '../OutputPane.js';\nimport { PostRunOverlay, type PostRunValueStep } from '../overlays/PostRunOverlay.js';\nimport { Panel } from '../Panel.js';\nimport { RunStream, type RunStreamSnapshot } from '../run-stream.js';\nimport { StatusBar } from '../StatusBar.js';\nimport type { TranscriptStore } from '../transcripts.js';\n\n/** What the run seam is handed while it works. */\nexport interface RunStartHandlers {\n /** One normalized host event, as it arrives. */\n readonly onEvent: (event: NoirEvent) => void;\n /** One raw stream-json line, for the transcript. */\n readonly onLine: (line: string) => void;\n /** Aborting this asks the host to stop; the run still resolves normally. */\n readonly signal: AbortSignal;\n /**\n * The host child, as soon as it exists — and again for every later spawn of\n * the same run. What a cancel kills: a signal aimed at Noir is not addressed\n * to the child, and one the host ignores needs a forceful follow-up.\n */\n readonly onChild?: (child: HostChild) => void;\n}\n\n/** A run in progress: the binary being driven, and how it ends. */\nexport interface RunSession {\n /** The binary being driven — what the failure text and the status line name. */\n readonly binary: string;\n readonly done: Promise<RunHostResult>;\n}\n\n/**\n * Start a host run for `prompt`. The implementation owns profile resolution\n * (which binary, which env, which extra args) and the spawn; the screen owns\n * the rendering. `resumeSessionId` continues a session an earlier run started.\n */\nexport type RunStarter = (\n prompt: string,\n handlers: RunStartHandlers,\n resumeSessionId?: string,\n) => RunSession;\n\n/** What a finished run hands the post-run action set. */\nexport interface RunActionRequest {\n readonly answer: string;\n readonly transcript: string;\n readonly sessionId?: string;\n readonly host: string;\n /** Whether a continuation is on offer (the host reported a session id). */\n readonly canContinue?: boolean;\n}\n\n/** The action seams the run screen needs, injected so no test spawns a host. */\nexport interface RunDeps {\n readonly start: RunStarter;\n /** The rows to offer for a finished run (the shared action set). */\n readonly actions: (input: RunActionRequest) => readonly PostRunMenuOption[];\n /** Do one chosen action. Never throws. */\n readonly perform: (\n input: RunActionRequest,\n choice: PostRunAction,\n value?: string,\n ) => Promise<PostRunOutcome>;\n /** Where this run's raw stream is persisted, and where old ones are listed. */\n readonly transcripts?: TranscriptStore;\n}\n\nexport interface RunModeProps {\n readonly prompt: string;\n readonly deps: RunDeps;\n /** Leave the run screen. `notice` is reported on the dashboard's notice line. */\n readonly onExit: (notice?: string) => void;\n /**\n * Leave the dashboard entirely. What Ctrl+C means here when there is no live\n * host to stop; injected rather than reached for so a test can watch it.\n */\n readonly onQuit?: () => void;\n /** Visible pane height. */\n readonly height?: number;\n /** Elapsed-time redraw interval in ms. */\n readonly tickMs?: number;\n /** Wall clock, injected so elapsed time is assertable in a test. */\n readonly now?: () => number;\n /**\n * How long a host gets to honour the cancel before it is forced. The same\n * knob the terminal run answers to; injectable so a test can watch the\n * forcing happen without waiting the real grace out.\n */\n readonly graceMs?: number;\n}\n\n/** The screen's state machine. */\ntype Phase =\n | { kind: 'running' }\n | { kind: 'menu'; active: number }\n | { kind: 'value'; option: PostRunMenuOption }\n | { kind: 'working'; message: string }\n | { kind: 'result'; ok: boolean; message: string }\n | { kind: 'ended'; ok: boolean; message: string };\n\nconst EMPTY_STREAM: RunStreamSnapshot = {\n lines: [],\n answer: '',\n toolCount: 0,\n inputTokens: 0,\n outputTokens: 0,\n};\n\n/** The token cell of the status line, in the same shape the CLI prints. */\nfunction tokenCell(input: number, output: number): string {\n return `↓${input.toLocaleString()}↑${output.toLocaleString()} tokens`;\n}\n\nexport function RunMode({\n prompt,\n deps,\n onExit,\n onQuit = onExit,\n height = 14,\n tickMs = 1000,\n now = Date.now,\n graceMs = INTERRUPT_GRACE_MS,\n}: RunModeProps): ReactElement {\n const [stream, setStream] = useState<RunStreamSnapshot>(EMPTY_STREAM);\n const [binary, setBinary] = useState('host');\n const [phase, setPhase] = useState<Phase>({ kind: 'running' });\n const [elapsedMs, setElapsedMs] = useState(0);\n // The turn being run: the prompt, plus the session to continue when this is a\n // follow-up. Replacing it starts a fresh turn on the same screen.\n const [turn, setTurn] = useState<{ prompt: string; sessionId?: string }>({ prompt });\n const [valueBuffer, setValueBuffer] = useState('');\n const [valueNotice, setValueNotice] = useState<string | null>(null);\n const [postRun, setPostRun] = useState<RunActionRequest | null>(null);\n const [statusHint, setStatusHint] = useState<string | null>(null);\n const interruptRef = useRef<RunInterrupt | null>(null);\n const rawLinesRef = useRef<string[]>([]);\n const startedAtRef = useRef(now());\n // Leaving the screen is read through a ref: the parent re-renders on every\n // status poll and hands a fresh `onExit` closure each time, and a turn keyed\n // off that identity would abandon a running host and spawn another.\n const exitRef = useRef(onExit);\n useEffect(() => {\n exitRef.current = onExit;\n }, [onExit]);\n\n // One turn, from spawn to settlement. The cleanup stops the host the same way\n // Esc does — signal first, force after a grace — because leaving the screen\n // (or starting a follow-up) must not leave a host running in the background,\n // and a host that ignores the signal must not outlive Noir either.\n useEffect(() => {\n const streamState = new RunStream();\n const interrupt = new RunInterrupt({ graceMs });\n interruptRef.current = interrupt;\n rawLinesRef.current = [];\n startedAtRef.current = now();\n let live = true;\n setStream(streamState.snapshot());\n setElapsedMs(0);\n setPostRun(null);\n setPhase({ kind: 'running' });\n setStatusHint(null);\n\n const tick = setInterval(() => {\n if (live) setElapsedMs(now() - startedAtRef.current);\n }, tickMs);\n\n let session: RunSession;\n try {\n session = deps.start(\n turn.prompt,\n {\n onEvent: (event) => {\n streamState.apply(event);\n if (live) setStream(streamState.snapshot());\n },\n onLine: (line) => {\n rawLinesRef.current.push(line);\n },\n signal: interrupt.signal,\n onChild: (child) => interrupt.track(child),\n },\n turn.sessionId,\n );\n } catch (err) {\n // A run that could not even resolve its spawn command (an unknown\n // profile, a host with no CLI) is a failure of THIS screen, not a crash.\n clearInterval(tick);\n setPhase({\n kind: 'ended',\n ok: false,\n message: err instanceof Error ? err.message : String(err),\n });\n return () => {\n live = false;\n };\n }\n setBinary(session.binary);\n\n void session.done\n .then(async (result) => {\n streamState.finalize();\n const snapshot = streamState.snapshot();\n const cancelled = interrupt.signal.aborted;\n // Persist before reporting: the transcript path is offered to the\n // post-run actions, and a run that is not on disk cannot be reopened\n // from the transcript picker.\n const transcript =\n (await deps.transcripts?.write(rawLinesRef.current)) ?? '(not persisted)';\n if (!live) return;\n setStream(snapshot);\n if (cancelled) {\n // Esc (or Ctrl+C) asked the host to stop, and it has: leave now rather\n // than holding a second keystroke. The transcript above is already on\n // disk, which is where the partial answer can still be read back.\n exitRef.current(interruptedNotice(transcript));\n return;\n }\n if (result.isError || result.exitCode !== 0) {\n setPhase({ kind: 'ended', ok: false, message: failureText(result, session.binary) });\n return;\n }\n setPostRun({\n answer: snapshot.answer,\n transcript,\n host: session.binary,\n ...(snapshot.sessionId === undefined ? {} : { sessionId: snapshot.sessionId }),\n canContinue: snapshot.sessionId !== undefined,\n });\n setPhase({ kind: 'menu', active: 0 });\n })\n .catch((err: unknown) => {\n if (!live) return;\n setPhase({\n kind: 'ended',\n ok: false,\n message: `could not run ${session.binary}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n });\n });\n\n return () => {\n live = false;\n clearInterval(tick);\n interrupt.terminate();\n };\n }, [turn, deps, now, tickMs, graceMs]);\n\n const options = useMemo(() => (postRun === null ? [] : deps.actions(postRun)), [postRun, deps]);\n\n /** Do the focused action, collecting its value first when it needs one. */\n const choose = useCallback(\n (choice: PostRunAction, value?: string) => {\n if (postRun === null) return;\n const label = options.find((o) => o.value === choice)?.label ?? 'That action';\n if (choice === 'dismiss') {\n onExit('run finished');\n return;\n }\n if (choice === 'resume') {\n // A continuation is another turn on this screen, not a command: the\n // same session, a prompt collected fresh, a fresh pane.\n const sessionId = postRun.sessionId;\n if (sessionId === undefined || value === undefined || value.trim().length === 0) {\n setValueNotice('a follow-up prompt is required');\n return;\n }\n setValueBuffer('');\n setValueNotice(null);\n setTurn({ prompt: value.trim(), sessionId });\n return;\n }\n setPhase({ kind: 'working', message: label });\n // The actions are CLI commands: they write to stdout/stderr and may\n // prompt. Captured here so none of that reaches the frame Ink is drawing,\n // and so a captured failure cannot rewrite the finished run's result. The\n // outcome is read back through the closure rather than the capture's\n // return value — the capture exists for the bytes, not the result.\n void (async () => {\n let outcome: PostRunOutcome = { ok: true, message: '' };\n const captured = await captureProcessOutput(async () => {\n outcome = await deps.perform(postRun, choice, value);\n });\n const written = `${captured.stdout}${captured.stderr}`.trim();\n setPhase({\n kind: 'result',\n ok: outcome.ok,\n message: [outcome.message, outcome.detail, written]\n .filter((part): part is string => part !== undefined && part.length > 0)\n .join(' — ')\n .replace(/\\s+/g, ' '),\n });\n })();\n },\n [deps, onExit, options, postRun],\n );\n\n /** Submit the value step's text, or hold the step open and say why. */\n const submitValue = useCallback(\n (option: PostRunMenuOption) => {\n const value = valueBuffer;\n if (option.needsValue !== undefined && value.trim().length === 0 && option.value !== 'save') {\n setValueNotice(`a ${option.needsValue.label} is required`);\n return;\n }\n setValueBuffer('');\n setValueNotice(null);\n choose(option.value, value);\n },\n [choose, valueBuffer],\n );\n\n useInput((input, key) => {\n // Ctrl+C while the host is live is the same request as Esc: stop it and say\n // so, rather than tearing the frame down and leaving the transcript path\n // unsaid. A second keystroke while that stop is already in flight forces the\n // host now, rather than holding the screen for the grace. With nothing\n // running there is nothing to stop, so it leaves the dashboard — what it does\n // in every other mode.\n if (key.ctrl && input === 'c') {\n if (phase.kind === 'running') {\n const interrupt = interruptRef.current;\n if (interrupt === null) return;\n if (interrupt.signal.aborted) {\n interrupt.forceNow();\n return;\n }\n interrupt.terminate();\n setStatusHint(RUN_CANCEL_HINT);\n return;\n }\n onQuit();\n return;\n }\n if (phase.kind === 'running') {\n if (key.escape) {\n interruptRef.current?.terminate();\n setStatusHint(RUN_CANCEL_HINT);\n }\n return;\n }\n if (phase.kind === 'ended') {\n if (key.return || key.escape) onExit();\n return;\n }\n if (phase.kind === 'result') {\n if (key.return || key.escape) setPhase({ kind: 'menu', active: 0 });\n return;\n }\n if (phase.kind === 'working') return;\n if (phase.kind === 'value') {\n if (key.escape) {\n setValueBuffer('');\n setValueNotice(null);\n setPhase({ kind: 'menu', active: 0 });\n return;\n }\n if (key.return) {\n submitValue(phase.option);\n return;\n }\n if (key.backspace || key.delete) {\n setValueBuffer((b) => b.slice(0, -1));\n setValueNotice(null);\n return;\n }\n if (key.ctrl || key.tab || key.upArrow || key.downArrow) return;\n if (input.length > 0) {\n setValueBuffer((b) => b + input);\n setValueNotice(null);\n }\n return;\n }\n // The menu.\n if (key.escape) {\n onExit('run finished');\n return;\n }\n if (key.return) {\n const option = options[phase.active];\n if (option === undefined) return;\n if (option.needsValue === undefined) {\n choose(option.value);\n return;\n }\n setValueBuffer('');\n setValueNotice(null);\n setPhase({ kind: 'value', option });\n return;\n }\n if (key.upArrow) {\n setPhase({ kind: 'menu', active: Math.max(0, phase.active - 1) });\n return;\n }\n if (key.downArrow) {\n setPhase({\n kind: 'menu',\n active: Math.min(Math.max(0, options.length - 1), phase.active + 1),\n });\n }\n });\n\n const valueStep: PostRunValueStep | undefined =\n phase.kind === 'value' && phase.option.needsValue !== undefined\n ? {\n label: phase.option.needsValue.label,\n placeholder: phase.option.needsValue.placeholder,\n value: valueBuffer,\n ...(valueNotice === null ? {} : { notice: valueNotice }),\n }\n : undefined;\n\n const overlay =\n phase.kind === 'menu' ||\n phase.kind === 'value' ||\n phase.kind === 'working' ||\n phase.kind === 'result' ? (\n <PostRunOverlay\n options={options}\n active={phase.kind === 'menu' ? phase.active : 0}\n {...(valueStep === undefined ? {} : { value: valueStep })}\n {...(phase.kind === 'working' ? { working: true } : {})}\n {...(phase.kind === 'result' ? { result: { ok: phase.ok, message: phase.message } } : {})}\n />\n ) : null;\n\n return (\n <Box flexDirection=\"column\">\n <Header tagline=\"run\" />\n <Panel>\n <Box paddingX={1}>\n <StatusBar\n payload={null}\n run={{\n model: stream.model ?? binary,\n elapsed: humanizeElapsed(elapsedMs),\n tokens: tokenCell(stream.inputTokens, stream.outputTokens),\n tools: stream.toolCount,\n }}\n />\n </Box>\n <Box paddingX={1}>\n <Text>{divider()}</Text>\n </Box>\n <Box flexDirection=\"column\" paddingX={1}>\n <OutputPane\n lines={stream.lines}\n scrollOffset={0}\n height={height}\n title={prompt}\n followTail\n />\n </Box>\n </Panel>\n {phase.kind === 'running' ? <Text>{c.dim(statusHint ?? RUN_HINT)}</Text> : null}\n {phase.kind === 'ended' ? (\n <Text>{phase.ok ? c.ok(phase.message) : c.warn(phase.message)}</Text>\n ) : null}\n {overlay}\n </Box>\n );\n}\n\n/** What a failed run says: the host's own reason, or its exit code. */\nfunction failureText(result: RunHostResult, binary: string): string {\n const reason =\n result.errorText !== undefined && result.errorText.trim().length > 0\n ? result.errorText.trim()\n : `exit code ${result.exitCode}`;\n return `${binary} failed: ${reason}`;\n}\n","// Scrollable output pane. Renders a windowed slice of `lines` based on the\n// current scroll offset (controlled by ArrowUp/ArrowDown at the App level).\n// Two sources of text share this pane: (a) the live project snapshot (the same\n// payload `noir status` shows), and (b) the captured stdout/stderr of a\n// dispatched `/<command>`. When dispatched output is present it takes\n// precedence (the user just asked for it); the App returns to the snapshot view\n// when the output is dismissed.\n\nimport { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c, contentWidth } from '../theme.js';\n\ninterface OutputPaneProps {\n lines: readonly string[];\n /** Index of the FIRST line scrolled to the top of the viewport. */\n scrollOffset: number;\n /** Visible row height of the pane. Defaults to a comfortable 12 rows. */\n height?: number;\n /** Optional title for the pane (shown as a dim header line). */\n title?: string;\n /**\n * Show the NEWEST lines rather than the oldest — for output still arriving,\n * where the interesting end is the one still being written. `scrollOffset` is\n * ignored while this is set.\n */\n followTail?: boolean;\n}\n\ninterface Row {\n /** Stable, content-derived React key (deduplicated so duplicate lines don't collide). */\n key: string;\n /** The text to render for this row. */\n text: string;\n}\n\n/**\n * Build stable React keys from line content. Duplicate lines get a `#N` suffix\n * so each row's key is unique WITHOUT using the array index — the lint rule\n * flags index keys, and content keys are also correct here (the pane is read-\n * only, but content keys keep reconciliation honest across scroll refreshes).\n */\nfunction toRows(visible: readonly string[]): Row[] {\n const rows: Row[] = [];\n const seen = new Map<string, number>();\n for (let i = 0; i < visible.length; i++) {\n const text = visible[i] ?? '';\n const n = (seen.get(text) ?? 0) + 1;\n seen.set(text, n);\n rows.push({ key: n === 1 ? text || '_blank' : `${text || '_blank'}#${n}`, text });\n }\n return rows;\n}\n\nexport function OutputPane({\n lines,\n scrollOffset,\n height = 12,\n title,\n followTail = false,\n}: OutputPaneProps): ReactElement {\n // Content width already accounts for the parent panel's border + padding\n // (see contentWidth()). Truncating to this (not the full terminal width)\n // keeps long lines — like the `noir status` table — inside the rounded box.\n const width = contentWidth();\n\n if (lines.length === 0) {\n return <Text>{c.dim('(no output — type a /command, or ? for help)')}</Text>;\n }\n\n // Clamp the offset into range so a stale value (e.g. after the content\n // shrinks) never produces a blank pane. A live pane pins to the bottom\n // instead: the newest line is the one being written, and it is what the user\n // is watching for.\n const maxOffset = Math.max(0, lines.length - height);\n const offset = followTail ? maxOffset : Math.min(Math.max(0, scrollOffset), maxOffset);\n const rows = toRows(lines.slice(offset, offset + height));\n\n return (\n <>\n {title !== undefined ? <Text wrap=\"truncate-end\">{c.dim(`── ${title} ──`)}</Text> : null}\n {rows.map((row) => {\n const truncated =\n row.text.length > width ? `${row.text.slice(0, Math.max(1, width - 1))}…` : row.text;\n // wrap=\"truncate-end\" guarantees a long line never wraps inside the\n // bordered panel — the manual truncate above is the first line of\n // defense; this is the second (Ink will hard-truncate if the panel is\n // narrower than contentWidth() reported, e.g. under a tiny COLUMNS).\n return (\n <Text key={row.key} wrap=\"truncate-end\">\n {truncated}\n </Text>\n );\n })}\n </>\n );\n}\n","// The post-run overlay: what to do with the answer that just streamed past.\n//\n// Presentational. The run screen owns the input routing (arrow keys, Enter,\n// Esc, and the value line) and the performer seam; this component paints the\n// rows and the state they are in. The rows themselves come from the shared\n// action set the terminal prompt also renders — the overlay decides how a row\n// LOOKS, never what it does, so the two surfaces cannot offer different things.\n//\n// The row idiom is the palette's: a `▸ ` marker and reverse video on the\n// focused row, the whole list dimmed while the value step owns the keyboard\n// (otherwise the highlight would read as \"Enter still picks this row\" when\n// Enter now submits the text), and a hint line under the panel.\n\nimport { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { PostRunMenuOption } from '../../run-actions.js';\nimport { c } from '../../theme.js';\nimport { Panel } from '../Panel.js';\n\nconst OVERLAY_WIDTH = 64;\n\n/** The value step: the question, what has been typed, and any one-line notice. */\nexport interface PostRunValueStep {\n readonly label: string;\n readonly placeholder: string;\n readonly value: string;\n readonly notice?: string;\n}\n\nexport interface PostRunOverlayProps {\n readonly options: readonly PostRunMenuOption[];\n /** Index of the focused row (ignored while a value step or a job is open). */\n readonly active: number;\n /** Set while a value is being collected for the focused row. */\n readonly value?: PostRunValueStep;\n /** Set while the chosen action is running. */\n readonly working?: boolean;\n /** What the action just did, once it is finished. */\n readonly result?: { readonly ok: boolean; readonly message: string };\n}\n\n/** Truncate a label so a long one cannot wrap the two-column row. */\nfunction truncate(text: string, max: number): string {\n return text.length > max ? `${text.slice(0, Math.max(1, max - 1))}…` : text;\n}\n\nexport function PostRunOverlay({\n options,\n active,\n value,\n working = false,\n result,\n}: PostRunOverlayProps): ReactElement {\n // A value step and a running action both take the keyboard away from the\n // list, so neither may leave a row looking selected.\n const listFocused = value === undefined && !working && result === undefined;\n const elements: ReactElement[] = [\n <Box key=\"header\" paddingX={1}>\n <Text>\n {c.bold('▸ run finished ')}\n <Text>{c.dim('· what next?')}</Text>\n </Text>\n </Box>,\n ];\n\n for (let i = 0; i < options.length; i++) {\n const row = options[i];\n if (!row) continue;\n const focused = listFocused && i === active;\n // Only the focused row shows its hint: the list is short, and one hint at a\n // time keeps the overlay from reading as a wall of text.\n const body = focused\n ? `${truncate(row.label, 28).padEnd(30)}${truncate(row.hint, 30)}`\n : truncate(row.label, 60);\n elements.push(\n <Box key={row.value} paddingX={1}>\n <Text>\n {focused ? c.inverse(`▸ ${body}`) : c.dim(listFocused ? ` ${body}` : `▸ ${body}`)}\n </Text>\n </Box>,\n );\n }\n\n if (value !== undefined) {\n elements.push(\n <Box key=\"value\" paddingX={1}>\n <Text>\n {c.accent('▸ ')}\n {value.value.length > 0 ? value.value : c.dim(value.placeholder)}\n <Text>{c.dim('▌')}</Text>\n </Text>\n </Box>,\n <Box key=\"value-label\" paddingX={1}>\n <Text>{c.dim(`${value.label} — Enter to continue · Esc to go back`)}</Text>\n </Box>,\n );\n }\n if (value?.notice !== undefined) {\n elements.push(\n <Box key=\"value-notice\" paddingX={1}>\n <Text>{c.warn(value.notice)}</Text>\n </Box>,\n );\n }\n if (working) {\n elements.push(\n <Box key=\"working\" paddingX={1}>\n <Text>{c.dim('working…')}</Text>\n </Box>,\n );\n }\n if (result !== undefined) {\n elements.push(\n <Box key=\"result\" paddingX={1}>\n <Text>{result.ok ? c.ok(result.message) : c.warn(result.message)}</Text>\n </Box>,\n );\n elements.push(\n <Box key=\"result-hint\" paddingX={1}>\n <Text>{c.dim('Enter or Esc to close')}</Text>\n </Box>,\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n <Panel maxWidth={OVERLAY_WIDTH}>{elements}</Panel>\n <Text>\n {c.dim(\n result !== undefined\n ? 'Enter close'\n : value !== undefined\n ? 'type the value · Enter continue · Esc back'\n : '↑/↓ choose · Enter run · Esc dismiss',\n )}\n </Text>\n </Box>\n );\n}\n","// Shared container — every TUI surface uses this one component for rounded\n// borders + width accounting so no caller duplicates `borderStyle round` /\n// `borderColor gray` / `width` math.\n//\n// When `maxWidth` is set the panel caps at that column ceiling — used by the\n// command palette for the fixed-width overlay look (like Raycast / VS Code).\n// Defaults to full terminal width via contentWidth().\n\nimport { Box } from 'ink';\nimport type { ReactElement, ReactNode } from 'react';\nimport { contentWidth, useColor } from '../theme.js';\n\nexport interface PanelProps {\n readonly children: ReactNode;\n /** Optional max-width ceiling in columns (e.g. 64 for the command palette). */\n readonly maxWidth?: number;\n}\n\nexport function Panel({ children, maxWidth }: PanelProps): ReactElement {\n const full = contentWidth() + 4; // border (2) + inner padding (2)\n const width = maxWidth != null ? Math.min(full, maxWidth) : full;\n // The border color rides the SAME color authority as every other surface:\n // when colors are off (NO_COLOR / non-TTY), pass `undefined` so Ink renders\n // the border in the default terminal color with no ANSI — a hardcoded\n // borderColor=\"gray\" bypassed useColor() and left colored borders around\n // plain content (the documented NO_COLOR contract).\n const borderColor = useColor() ? ('gray' as const) : undefined;\n return (\n <Box\n flexDirection=\"column\"\n borderStyle=\"round\"\n borderColor={borderColor}\n paddingX={1}\n width={width}\n >\n {children}\n </Box>\n );\n}\n","// Live state for one host run, rendered inside the TUI.\n//\n// THIS PATH MUST NEVER GO THROUGH `captureProcessOutput`. That helper swaps\n// `process.stdout.write` / `process.stderr.write` for collectors while a\n// dispatched command runs, and Ink renders every frame by calling\n// `process.stdout.write` itself — so a run screen wrapped in it would have its\n// own frames swallowed. A run therefore spawns through the injected run seam,\n// folds the host's normalized events into React state here, and lets Ink render\n// normally: the host's output is data this module turns into lines, and it\n// never touches the writer the capture would have replaced.\n//\n// The fold is a pure, inert class (no timers, no I/O) so it can be driven\n// frame-by-frame in a test and reused to re-render a transcript read back from\n// disk. Its one subtlety is that a host asked for partial messages reports the\n// same thing twice: a stretch of assistant text arrives first as the deltas\n// that spell it out and again as the block that closes it, and a tool call\n// arrives first as the frame that opens its block and again inside the message\n// that finished it. Both are therefore PROVISIONAL on arrival, and the thing\n// that announced them is what they resolve into — a tool call is one row and\n// one count whether or not the host streams partials — with the provisional\n// form surviving only when the run ends before the confirmation lands (a\n// cancelled run still shows the text it wrote and the tools it ran).\n\nimport {\n type NoirEvent,\n normalizeStreamEvent,\n parseStreamLine,\n UsageReducer,\n} from '../orchestrator.js';\n\n/** The marker a tool-activity row carries. Matches the host's own convention. */\nconst TOOL_MARK = '●';\n\n/** A snapshot of everything the run screen renders. */\nexport interface RunStreamSnapshot {\n /** The pane rows: assistant text and tool activity, in arrival order. */\n readonly lines: readonly string[];\n /** The host's answer as plain text — what a post-run action would save. */\n readonly answer: string;\n /** How many tool calls the run has started. */\n readonly toolCount: number;\n /** The host's id for this session, once it has reported one. */\n readonly sessionId?: string;\n /** The model the host announced, once it has. */\n readonly model?: string;\n readonly inputTokens: number;\n readonly outputTokens: number;\n}\n\n/**\n * One pane row, and whether the host has confirmed it yet.\n *\n * `text` is a tool's name on a tool row and a stretch of assistant text on a\n * text row — one field either way, because the fold has to be able to find the\n * row an announcement opened by the name it opened it with.\n */\ninterface Row {\n readonly kind: 'text' | 'tool';\n readonly text: string;\n /** True until a completed message — or the end of the run — confirms it. */\n pending: boolean;\n}\n\nexport class RunStream {\n private readonly reducer = new UsageReducer();\n /** The pane, in arrival order. */\n private readonly rows: Row[] = [];\n private sessionId: string | undefined;\n private model: string | undefined;\n\n /** Fold one normalized host event in. */\n apply(event: NoirEvent): void {\n switch (event.kind) {\n case 'init':\n if (event.sessionId !== undefined) this.sessionId = event.sessionId;\n if (event.model !== undefined) this.model = event.model;\n break;\n case 'assistant':\n this.commitMessage(event);\n break;\n case 'tool':\n // The incremental feed's word for a call it has just started. A call\n // read out of a completed message carries that message's id, and is the\n // authoritative row — it is also the only row a host that streams no\n // partials ever sends.\n this.recordTool(event.name, event.messageId !== undefined);\n break;\n case 'delta':\n this.appendText(event.text);\n break;\n default:\n // `result` and `other` carry nothing this screen shows.\n break;\n }\n this.reducer.add(event);\n }\n\n /**\n * Settle what is still provisional: called once the run has ended, so a run\n * that stopped mid-block keeps the words it had already produced and the\n * tools it had already started instead of losing them (and so a transcript\n * read back from disk renders completely).\n */\n finalize(): void {\n for (const row of this.rows) row.pending = false;\n }\n\n /** Everything the run screen renders, as of now. */\n snapshot(): RunStreamSnapshot {\n const usage = this.reducer.snapshot();\n const lines: string[] = [];\n const answer: string[] = [];\n let toolCount = 0;\n for (const row of this.rows) {\n if (row.kind === 'tool') {\n // Every tool row is one call, provisional or not: confirming a row\n // replaces it rather than adding one, so the count never moves twice\n // for the same call.\n toolCount += 1;\n lines.push(`${TOOL_MARK} ${row.text}`);\n continue;\n }\n answer.push(row.text);\n lines.push(...splitRows(row.text));\n }\n return {\n lines,\n // Provisional text counts: a cancelled run's answer is what it managed to\n // say, and `finalize` will have committed it anyway.\n answer: answer.join(''),\n toolCount,\n ...(this.sessionId === undefined ? {} : { sessionId: this.sessionId }),\n ...(this.model === undefined ? {} : { model: this.model }),\n inputTokens: usage.inputTokens,\n outputTokens: usage.outputTokens,\n };\n }\n\n /**\n * Commit a completed assistant message.\n *\n * Its text supersedes the deltas that spelled it out — the host reports the\n * same words twice — and its tool calls are the authoritative word on the\n * calls the incremental feed opened, each taking over the row its own\n * announcement opened. The text lands where its deltas were, which is above\n * the calls it went on to make: a message that says something and then calls\n * a tool reads in that order, rather than the call floating above the\n * sentence that introduced it.\n */\n private commitMessage(event: {\n readonly text?: string;\n readonly tools?: readonly string[];\n readonly isError?: boolean;\n }): void {\n // Where the block's words were already showing, so the committed text takes\n // that place; a block whose text never streamed is appended instead.\n const opened = this.rows.findIndex((row) => row.kind === 'text' && row.pending);\n const at = opened === -1 ? this.rows.length : opened;\n for (let i = this.rows.length - 1; i >= 0; i--) {\n const row = this.rows[i];\n if (row !== undefined && row.kind === 'text' && row.pending) this.rows.splice(i, 1);\n }\n // API-error text is a diagnostic, not the answer: it is shown when the run\n // fails, and never accumulated as something to save.\n if (event.isError !== true && event.text !== undefined && event.text.length > 0) {\n this.rows.splice(at, 0, { kind: 'text', text: event.text, pending: false });\n }\n for (const name of event.tools ?? []) this.recordTool(name, true);\n }\n\n /**\n * Record a tool call, confirmed by a completed message or announced by the\n * frame that opened its block. A confirmed call takes over the row the\n * announcement left open; with nothing to take over — the first word on the\n * call, which is all some hosts ever send — it opens a row of its own.\n */\n private recordTool(name: string, confirmed: boolean): void {\n if (confirmed) {\n const opened = this.rows.findIndex(\n (row) => row.kind === 'tool' && row.pending && row.text === name,\n );\n const row = this.rows[opened];\n if (row !== undefined) {\n row.pending = false;\n return;\n }\n }\n this.rows.push({ kind: 'tool', text: name, pending: !confirmed });\n }\n\n /** Extend the stretch of text the host is writing, or start a new one. */\n private appendText(text: string): void {\n const last = this.rows[this.rows.length - 1];\n // Only the last row, and only while it is unconfirmed: a delta that arrives\n // after a tool call starts a new stretch, and one that follows a committed\n // block belongs to the next message.\n if (last !== undefined && last.kind === 'text' && last.pending) {\n this.rows[this.rows.length - 1] = { kind: 'text', text: last.text + text, pending: true };\n return;\n }\n this.rows.push({ kind: 'text', text, pending: true });\n }\n}\n\n/** Split a stretch of text into the rows a pane displays it on. */\nfunction splitRows(text: string): string[] {\n const rows = text.split('\\n');\n // A block that ends with a newline would otherwise leave a trailing blank row.\n while (rows.length > 0 && rows[rows.length - 1] === '') rows.pop();\n return rows;\n}\n\n/**\n * Re-render a persisted transcript (raw stream-json lines) the way the run\n * screen would have shown it, so reopening an old run reads as prose and tool\n * activity rather than as JSONL. Unparseable lines are skipped: a transcript is\n * an audit record written by another process, and one bad line must not cost\n * the reader the rest of it.\n */\nexport function renderTranscript(rawLines: readonly string[]): readonly string[] {\n const stream = new RunStream();\n for (const line of rawLines) {\n const raw = parseStreamLine(line);\n if (raw === null) continue;\n const event = normalizeStreamEvent(raw);\n if (event !== null) stream.apply(event);\n }\n stream.finalize();\n return stream.snapshot().lines;\n}\n","// Top status bar. Renders a one-line summary of the live snapshot: host, mode\n// (from the active workflow task, if any), phase (or \"idle\"), and daemon\n// health. Degrades cleanly — when the payload is null (project uninitialized,\n// snapshot in flight, or the daemon fully down), every field shows a dash and\n// the daemon cell reads \"down\" so the user is never looking at empty space.\n//\n// The run screen reuses the same bar with a `run` summary instead of the\n// snapshot cells: a host run has no workflow/daemon snapshot to show, and what\n// a user needs mid-run is what is streaming — which model, how long, and how\n// many tokens have moved.\n\nimport { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { StatusPayload } from '../commands/status.js';\nimport { c } from '../theme.js';\n\n/** The live summary of a host run, when the bar renders the run screen. */\nexport interface RunProgress {\n /** The model the host announced, or the binary until it has. */\n readonly model: string;\n /** Elapsed time, already humanized (`8s`, `2m 18s`). */\n readonly elapsed: string;\n /** The token cell, already formatted (`↓1,234↑56 tokens`). */\n readonly tokens: string;\n /** How many tool calls the run has started (shown when non-zero). */\n readonly tools?: number;\n}\n\ninterface StatusBarProps {\n payload: StatusPayload | null;\n /** When true, the snapshot is being (re)loaded — shown as a dim hint. */\n loading?: boolean;\n /** Present on the run screen: render the live run instead of the snapshot. */\n run?: RunProgress;\n}\n\nfunction Cell({\n label,\n value,\n accent,\n}: {\n label: string;\n value: string;\n accent?: boolean;\n}): ReactElement {\n return (\n <Text>\n <Text>{c.dim(`${label}:`)}</Text> <Text>{accent ? c.accent(value) : value}</Text>\n </Text>\n );\n}\n\nexport function StatusBar({ payload, loading, run }: StatusBarProps): ReactElement {\n if (run !== undefined) {\n // The run screen's whole state is the run, so the snapshot cells are\n // replaced by it: `run · model · elapsed · tokens`, with the tool count\n // appended once the host has started working.\n const tools =\n run.tools !== undefined && run.tools > 0\n ? ` · ${run.tools} tool${run.tools === 1 ? '' : 's'}`\n : '';\n return (\n <Text>\n <Cell label=\"run\" value={`${run.model} · ${run.elapsed} · ${run.tokens}${tools}`} accent />\n </Text>\n );\n }\n\n const host = payload?.host ?? '—';\n const mode = payload?.workflow?.mode ?? '—';\n const phase = payload?.workflow?.phase ?? (loading ? '…' : 'idle');\n const daemonRunning = payload?.daemon.running === true;\n const daemon = payload === null && loading ? '…' : daemonRunning ? 'up' : 'down';\n\n return (\n <Text>\n <Cell label=\"host\" value={host} accent />\n <Text>{c.dim(' · ')}</Text>\n <Cell label=\"mode\" value={mode} />\n <Text>{c.dim(' · ')}</Text>\n <Cell label=\"phase\" value={phase} />\n <Text>{c.dim(' · ')}</Text>\n <Text>\n {c.dim('daemon:')} {daemonRunning ? c.ok(daemon) : c.warn(daemon)}\n </Text>\n </Text>\n );\n}\n","// The transcript picker (Ctrl+T): open a past run read-only.\n//\n// A run leaves its raw stream-json in `.noir/transcripts/`, and this screen\n// lists the recent ones and re-renders a chosen one through the SAME stream\n// fold the live run screen uses — so reopening an old run reads as prose and\n// tool activity, not as JSONL. It is deliberately read-only: nothing here can\n// change or delete a transcript, because a transcript is the audit record of\n// what a host actually did, and that is not something to edit from a list.\n//\n// The store is a seam: injected, so the picker can be driven in a test against\n// an in-memory list and never touches a real `.noir/transcripts/` directory.\n\nimport { Box, Text, useInput } from 'ink';\nimport { type ReactElement, useEffect, useState } from 'react';\nimport { c } from '../../theme.js';\nimport { Header } from '../Header.js';\nimport { TRANSCRIPT_LIST_HINT, TRANSCRIPT_READ_HINT } from '../hints.js';\nimport { OutputPane } from '../OutputPane.js';\nimport { Panel } from '../Panel.js';\nimport { renderTranscript } from '../run-stream.js';\nimport type { TranscriptEntry, TranscriptStore } from '../transcripts.js';\n\nexport interface TranscriptPickerProps {\n readonly transcripts: TranscriptStore;\n readonly onExit: (notice?: string) => void;\n readonly height?: number;\n}\n\ntype View = { kind: 'list' } | { kind: 'open'; name: string; lines: readonly string[] };\n\nexport function TranscriptPicker({\n transcripts,\n onExit,\n height = 14,\n}: TranscriptPickerProps): ReactElement {\n const [entries, setEntries] = useState<readonly TranscriptEntry[]>([]);\n const [loading, setLoading] = useState(true);\n const [active, setActive] = useState(0);\n const [view, setView] = useState<View>({ kind: 'list' });\n const [scroll, setScroll] = useState(0);\n\n useEffect(() => {\n let live = true;\n void transcripts\n .list()\n .then((found) => {\n if (live) setEntries(found);\n })\n .catch(() => {\n // A store that cannot list is \"no transcripts yet\", not a crash.\n if (live) setEntries([]);\n })\n .finally(() => {\n if (live) setLoading(false);\n });\n return () => {\n live = false;\n };\n }, [transcripts]);\n\n useInput((_input, key) => {\n if (loading) {\n if (key.escape) onExit();\n return;\n }\n if (view.kind === 'list') {\n if (key.escape) {\n onExit();\n return;\n }\n if (key.return) {\n const entry = entries[active];\n if (entry === undefined) return;\n void transcripts\n .read(entry.path)\n .then((lines) => {\n setView({ kind: 'open', name: entry.name, lines: renderTranscript(lines) });\n setScroll(0);\n })\n .catch(() => {\n // A read that fails keeps the list up: the entry is still there,\n // it is just not openable right now.\n });\n return;\n }\n if (key.upArrow) {\n setActive((a) => Math.max(0, a - 1));\n return;\n }\n if (key.downArrow) {\n setActive((a) => Math.min(Math.max(0, entries.length - 1), a + 1));\n return;\n }\n return;\n }\n // An open transcript: scroll, and Esc steps back to the list.\n if (key.escape) {\n setView({ kind: 'list' });\n return;\n }\n if (key.upArrow) setScroll((s) => Math.max(0, s - 1));\n if (key.downArrow) setScroll((s) => s + 1);\n });\n\n if (loading) {\n return (\n <Box flexDirection=\"column\">\n <Header tagline=\"transcripts\" />\n <Panel>\n <Box paddingX={1}>\n <Text>{c.dim('loading transcripts…')}</Text>\n </Box>\n </Panel>\n <Text>{c.dim('Esc back')}</Text>\n </Box>\n );\n }\n\n if (view.kind === 'open') {\n return (\n <Box flexDirection=\"column\">\n <Header tagline={`transcripts · ${view.name}`} />\n <Panel>\n <Box flexDirection=\"column\" paddingX={1}>\n <OutputPane\n lines={view.lines.length > 0 ? view.lines : ['(empty transcript)']}\n scrollOffset={scroll}\n height={height}\n title={view.name}\n />\n </Box>\n </Panel>\n <Text>{c.dim(TRANSCRIPT_READ_HINT)}</Text>\n </Box>\n );\n }\n\n if (entries.length === 0) {\n return (\n <Box flexDirection=\"column\">\n <Header tagline=\"transcripts\" />\n <Panel>\n <Box paddingX={1}>\n <Text>{c.dim('no transcripts yet — a run leaves one here')}</Text>\n </Box>\n </Panel>\n <Text>{c.dim('Esc back')}</Text>\n </Box>\n );\n }\n\n return (\n <Box flexDirection=\"column\">\n <Header tagline=\"transcripts\" />\n <Panel>\n <Box paddingX={1}>\n <Text>\n {c.bold('▸ recent transcripts ')}\n <Text>{c.dim('· newest first')}</Text>\n </Text>\n </Box>\n {entries.map((entry, i) => {\n const focused = i === active;\n const when = new Date(entry.mtimeMs).toISOString().replace('T', ' ').slice(0, 19);\n const row = `${entry.name} · ${when} · ${entry.sizeBytes} bytes`;\n return (\n <Box key={entry.path} paddingX={1}>\n <Text>{focused ? c.inverse(`▸ ${row}`) : c.dim(` ${row}`)}</Text>\n </Box>\n );\n })}\n </Panel>\n <Text>{c.dim(TRANSCRIPT_LIST_HINT)}</Text>\n </Box>\n );\n}\n","// In-TUI confirmation prompt for a destructive palette dispatch.\n//\n// Presentational only: it renders the `y/N` prompt for the argv the App wants\n// to approve. The App owns the input routing (its single `useInput` handles `y`\n// / `n` / Esc in confirm mode) and the dispatch seam, so this component has no\n// knowledge of what the command does or how to run it.\n//\n// Drawn inside a rounded, warn-colored border so a destructive gate is visually\n// unmistakable (TUI redesign: rounded outlines on every interactive surface).\n\nimport { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c } from '../../theme.js';\nimport { Panel } from '../Panel.js';\n\n/** The confirm overlay's props — supplied by the App. */\nexport interface ConfirmOverlayProps {\n /** The argv being approved, e.g. `['context','index']`. */\n readonly argv: readonly string[];\n}\n\n/**\n * Render the `y/N` confirmation prompt. Capital `N` marks the default (decline),\n * matching the CLI's prompt convention.\n */\nexport function ConfirmOverlay({ argv }: ConfirmOverlayProps): ReactElement {\n return (\n <Panel>\n <Box paddingX={1}>\n <Text>\n {c.warn(`! run /${argv.join(' ')}? (y/N)`)}\n <Text>{c.dim(' [y approve · n/Esc back to palette]')}</Text>\n </Text>\n </Box>\n </Panel>\n );\n}\n","// Hand-rolled fuzzy scorer for the command palette. This is the swap seam:\n// `fuzzyScore` + `matchCommand` are pure functions a future fzf/ItsFuzzy-style\n// matcher can replace without touching the TUI; `FuzzyMatch` is the shared\n// result shape both the matcher and the rendering layer speak.\n//\n// Design notes:\n// - Subsequence matcher (not substring): \"sts\" matches \"status\" (s, t, s).\n// - Case-insensitive — both sides lowercased before comparison.\n// - Gap penalty (-0.1 per skipped char) makes tight matches beat scattered ones.\n// - Consecutive bonus (+8) rewards runs like a real fuzzy finder.\n// - Prefix bonus (+4 while text-index < query.length) makes a leading match\n// dominate a trailing one, so \"sta\" ranks above a \"status\"-tail hit.\n\nimport type { PaletteCommand } from './types.js';\n\n/** A scored palette hit returned by the matcher. */\nexport interface FuzzyMatch {\n item: PaletteCommand;\n score: number;\n /** Text indices of the best-scoring field that matched (for highlight). */\n matchedIndices: number[];\n}\n\n/** Field weights — label dominates so titles win ties over help text. */\nconst WEIGHTS = {\n label: 1.0,\n keyword: 0.7,\n description: 0.5,\n} as const;\n\n/** Flat bonus added to the label field so it wins cross-field ties. */\nconst LABEL_TIE_BONUS = 50;\n\n/**\n * Score `query` against `text` as a (case-insensitive) subsequence. Returns\n * `{score, indices}` when every query char is matched in order, else `null`.\n * An empty query scores 0 with no indices.\n */\nexport function fuzzyScore(\n query: string,\n text: string,\n): { score: number; indices: number[] } | null {\n if (query.length === 0) return { score: 0, indices: [] };\n\n const q = query.toLowerCase();\n const t = text.toLowerCase();\n\n const indices: number[] = [];\n let score = 0;\n let prevMatchedIndex = -1;\n\n let qi = 0;\n for (let ti = 0; ti < t.length && qi < q.length; ti++) {\n if (t[ti] === q[qi]) {\n indices.push(ti);\n // consecutive-match bonus: only when this char directly follows the\n // previous matched char (a real fuzzy-finder \"run\" reward).\n const consecutive = prevMatchedIndex !== -1 && ti === prevMatchedIndex + 1;\n if (consecutive) score += 8;\n if (ti < q.length) score += 4; // prefix-region bonus\n // gap penalty for chars skipped between matches\n if (prevMatchedIndex !== -1) {\n const skipped = ti - prevMatchedIndex - 1;\n if (skipped > 0) score -= 0.1 * skipped;\n }\n prevMatchedIndex = ti;\n qi++;\n }\n }\n\n if (qi < q.length) return null; // query not a full subsequence\n return { score, indices };\n}\n\n/**\n * Score a single command across its label / keywords / description, returning\n * the best field's weighted result (plus the flat label tie-break bonus so a\n * label hit always wins a tie against a keyword or description hit). Returns\n * `null` when the query matches no field.\n */\nexport function matchCommand(query: string, item: PaletteCommand): FuzzyMatch | null {\n if (query.length === 0) {\n return { item, score: 0, matchedIndices: [] };\n }\n\n // Track the best field hit. Kept as a mutable holder (rather than a\n // `| null` union) so the `consider` closure can read the running high score\n // without TypeScript narrowing it to `never` across the conditional updates.\n const best: { score: number; indices: number[] } = {\n score: Number.NEGATIVE_INFINITY,\n indices: [],\n };\n let matched = false;\n\n // Replace the running best if `candidate` scores higher.\n const consider = (score: number, indices: number[]): void => {\n if (!matched || score > best.score) {\n best.score = score;\n best.indices = indices;\n matched = true;\n }\n };\n\n // Label — weighted highest, plus the flat tie-break bonus.\n const labelHit = fuzzyScore(query, item.label);\n if (labelHit) {\n consider(labelHit.score * WEIGHTS.label + LABEL_TIE_BONUS, labelHit.indices);\n }\n\n // Keywords — each scored independently, best keyword wins the field.\n if (item.keywords) {\n let bestKw: { score: number; indices: number[] } | null = null;\n for (const kw of item.keywords) {\n const kwHit = fuzzyScore(query, kw);\n if (kwHit && (bestKw === null || kwHit.score > bestKw.score)) bestKw = kwHit;\n }\n if (bestKw) {\n consider(bestKw.score * WEIGHTS.keyword, bestKw.indices);\n }\n }\n\n // Description — lowest weight, last resort.\n if (item.description) {\n const descHit = fuzzyScore(query, item.description);\n if (descHit) {\n consider(descHit.score * WEIGHTS.description, descHit.indices);\n }\n }\n\n if (!matched) return null;\n return { item, score: best.score, matchedIndices: best.indices };\n}\n","// The matcher swap seam. `FuzzyMatcher` is the interface the TUI depends on;\n// `handRolledMatcher` is the default, dependency-free implementation built on\n// `matchCommand`. Swapping in a native fzf/ItsFuzzy binding later means shipping\n// a second `FuzzyMatcher` — the palette code never changes.\n\nimport { type FuzzyMatch, matchCommand } from './fuzzyMatch.js';\nimport type { PaletteCommand } from './types.js';\n\n/**\n * What the palette asks for: \"rank these items against this query, capped at\n * `limit`\". Implementations must be deterministic and side-effect free.\n */\nexport interface FuzzyMatcher {\n search(query: string, items: readonly PaletteCommand[], limit: number): FuzzyMatch[];\n}\n\n/**\n * Default matcher. Empty query returns every item (score 0, empty indices) so\n * the palette can render the full list before the user types; otherwise each\n * item is scored, filtered to matches, sorted by descending score, and sliced\n * to `limit`. Sort is stable (ties keep input order) via a Array.prototype.sort\n * that only reorders on a strict score difference.\n */\nexport const handRolledMatcher: FuzzyMatcher = {\n search(query: string, items: readonly PaletteCommand[], limit: number): FuzzyMatch[] {\n if (query.length === 0) {\n return items.slice(0, Math.max(0, limit)).map((item) => ({\n item,\n score: 0,\n matchedIndices: [],\n }));\n }\n\n const matched: FuzzyMatch[] = [];\n for (const item of items) {\n const m = matchCommand(query, item);\n if (m) matched.push(m);\n }\n\n matched.sort((a, b) => b.score - a.score);\n return matched.slice(0, Math.max(0, limit));\n },\n};\n","// The single command surface (v2) — presentational renderer.\n//\n// The App owns the query + active row + the single `useInput` dispatcher; this\n// component only paints the rows {@link buildPaletteRows} produced for the\n// active corpus (commands / output / help). No `useInput`, no dispatch, no\n// history — the App is the sole decision-maker, so home, palette, and typed\n// /command all funnel through one seam.\n//\n// Layout (two-column, per the v2 design): a fixed label column (bold) on the\n// left, a dim hint column on the right. For command rows the hint is the\n// command's argv (short + unambiguous); the FULL description is shown as a\n// wrapped detail line on the ACTIVE row only, so it is never truncated. The\n// active row is highlighted with reverse video (SGR 7) — visible on any\n// terminal, not just color ones.\n\nimport { Box, Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { c } from '../../theme.js';\nimport { LIST_NAV_HINT } from '../hints.js';\nimport { Panel } from '../Panel.js';\nimport { type Corpus, type PaletteRow, VISIBLE_ROWS } from './rows.js';\n\nconst PALETTE_WIDTH = 64;\n/** Border + padding overhead between the panel width and a row's text budget:\n * round border (2) + Panel paddingX (2) + row paddingX (2) = 6. A row's\n * label(26) + hint must never exceed the remaining width or Ink wraps the\n * hint tail onto a flush-left second line (the v1.11.2 two-column bug). */\nconst ROW_TEXT_WIDTH = PALETTE_WIDTH - 6; // 58\n/** Fixed label column: the `▸ `/` ` prefix (2) + the label. */\nconst LABEL_WIDTH = 26;\n/** Max hint column width — derived from the row budget so the two-column row\n * can never overflow again (was a hardcoded 34, off by 2). */\nconst HINT_WIDTH = ROW_TEXT_WIDTH - LABEL_WIDTH; // 32\n\n/** Word-boundary wrap so a long description never truncates or overflows. */\nfunction wrap(text: string, width: number): string[] {\n const words = text.split(/\\s+/).filter((w) => w.length > 0);\n const lines: string[] = [];\n let cur = '';\n for (const w of words) {\n if (cur.length === 0) cur = w;\n else if (`${cur} ${w}`.length <= width) cur = `${cur} ${w}`;\n else {\n lines.push(cur);\n cur = w;\n }\n }\n if (cur) lines.push(cur);\n return lines;\n}\n\n/** Truncate a label to fit the fixed label column. */\nfunction truncateLabel(label: string): string {\n const max = LABEL_WIDTH - 2;\n return label.length > max ? `${label.slice(0, max - 1)}…` : label;\n}\n\n/** Placeholder hint for the query row, per corpus. */\nfunction placeholder(corpus: Corpus): string {\n if (corpus === 'commands') return 'type to search commands';\n if (corpus === 'output') return 'type to filter output';\n return 'keybindings';\n}\n\n/** The palette's props — supplied by the App (which owns input + dispatch). */\nexport interface PaletteProps {\n readonly corpus: Corpus;\n readonly query: string;\n readonly active: number;\n readonly rows: readonly PaletteRow[];\n /**\n * Set while the palette is collecting the selected command's argument: the\n * input line shows `value` (or `placeholder`) instead of the filter, and\n * Enter dispatches rather than filters. `hint` is a one-line notice shown\n * under the input line (e.g. after Enter with nothing typed).\n */\n readonly arg?: {\n readonly value: string;\n readonly placeholder: string;\n readonly hint?: string;\n };\n}\n\n/**\n * Render the palette overlay: an input row + the corpus list. Presentational —\n * the App owns query/active/input and passes the rows in.\n */\nexport function Palette({ corpus, query, active, rows, arg }: PaletteProps): ReactElement {\n const total = rows.length;\n // Sliding window: the cursor spans the FULL row list; slide a VISIBLE_ROWS\n // window so the active row is always on screen (sticking to the bottom once\n // scrolled). Without this, only the first 10 rows of a ~60-row command list\n // would be reachable.\n const scroll = Math.max(0, Math.min(active - (VISIBLE_ROWS - 1), total - VISIBLE_ROWS));\n const visible = rows.slice(scroll, scroll + VISIBLE_ROWS);\n const activeIndex = Math.min(Math.max(active - scroll, 0), Math.max(0, visible.length - 1));\n\n // The input row doubles as the argument step. An accent marker distinguishes\n // \"this text becomes the command's argument\" from the filter, where Enter\n // selects a row instead of submitting the text.\n const input =\n arg === undefined\n ? { marker: c.dim('> '), body: query.length > 0 ? query : c.dim(placeholder(corpus)) }\n : {\n marker: c.accent('▸ '),\n body: arg.value.length > 0 ? arg.value : c.dim(arg.placeholder),\n };\n\n const elements: ReactElement[] = [\n <Box key=\"header\" paddingX={1}>\n <Text>\n {c.bold('▸ palette ')}\n <Text>{c.dim(`· ${corpus}`)}</Text>\n </Text>\n </Box>,\n <Box key=\"query\" paddingX={1}>\n <Text>\n {input.marker}\n {input.body}\n <Text>{c.dim('▌')}</Text>\n </Text>\n </Box>,\n ];\n\n if (arg?.hint !== undefined) {\n elements.push(\n <Box key=\"arg-hint\" paddingX={1}>\n <Text>{c.warn(arg.hint)}</Text>\n </Box>,\n );\n }\n\n let lastGroup: string | null = null;\n for (let i = 0; i < visible.length; i++) {\n const row = visible[i];\n if (!row) continue;\n if (row.group !== null && row.group !== lastGroup) {\n // A blank spacer before every section (except the first) so the group\n // headers read as distinct, scannable blocks rather than a wall of rows.\n if (lastGroup !== null) {\n elements.push(\n <Box key={`gap:${row.group}`}>\n <Text> </Text>\n </Box>,\n );\n }\n elements.push(\n <Box key={`cat:${row.group}`} paddingX={1}>\n <Text>{c.bold(`── ${row.group} ──`)}</Text>\n </Box>,\n );\n lastGroup = row.group;\n }\n const isActive = i === activeIndex;\n // While the argument step owns the keyboard no row is highlighted and the\n // whole list is dimmed: an active row (reverse video, `▸ ` marker) would\n // read as \"Enter still selects this row\" when Enter now submits the value.\n const focused = isActive && arg === undefined;\n const prefix = focused ? '▸ ' : ' ';\n const label = truncateLabel(row.primary);\n const labelCol = (prefix + label).padEnd(LABEL_WIDTH);\n // Hint (right column): the argv for command rows, prefixed with `/` so it\n // reads as \"the command that runs on Enter\" (matching the dashboard's\n // `/command` convention). Non-command rows use the secondary line. Clipped\n // to the hint column so a long keybinding description (help corpus) never\n // overflows the panel.\n const hintRaw = row.argv ? `/${row.argv.join(' ')}` : row.secondary;\n const hint = hintRaw.length > HINT_WIDTH ? `${hintRaw.slice(0, HINT_WIDTH - 1)}…` : hintRaw;\n\n if (focused) {\n // Reverse video highlights the whole row — no inner color needed.\n elements.push(\n <Box key={row.key} paddingX={1}>\n <Text>{c.inverse(`${labelCol}${hint}`)}</Text>\n </Box>,\n );\n } else if (arg !== undefined) {\n elements.push(\n <Box key={row.key} paddingX={1}>\n <Text>{c.dim(`${labelCol}${hint}`)}</Text>\n </Box>,\n );\n } else {\n elements.push(\n <Box key={row.key} paddingX={1}>\n <Text>\n {c.bold(labelCol)}\n {hint.length > 0 ? c.dim(hint) : null}\n </Text>\n </Box>,\n );\n }\n\n // Detail line: the FULL description on the active row, wrapped so it is\n // never truncated. Command rows always show it; help rows show it too\n // (matching command-row UX — the hint column only truncates). Output-corpus\n // rows are excluded: their secondary is a line number / empty-state text,\n // not a description. A leading `↳` marks the detail as belonging to the row\n // above (not a new selectable item); continuation lines align under the\n // text so they read as one wrapped block.\n if (isActive && row.secondary && (row.argv || corpus === 'help')) {\n const detailLines = wrap(row.secondary, ROW_TEXT_WIDTH - 4);\n for (let di = 0; di < detailLines.length; di++) {\n const line = detailLines[di] ?? '';\n const prefix = di === 0 ? ' ↳ ' : ' ';\n elements.push(\n <Box key={`${row.key}:d:${di}`} paddingX={1}>\n <Text>{c.dim(`${prefix}${line}`)}</Text>\n </Box>,\n );\n }\n }\n }\n\n return (\n <Box flexDirection=\"column\">\n <Panel maxWidth={PALETTE_WIDTH}>{elements}</Panel>\n <Text>{c.dim(LIST_NAV_HINT)}</Text>\n </Box>\n );\n}\n","// Output-search primitives (moved from the retired `overlays/SearchMode.ts`).\n//\n// Pure, framework-free functions the palette's `output` corpus consumes:\n// `computeMatches` turns a query into the line indices that contain it. The\n// `/` dispatch prefix makes '/' unusable as a search key, so Ctrl+F (or the\n// palette `Tab` switch) is the entry into this corpus.\n\n/**\n * Line indices whose (case-insensitive) text contains `query`, in line order.\n * An empty query matches nothing (`[]`) — the search only starts once the user\n * types, and an empty query must never be treated as \"matches every line\".\n */\nexport function computeMatches(lines: readonly string[], query: string): number[] {\n if (query.length === 0) return [];\n const needle = query.toLowerCase();\n const out: number[] = [];\n for (let i = 0; i < lines.length; i++) {\n if ((lines[i] ?? '').toLowerCase().includes(needle)) out.push(i);\n }\n return out;\n}\n","// Pure row-building for the single command surface (v2). The App owns the\n// query + active row + key routing; this module derives the visible rows for a\n// corpus so the App can resolve the active row into a dispatch (Enter) while\n// the presentational `Palette` renders the same rows — one source of truth for\n// what the palette shows and what a selection runs.\n\nimport type { HomeAction, HomeSection } from '../commands/sections.js';\nimport { HELP_ENTRIES } from '../hints.js';\nimport type { FuzzyMatcher } from './matcher.js';\nimport { computeMatches } from './output.js';\nimport type { PaletteCommand } from './types.js';\n\n/** The palette's active corpus. */\nexport type Corpus = 'commands' | 'output' | 'help';\n\n/** All corpora, in `Tab`-cycle order. */\nexport const CORPORA: readonly Corpus[] = ['commands', 'output', 'help'];\n\nconst MATCH_LIMIT = 20;\n\n/** How many rows the palette renders at once (the sliding window size). */\nexport const VISIBLE_ROWS = 10;\n\n/** A rendered palette row. `argv` is null for non-dispatchable rows. */\nexport interface PaletteRow {\n key: string;\n primary: string;\n secondary: string;\n argv: readonly string[] | null;\n destructive: boolean;\n group: string | null;\n /**\n * The command's required value ('query', 'name', 'prompt') — the palette\n * collects it inline before dispatching, so `argv` alone would run the\n * command without its argument. Undefined for rows that dispatch bare (and\n * for non-dispatchable rows).\n */\n needsArg?: string;\n}\n\n/** Inputs to {@link buildPaletteRows}. */\nexport interface BuildRowsInput {\n corpus: Corpus;\n query: string;\n commands: readonly PaletteCommand[];\n matcher: FuzzyMatcher;\n recent: readonly PaletteCommand[];\n homeSections: readonly HomeSection[];\n outputLines: readonly string[];\n}\n\n/** Flatten curated home sections into `{ group, action }` rows. */\nfunction flattenHome(sections: readonly HomeSection[]): { group: string; action: HomeAction }[] {\n const out: { group: string; action: HomeAction }[] = [];\n for (const section of sections) {\n for (const action of section.items) out.push({ group: section.label, action });\n }\n return out;\n}\n\n/** `recent` entries that still exist in `commands`, in recent order. */\nfunction recentDeduped(\n recent: readonly PaletteCommand[],\n commands: readonly PaletteCommand[],\n): PaletteCommand[] {\n const known = new Set(commands.map((cmd) => cmd.id));\n return recent.filter((cmd) => known.has(cmd.id));\n}\n\n/**\n * Argument requirements keyed by the argv a row dispatches. The CLI definition\n * supplies one for every command with a required positional; a curated quick\n * action may declare its own for a value the definition leaves optional or\n * passes as a flag. Keying by argv rather than by command id matters for the\n * flag case: a curated action that carries its value behind a flag (its argv\n * includes the flag) is the only row that can accept the value, so rows that\n * dispatch the command without the flag must not ask for one they would drop.\n */\nfunction argRequirements(\n commands: readonly PaletteCommand[],\n homeSections: readonly HomeSection[],\n): Map<string, string> {\n const out = new Map<string, string>();\n for (const cmd of commands) {\n if (cmd.needsArg !== undefined) out.set(cmd.argv.join(' '), cmd.needsArg);\n }\n for (const { action } of flattenHome(homeSections)) {\n const label = action.needsArg?.label;\n if (label !== undefined) out.set((action.dispatch ?? [action.id]).join(' '), label);\n }\n return out;\n}\n\n/**\n * The requirement for one row, looked up by the argv it would dispatch. The\n * command id is a fallback for a row whose argv the palette overrides without\n * declaring how the value is passed.\n */\nfunction requirementFor(\n requirements: Map<string, string>,\n argv: readonly string[],\n id: string,\n): string | undefined {\n return requirements.get(argv.join(' ')) ?? requirements.get(id);\n}\n\n/** Build the visible rows for the active corpus + query. */\nexport function buildPaletteRows(input: BuildRowsInput): PaletteRow[] {\n const { corpus, query, commands, matcher, recent, homeSections, outputLines } = input;\n\n if (corpus === 'help') {\n return HELP_ENTRIES.map((e) => ({\n key: `help:${e.keys}`,\n primary: e.keys,\n secondary: e.desc,\n argv: null,\n destructive: false,\n group: 'keybindings',\n }));\n }\n\n if (corpus === 'output') {\n const matches = computeMatches(outputLines, query);\n if (query.length > 0 && matches.length === 0) {\n // A typed filter with zero hits is an explicit empty state — never list\n // the full output under a misleading \"matches\" header.\n return [\n {\n key: 'empty',\n primary: '(no matches)',\n secondary: `nothing matches \"${query}\"`,\n argv: null,\n destructive: false,\n group: null,\n },\n ];\n }\n if (matches.length === 0) {\n if (outputLines.length === 0) {\n return [\n {\n key: 'empty',\n primary: '(no output)',\n secondary: 'run a /command first',\n argv: null,\n destructive: false,\n group: null,\n },\n ];\n }\n // Empty query: browse the full output.\n return outputLines.map((line, i) => ({\n key: `out:${i}`,\n primary: line,\n secondary: String(i + 1),\n argv: null,\n destructive: false,\n group: null,\n }));\n }\n return matches.map((i) => ({\n key: `out:${i}`,\n primary: outputLines[i] ?? '',\n secondary: String(i + 1),\n argv: null,\n destructive: false,\n group: 'matches',\n }));\n }\n\n // commands corpus.\n const args = argRequirements(commands, homeSections);\n if (query.length === 0) {\n const rows: PaletteRow[] = [];\n const recents = recentDeduped(recent, commands);\n const recentIds = new Set(recents.map((r) => r.id));\n for (const cmd of recents) {\n rows.push({\n key: `recent:${cmd.id}`,\n primary: cmd.label,\n secondary: cmd.description,\n argv: cmd.argv,\n destructive: cmd.destructive,\n group: 'recent',\n needsArg: requirementFor(args, cmd.argv, cmd.id),\n });\n }\n const homeIds = new Set<string>();\n for (const { group, action } of flattenHome(homeSections)) {\n // Skip a curated action already shown in the recents section, so a command\n // run recently never appears twice (recents + its section) on one screen.\n if (recentIds.has(action.id)) continue;\n const argv = action.dispatch ?? [action.id];\n homeIds.add(action.id);\n rows.push({\n key: `home:${action.id}`,\n primary: action.label,\n secondary: action.hint,\n argv,\n destructive: action.destructive ?? false,\n group,\n needsArg: requirementFor(args, argv, action.id),\n });\n }\n for (const cmd of commands) {\n // Skip commands already shown as a recent or as a curated quick action —\n // the curated label wins over the raw leaf label (one row per command).\n if (recentIds.has(cmd.id) || homeIds.has(cmd.id)) continue;\n rows.push({\n key: `cmd:${cmd.id}`,\n primary: cmd.label,\n secondary: cmd.description,\n argv: cmd.argv,\n destructive: cmd.destructive,\n group: cmd.category,\n needsArg: requirementFor(args, cmd.argv, cmd.id),\n });\n }\n return rows;\n }\n\n // Non-empty query: fuzzy-rank the full command list.\n return matcher.search(query, commands, MATCH_LIMIT).map((m) => ({\n key: `cmd:${m.item.id}`,\n primary: m.item.label,\n secondary: m.item.description,\n argv: m.item.argv,\n destructive: m.item.destructive,\n group: null,\n needsArg: requirementFor(args, m.item.argv, m.item.id),\n }));\n}\n","// Palette command-history persistence.\n//\n// The palette shows the user's recently-run commands above the full list on an\n// empty query. That recency data lives on disk so it survives sessions. The file\n// is keyed by canonical ProjectId — `~/.noir/<projectId>/tui-history.json` (NOT a\n// filesystem path) — so it respects the `.noir/` single-source-of-truth invariant\n// (CLAUDE.md): a project's recents are isolated from every other project. The\n// file is a simple newest-first JSON array of `{ argv, id }` (id is the\n// space-joined argv — the same stable dispatch key the registry uses).\n//\n// Opt-out: set `NOIR_DISABLE_TUI_HISTORY=1` to make persistence in-memory only\n// (loadRecent/recordRecent become no-ops). Useful for ephemeral/CI shells.\n//\n// Bounds: `MAX_ENTRIES` (50) caps the file so it never grows without bound, and\n// every write is atomic via the shared `atomicWriteFile` (@noir-ai/core — the\n// same helper daemon/lifecycle.ts and update-check.ts use) so a crash mid-write\n// never leaves a half-written JSON blob. A corrupt file (manual edit, truncated\n// write from an older build) degrades to `[]` rather than throwing — recency is\n// a nice-to-have, never a crash.\n//\n// Test seam: `__setNoirHome` overrides the home dir so the offline suite can\n// point at a tmpdir instead of the real `~/.noir` (mirrors `NOIR_DAEMON_JSON`).\n\nimport { existsSync, mkdirSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { atomicWriteFile } from '@noir-ai/core';\n\n/** A single entry in the persisted recent-commands list. */\nexport interface RecentEntry {\n /** The raw argv the user selected (e.g. `['context','search','foo']`). */\n readonly argv: readonly string[];\n /** Space-joined argv — the stable dispatch key (e.g. `context search foo`). */\n readonly id: string;\n}\n\n/** Bounded history size — the palette only needs a handful of recents. */\nconst MAX_ENTRIES = 50;\n\n/** The env var that disables palette-history persistence (in-memory only). */\nexport const TUI_HISTORY_DISABLE_ENV = 'NOIR_DISABLE_TUI_HISTORY';\n\nlet testHome: string | null = null;\n\n/**\n * Test override for the Noir home dir. Pass `null` to restore the real\n * `~/.noir` resolution. Never called from production code.\n */\nexport function __setNoirHome(home: string | null): void {\n testHome = home;\n}\n\n/** The user-global Noir home (test override wins when set). */\nfunction homeDir(): string {\n return testHome ?? join(homedir(), '.noir');\n}\n\n/**\n * Path of the palette history file for a project:\n * `<home>/<projectId>/tui-history.json`. ProjectId-keyed so recents are isolated\n * per project (respects the `.noir/` single-source-of-truth invariant).\n */\nfunction historyFile(projectId: string): string {\n return join(homeDir(), projectId, 'tui-history.json');\n}\n\n/**\n * Whether palette history persistence is enabled. Honors\n * `NOIR_DISABLE_TUI_HISTORY` (any non-empty value disables it → in-memory only).\n */\nexport function isHistoryEnabled(): boolean {\n const v = process.env[TUI_HISTORY_DISABLE_ENV];\n return v === undefined || v === '';\n}\n\n/**\n * Load the persisted recent commands for `projectId`, newest first. Returns `[]`\n * when persistence is disabled, the file is missing/empty/corrupt — recency is\n * best-effort and must never throw.\n */\nexport function loadRecent(projectId: string): RecentEntry[] {\n if (!isHistoryEnabled()) return [];\n const file = historyFile(projectId);\n try {\n if (!existsSync(file)) return [];\n const raw = readFileSync(file, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n const entries: RecentEntry[] = [];\n for (const item of parsed) {\n if (typeof item !== 'object' || item === null) continue;\n const argv = (item as { argv?: unknown }).argv;\n const id = (item as { id?: unknown }).id;\n if (!Array.isArray(argv) || typeof id !== 'string') continue;\n if (!argv.every((tok): tok is string => typeof tok === 'string')) continue;\n entries.push({ argv, id });\n }\n return entries;\n } catch {\n return [];\n }\n}\n\n/**\n * Record a dispatched command as the newest recent entry for `projectId`.\n * Adjacent duplicates are dropped (re-running the same command once is one\n * entry); re-running a command from deeper in the list moves it to the front.\n * The persisted list is capped at {@link MAX_ENTRIES}. No-op when persistence is\n * disabled. Best-effort: a failed write is swallowed (recency is a nice-to-have\n * — a read-only home or a full disk must not break command dispatch).\n */\nexport function recordRecent(projectId: string, argv: readonly string[]): void {\n if (argv.length === 0) return;\n if (!isHistoryEnabled()) return;\n const entry: RecentEntry = { argv, id: argv.join(' ') };\n const current = loadRecent(projectId).filter((e) => e.id !== entry.id);\n const next = [entry, ...current].slice(0, MAX_ENTRIES);\n try {\n const dir = join(homeDir(), projectId);\n mkdirSync(dir, { recursive: true });\n atomicWriteFile(historyFile(projectId), `${JSON.stringify(next, null, 2)}\\n`);\n } catch {\n // Recency is a nice-to-have — a read-only home (or a full disk) must not\n // break command dispatch.\n }\n}\n","// The recent-transcript store behind the TUI's transcript picker.\n//\n// `noir run` writes the host's raw stream-json to `.noir/transcripts/`, and\n// until now nothing could read it back: the audit record of a run existed on\n// disk and was reachable only by knowing the path. The store lists what is\n// there, newest first, and reads one back so the run screen can render it.\n//\n// It is a seam (an interface plus one filesystem implementation) so the TUI can\n// be driven in a test without a run ever writing into the repository — and so\n// the persistence rules live in one place: the same 0700 directory and 0600\n// file the CLI's own runs create, because a transcript quotes raw host output\n// and prompts, which can contain credentials.\n\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { join, resolve, sep } from 'node:path';\nimport { paths } from '@noir-ai/core';\nimport { writeTranscript } from '../commands/run.js';\n\n/** One persisted transcript, as the picker lists it. */\nexport interface TranscriptEntry {\n /** Absolute path — what {@link TranscriptStore.read} takes back. */\n readonly path: string;\n /** Base name, which is what the picker shows. */\n readonly name: string;\n readonly sizeBytes: number;\n readonly mtimeMs: number;\n}\n\nexport interface TranscriptStore {\n /** The most recent transcripts, newest first. */\n list(limit?: number): Promise<readonly TranscriptEntry[]>;\n /** The raw stream-json lines of one transcript, in order. */\n read(path: string): Promise<readonly string[]>;\n /** Persist a run's raw lines; returns the path (or a \"not persisted\" note). */\n write(lines: readonly string[]): Promise<string>;\n}\n\n/** How many transcripts the picker offers before it stops being a short list. */\nexport const TRANSCRIPT_LIST_LIMIT = 20;\n\n/** The transcript directory under `root`, where every run persists its stream. */\nexport function transcriptDir(root: string = process.cwd()): string {\n return paths.transcriptsDir(root);\n}\n\n/**\n * The filesystem-backed store the TUI uses. Reads are best-effort and answered\n * with empty results rather than errors: an unreadable or absent directory is\n * \"no transcripts yet\", which is a state the picker renders, not a failure the\n * user has to dismiss.\n */\nexport function createTranscriptStore(opts: {\n /** The host label the file name is built from (`claude`, `claude-work`, …). */\n readonly host: string;\n /** Project root (defaults to the working directory). */\n readonly root?: string;\n}): TranscriptStore {\n const root = opts.root ?? process.cwd();\n const dir = transcriptDir(root);\n return {\n list: async (limit = TRANSCRIPT_LIST_LIMIT) => {\n let names: string[];\n try {\n names = readdirSync(dir);\n } catch {\n return [];\n }\n const entries: TranscriptEntry[] = [];\n for (const name of names) {\n if (!name.endsWith('.jsonl')) continue;\n const path = join(dir, name);\n try {\n const info = statSync(path);\n if (!info.isFile()) continue;\n entries.push({ path, name, sizeBytes: info.size, mtimeMs: info.mtimeMs });\n } catch {\n // A file that vanished between the listing and the stat is simply not\n // in the list.\n }\n }\n // Newest first: the run a user wants back is almost always the last one.\n entries.sort((a, b) => b.mtimeMs - a.mtimeMs || a.name.localeCompare(b.name));\n return entries.slice(0, Math.max(0, limit));\n },\n read: async (path: string) => {\n // Only this project's own transcripts: the picker hands back paths it was\n // given, but the seam is public and a read is a read.\n const resolved = resolve(path);\n if (resolved !== resolve(dir) && !resolved.startsWith(resolve(dir) + sep)) return [];\n try {\n return readFileSync(resolved, 'utf8').split('\\n');\n } catch {\n return [];\n }\n },\n write: async (lines: readonly string[]) => {\n // Delegates to the CLI's own writer so a run started from the TUI lands in\n // the same place, with the same permissions, as one started from a shell.\n // The store's own `root` is threaded through so list/read/write all resolve\n // against the same project, not the process's working directory.\n return writeTranscript(opts.host, lines, root);\n },\n };\n}\n"],"mappings":"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","names":["Box","Text","useInput","useEffect","useMemo","useState","Text","jsx","Text","Text","jsx","jsxs","Text","Box","Text","useCallback","useRef","useState","Text","jsx","jsxs","Text","Box","Text","Box","jsx","Box","jsx","jsxs","Box","Text","row","Text","jsx","jsxs","Text","jsx","jsxs","useState","useRef","useCallback","Box","Text","Box","Text","useInput","useEffect","useState","jsx","jsxs","useState","useEffect","useInput","Box","Text","Box","Text","jsx","jsxs","Box","Text","Box","Text","jsx","jsxs","Box","Text","prefix","jsx","jsxs","useState","useEffect","c","useInput","useMemo","Box","notice","Text","readFileSync","join","join","readFileSync","jsx","c"]}
|