@noir-ai/cli 1.10.1 → 1.11.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/dist/tui/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  DEFAULT_UPDATE_CONFIG,
3
+ buildUpdateNotice,
3
4
  runAsyncUpdateCheck
4
- } from "./chunk-RCMEIONF.js";
5
+ } from "./chunk-4OBUMHF6.js";
5
6
  import "./chunk-R6246ISQ.js";
6
7
  import {
7
8
  doctor,
@@ -38,7 +39,7 @@ import { render } from "ink";
38
39
  // src/bin.ts
39
40
  import { realpathSync } from "fs";
40
41
  import { pathToFileURL } from "url";
41
- import { SUPPORTED_HOSTS } from "@noir-ai/adapters";
42
+ import { SUPPORTED_HOSTS as SUPPORTED_HOSTS2 } from "@noir-ai/adapters";
42
43
  import { NOIR_VERSION as NOIR_VERSION4 } from "@noir-ai/core";
43
44
  import { Command, Option } from "commander";
44
45
 
@@ -1467,9 +1468,12 @@ async function handoff(opts) {
1467
1468
  // src/commands/home.ts
1468
1469
  import { hostLaunchDirective as hostLaunchDirective2 } from "@noir-ai/adapters";
1469
1470
  import {
1471
+ detectActiveMethod,
1472
+ latestVersionFromCache,
1470
1473
  loadProjectInfo as loadProjectInfo6,
1471
1474
  NOIR_VERSION as NOIR_VERSION3,
1472
- readInstallRecord
1475
+ readInstallRecord,
1476
+ readUpdateCache
1473
1477
  } from "@noir-ai/core";
1474
1478
 
1475
1479
  // src/banner.ts
@@ -1507,7 +1511,6 @@ function shouldShowBanner(opts) {
1507
1511
  // src/tui/commands/registry.ts
1508
1512
  var DESTRUCTIVE_PREFIXES = [
1509
1513
  ["context", "index"],
1510
- ["context", "forget"],
1511
1514
  ["memory", "save"],
1512
1515
  ["memory", "forget"],
1513
1516
  ["memory", "consolidate"],
@@ -1521,7 +1524,8 @@ var DESTRUCTIVE_PREFIXES = [
1521
1524
  ["create"],
1522
1525
  ["sync"],
1523
1526
  ["install"],
1524
- ["update"]
1527
+ ["update"],
1528
+ ["run"]
1525
1529
  ];
1526
1530
  function isDestructive(argv) {
1527
1531
  return DESTRUCTIVE_PREFIXES.some((prefix) => {
@@ -1570,7 +1574,6 @@ var HOME_SECTIONS = [
1570
1574
  id: "status",
1571
1575
  label: "Status & context",
1572
1576
  hint: "snapshot \xB7 index \xB7 search \xB7 doctor",
1573
- key: "1",
1574
1577
  items: [
1575
1578
  {
1576
1579
  id: "status",
@@ -1605,7 +1608,6 @@ var HOME_SECTIONS = [
1605
1608
  id: "memory",
1606
1609
  label: "Memory",
1607
1610
  hint: "recall \xB7 save \xB7 sessions \xB7 forget \xB7 consolidate",
1608
- key: "2",
1609
1611
  items: [
1610
1612
  {
1611
1613
  id: "memory recall",
@@ -1643,7 +1645,6 @@ var HOME_SECTIONS = [
1643
1645
  id: "workflow",
1644
1646
  label: "Workflow",
1645
1647
  hint: "next \xB7 status \xB7 advance \xB7 handoff \xB7 wrap",
1646
- key: "3",
1647
1648
  items: [
1648
1649
  {
1649
1650
  id: "task next",
@@ -1678,7 +1679,6 @@ var HOME_SECTIONS = [
1678
1679
  id: "setup",
1679
1680
  label: "Setup & maintenance",
1680
1681
  hint: "init \xB7 create \xB7 sync \xB7 skills \xB7 install \xB7 update",
1681
- key: "4",
1682
1682
  items: [
1683
1683
  {
1684
1684
  id: "init",
@@ -1727,7 +1727,6 @@ var HOME_SECTIONS = [
1727
1727
  id: "dashboard",
1728
1728
  label: "Dashboard (full-screen)",
1729
1729
  hint: "noir tui \xB7 noir palette \xB7 home",
1730
- key: "5",
1731
1730
  items: [
1732
1731
  {
1733
1732
  id: "tui",
@@ -1956,6 +1955,19 @@ ${COMMANDS_HINT}
1956
1955
  `
1957
1956
  ${c.warn(`noir installed via ${rec.method}`)} \u2014 consider \`noir install\` for the native path (auto-update, no npm prefix/PATH issues). Dismiss with: \`noir install --dismiss\` (persisted per version).
1958
1957
 
1958
+ `
1959
+ );
1960
+ }
1961
+ const updateNotice = buildUpdateNotice({
1962
+ method: detectActiveMethod(),
1963
+ currentVersion: NOIR_VERSION3,
1964
+ latestKnown: latestVersionFromCache(readUpdateCache(), "latest")
1965
+ });
1966
+ if (updateNotice) {
1967
+ process.stderr.write(
1968
+ `
1969
+ ${c.warn(`noir ${updateNotice.latestVersion} is available`)} (you have ${updateNotice.currentVersion}) \u2014 ${updateNotice.advice}.
1970
+
1959
1971
  `
1960
1972
  );
1961
1973
  }
@@ -2321,6 +2333,255 @@ async function release(opts) {
2321
2333
  }
2322
2334
  }
2323
2335
 
2336
+ // src/commands/run.ts
2337
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
2338
+ import { join as join2 } from "path";
2339
+ import { SUPPORTED_HOSTS } from "@noir-ai/adapters";
2340
+
2341
+ // src/orchestrator.ts
2342
+ import { spawn } from "child_process";
2343
+ import { createInterface } from "readline";
2344
+ var HOST_BINARY = {
2345
+ claude: "claude",
2346
+ gemini: "gemini",
2347
+ opencode: "opencode",
2348
+ cursor: "cursor-agent",
2349
+ "agents-md": null
2350
+ // emits AGENTS.md — a file host, not a spawnable CLI
2351
+ };
2352
+ var HOST_FLAGS = {
2353
+ claude: ["-p", "--output-format", "stream-json", "--verbose"],
2354
+ gemini: ["-p", "--output-format", "stream-json"],
2355
+ opencode: ["run"],
2356
+ cursor: ["-p"],
2357
+ "agents-md": []
2358
+ };
2359
+ function resolveHostRun(host, customBinary) {
2360
+ const binary = customBinary && customBinary.length > 0 ? customBinary : HOST_BINARY[host];
2361
+ if (!binary) return null;
2362
+ return { binary, flags: HOST_FLAGS[host] };
2363
+ }
2364
+ function asObj(v) {
2365
+ return typeof v === "object" && v !== null ? v : null;
2366
+ }
2367
+ function str(v) {
2368
+ return typeof v === "string" ? v : void 0;
2369
+ }
2370
+ function num(v) {
2371
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
2372
+ }
2373
+ function usageFrom(v) {
2374
+ const o = asObj(v);
2375
+ if (!o) return void 0;
2376
+ const input = num(o.input_tokens);
2377
+ const output = num(o.output_tokens);
2378
+ const cache = num(o.cache_read_input_tokens) ?? num(o.cache_creation_input_tokens);
2379
+ if (input === void 0 && output === void 0 && cache === void 0) return void 0;
2380
+ return { inputTokens: input, outputTokens: output, cacheReadTokens: cache };
2381
+ }
2382
+ function messageText(message) {
2383
+ if (!message) return void 0;
2384
+ const content = message.content;
2385
+ if (typeof content === "string") return content;
2386
+ if (Array.isArray(content)) {
2387
+ const parts = content.map((block) => {
2388
+ const b = asObj(block);
2389
+ return b && typeof b.text === "string" ? b.text : null;
2390
+ }).filter((t) => t !== null);
2391
+ if (parts.length > 0) return parts.join("");
2392
+ }
2393
+ return void 0;
2394
+ }
2395
+ function parseStreamLine(line) {
2396
+ const trimmed = line.trim();
2397
+ if (trimmed.length === 0) return null;
2398
+ try {
2399
+ return JSON.parse(trimmed);
2400
+ } catch {
2401
+ return null;
2402
+ }
2403
+ }
2404
+ function normalizeStreamEvent(raw) {
2405
+ const r = asObj(raw);
2406
+ if (!r) return null;
2407
+ const type = r.type;
2408
+ if (type === "system") {
2409
+ if (r.subtype === "init") {
2410
+ return { kind: "init", sessionId: str(r.session_id), model: str(r.model) };
2411
+ }
2412
+ return { kind: "other" };
2413
+ }
2414
+ if (type === "assistant") {
2415
+ const message = asObj(r.message);
2416
+ return {
2417
+ kind: "assistant",
2418
+ messageId: message ? str(message.id) : void 0,
2419
+ text: messageText(message),
2420
+ usage: message ? usageFrom(message.usage) : void 0
2421
+ };
2422
+ }
2423
+ if (type === "result") {
2424
+ return {
2425
+ kind: "result",
2426
+ isError: r.is_error === true,
2427
+ durationMs: num(r.duration_ms),
2428
+ numTurns: num(r.num_turns),
2429
+ totalCostUsd: num(r.total_cost_usd),
2430
+ usage: usageFrom(r.usage)
2431
+ };
2432
+ }
2433
+ return { kind: "other" };
2434
+ }
2435
+ var UsageReducer = class {
2436
+ maxByMessage = /* @__PURE__ */ new Map();
2437
+ input = 0;
2438
+ output = 0;
2439
+ costUsd = 0;
2440
+ turns = 0;
2441
+ /** Feed one normalized event into the reducer. */
2442
+ add(event) {
2443
+ if (event.kind === "assistant" && event.messageId && event.usage) {
2444
+ const key = event.messageId;
2445
+ const input = event.usage.inputTokens ?? 0;
2446
+ const output = event.usage.outputTokens ?? 0;
2447
+ const prev = this.maxByMessage.get(key);
2448
+ if (!prev) {
2449
+ this.maxByMessage.set(key, { input, output });
2450
+ this.input += input;
2451
+ this.output += output;
2452
+ } else {
2453
+ this.input += Math.max(0, input - prev.input);
2454
+ this.output += Math.max(0, output - prev.output);
2455
+ this.maxByMessage.set(key, {
2456
+ input: Math.max(prev.input, input),
2457
+ output: Math.max(prev.output, output)
2458
+ });
2459
+ }
2460
+ } else if (event.kind === "result") {
2461
+ if (event.totalCostUsd !== void 0) this.costUsd = event.totalCostUsd;
2462
+ if (event.numTurns !== void 0) this.turns = event.numTurns;
2463
+ }
2464
+ }
2465
+ /** The current accumulator snapshot. */
2466
+ snapshot() {
2467
+ return {
2468
+ inputTokens: this.input,
2469
+ outputTokens: this.output,
2470
+ totalCostUsd: this.costUsd,
2471
+ numTurns: this.turns
2472
+ };
2473
+ }
2474
+ };
2475
+ function runHost(opts) {
2476
+ const spec = resolveHostRun(opts.host, opts.customBinary);
2477
+ return new Promise((resolve, reject) => {
2478
+ if (!spec) {
2479
+ reject(new Error(`host '${opts.host}' is not a spawnable CLI (no default command)`));
2480
+ return;
2481
+ }
2482
+ const child = spawn(spec.binary, [...spec.flags, opts.prompt], {
2483
+ stdio: ["ignore", "pipe", "pipe"]
2484
+ });
2485
+ const reducer = new UsageReducer();
2486
+ let eventCount = 0;
2487
+ let stderrBuf = "";
2488
+ child.on("error", (err) => reject(err));
2489
+ const rl = createInterface({ input: child.stdout });
2490
+ rl.on("line", (line) => {
2491
+ opts.onLine?.(line);
2492
+ const raw = parseStreamLine(line);
2493
+ if (raw === null) return;
2494
+ const event = normalizeStreamEvent(raw);
2495
+ if (event === null) return;
2496
+ eventCount += 1;
2497
+ reducer.add(event);
2498
+ opts.onEvent?.(event);
2499
+ });
2500
+ child.stderr.setEncoding("utf8");
2501
+ child.stderr.on("data", (chunk) => {
2502
+ stderrBuf += chunk;
2503
+ });
2504
+ child.on("close", (code) => {
2505
+ resolve({ exitCode: code ?? 0, usage: reducer.snapshot(), eventCount, stderr: stderrBuf });
2506
+ });
2507
+ });
2508
+ }
2509
+
2510
+ // src/commands/run.ts
2511
+ function formatUsage(u) {
2512
+ const cost = u.totalCostUsd > 0 ? ` $${u.totalCostUsd.toFixed(2)}` : "";
2513
+ const turns = u.numTurns > 0 ? ` \xB7 ${u.numTurns} turns` : "";
2514
+ return `${u.inputTokens.toLocaleString()} in / ${u.outputTokens.toLocaleString()} out${cost}${turns}`;
2515
+ }
2516
+ function streamEvent(event, opts) {
2517
+ if (opts.json === true) return;
2518
+ if (event.kind === "assistant" && event.text && event.text.length > 0) {
2519
+ process.stdout.write(event.text);
2520
+ }
2521
+ }
2522
+ async function run(prompt, opts) {
2523
+ const host = opts.host ?? "claude";
2524
+ if (!SUPPORTED_HOSTS.includes(host)) {
2525
+ fail(EXIT.USAGE, `unknown host '${host}' (supported: ${SUPPORTED_HOSTS.join(", ")})`, opts);
2526
+ }
2527
+ if (prompt.length === 0) {
2528
+ fail(EXIT.USAGE, "a prompt is required: `noir run <prompt>`", opts);
2529
+ }
2530
+ const transcriptLines = [];
2531
+ try {
2532
+ const result = await runHost({
2533
+ host,
2534
+ prompt,
2535
+ customBinary: opts.command,
2536
+ onLine: (line) => transcriptLines.push(line),
2537
+ onEvent: (event) => streamEvent(event, opts)
2538
+ });
2539
+ if (opts.json !== true) {
2540
+ process.stdout.write("\n");
2541
+ }
2542
+ const transcript = writeTranscript(host, transcriptLines);
2543
+ if (opts.json === true) {
2544
+ json({
2545
+ ok: true,
2546
+ data: {
2547
+ host,
2548
+ prompt,
2549
+ exitCode: result.exitCode,
2550
+ usage: result.usage,
2551
+ numTurns: result.usage.numTurns,
2552
+ events: result.eventCount,
2553
+ transcript
2554
+ }
2555
+ });
2556
+ return;
2557
+ }
2558
+ if (result.exitCode !== 0) {
2559
+ log(`host exited ${result.exitCode}`, opts);
2560
+ if (result.stderr.length > 0) log(result.stderr.trim(), opts);
2561
+ }
2562
+ success(`usage: ${formatUsage(result.usage)} (API-equivalent estimate, not billed)`, opts);
2563
+ log(`transcript: ${transcript}`, opts);
2564
+ } catch (err) {
2565
+ fail(
2566
+ EXIT.ERROR,
2567
+ `failed to run host '${host}': ${err instanceof Error ? err.message : String(err)}`,
2568
+ opts
2569
+ );
2570
+ }
2571
+ }
2572
+ function writeTranscript(host, lines) {
2573
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2574
+ const dir = join2(process.cwd(), ".noir", "transcripts");
2575
+ const file = join2(dir, `${host}-${ts}.jsonl`);
2576
+ try {
2577
+ mkdirSync2(dir, { recursive: true });
2578
+ writeFileSync2(file, `${lines.join("\n")}${lines.length > 0 ? "\n" : ""}`);
2579
+ } catch {
2580
+ return "(not persisted)";
2581
+ }
2582
+ return file;
2583
+ }
2584
+
2324
2585
  // src/commands/skills.ts
2325
2586
  import { claudeAdapter } from "@noir-ai/adapters";
2326
2587
  import { loadProjectInfo as loadProjectInfo7 } from "@noir-ai/core";
@@ -2640,8 +2901,8 @@ function createProgram() {
2640
2901
  // them), errors → stderr. --json data is emitted by command actions via
2641
2902
  // process.stdout directly, NOT by commander's writeOut, so routing help/
2642
2903
  // version to stdout does not conflict with --json payloads.
2643
- writeOut: (str) => process.stdout.write(str),
2644
- writeErr: (str) => process.stderr.write(str)
2904
+ writeOut: (str2) => process.stdout.write(str2),
2905
+ writeErr: (str2) => process.stderr.write(str2)
2645
2906
  });
2646
2907
  program2.hook("preAction", (_thisCmd, actionCmd) => {
2647
2908
  const opts = actionCmd.optsWithGlobals();
@@ -2672,7 +2933,7 @@ function createProgram() {
2672
2933
  new Option(
2673
2934
  "--host <id>",
2674
2935
  "target agentic CLI (default: claude) \u2014 drives host-side emission"
2675
- ).choices(SUPPORTED_HOSTS)
2936
+ ).choices(SUPPORTED_HOSTS2)
2676
2937
  ).action(
2677
2938
  async (opts, cmd) => {
2678
2939
  const transport = opts.transport === "streamable-http" ? "streamable-http" : "stdio";
@@ -2697,7 +2958,7 @@ function createProgram() {
2697
2958
  new Option(
2698
2959
  "--host <id>",
2699
2960
  "target agentic CLI (default: claude) \u2014 drives host-side emission"
2700
- ).choices(SUPPORTED_HOSTS)
2961
+ ).choices(SUPPORTED_HOSTS2)
2701
2962
  ).action(
2702
2963
  async (dir, opts, cmd) => {
2703
2964
  const transport = opts.transport === "streamable-http" ? "streamable-http" : "stdio";
@@ -2740,7 +3001,7 @@ function createProgram() {
2740
3001
  new Option(
2741
3002
  "--host <id>",
2742
3003
  "override the configured host (advanced; default reads .noir/config.yml)"
2743
- ).choices(SUPPORTED_HOSTS)
3004
+ ).choices(SUPPORTED_HOSTS2)
2744
3005
  ).action(
2745
3006
  async (opts, cmd) => {
2746
3007
  const { sync } = await import("./sync-Y7MTQ4JC.js");
@@ -3059,7 +3320,7 @@ function createProgram() {
3059
3320
  );
3060
3321
  installCmd.alias("migrate");
3061
3322
  program2.command("update").description("update Noir to the latest version via the active install method").option("--check", "check for a new version without changing anything").argument("[spec]", "channel ('latest'|'beta') or exact version (default: latest)").action(async (spec, opts, cmd) => {
3062
- const { update } = await import("./update-VIQUC3DT.js");
3323
+ const { update } = await import("./update-OM2EP2F5.js");
3063
3324
  const cli = cmd.optsWithGlobals();
3064
3325
  await update({ ...cli, spec, check: opts.check === true });
3065
3326
  });
@@ -3120,6 +3381,16 @@ function createProgram() {
3120
3381
  };
3121
3382
  await runPalette2(opts, dispatch);
3122
3383
  });
3384
+ program2.command("run").description("drive the host CLI headless and render its stream-json (v2)").argument("[prompt...]", "prompt to send to the host").addOption(new Option("--host <id>", "host to drive (default claude)").choices(SUPPORTED_HOSTS2)).option("--command <binary>", "custom host binary (e.g. claude-work)").action(async (...args) => {
3385
+ const g = trailingCmd(args).optsWithGlobals();
3386
+ const prompt = args.filter((a) => typeof a === "string").join(" ").trim();
3387
+ const opts = {
3388
+ ...toCliOptions(g),
3389
+ ...typeof g.host === "string" ? { host: g.host } : {},
3390
+ ...typeof g.command === "string" ? { command: g.command } : {}
3391
+ };
3392
+ await run(prompt, opts);
3393
+ });
3123
3394
  program2.action(async (...args) => {
3124
3395
  const cmd = trailingCmd(args);
3125
3396
  const leftovers = cmd.args;
@@ -3149,7 +3420,7 @@ var homeDeps = {
3149
3420
  // uses — so the menu cannot drift from the commander tree.
3150
3421
  commands: buildPaletteCommands(createProgram())
3151
3422
  };
3152
- async function run(argv = []) {
3423
+ async function run2(argv = []) {
3153
3424
  try {
3154
3425
  await program.parseAsync([...argv], { from: "user" });
3155
3426
  } catch (err) {
@@ -3167,12 +3438,12 @@ var isMainModule = (() => {
3167
3438
  }
3168
3439
  })();
3169
3440
  if (isMainModule) {
3170
- void run(process.argv.slice(2));
3441
+ void run2(process.argv.slice(2));
3171
3442
  }
3172
3443
 
3173
3444
  // src/tui/App.tsx
3174
- import { Box as Box6, Text as Text9, useApp, useInput as useInput3 } from "ink";
3175
- import { useEffect, useState as useState4 } from "react";
3445
+ import { Box as Box5, Text as Text8, useApp, useInput } from "ink";
3446
+ import { useEffect, useState as useState2 } from "react";
3176
3447
 
3177
3448
  // src/tui/CommandInput.tsx
3178
3449
  import { Box, Text } from "ink";
@@ -3228,14 +3499,30 @@ async function captureProcessOutput(fn) {
3228
3499
 
3229
3500
  // src/tui/Footer.tsx
3230
3501
  import { Text as Text2 } from "ink";
3502
+
3503
+ // src/tui/hints.ts
3504
+ var FOOTER_HINT = "?/h/Ctrl+K palette \xB7 Ctrl+F find \xB7 \u2191/\u2193 scroll \xB7 Enter run \xB7 q/Esc quit \xB7 Ctrl+C exit";
3505
+ var RUNNING_HINT = "running\u2026 (Ctrl+C to force exit)";
3506
+ var LIST_NAV_HINT = "\u2191/\u2193 navigate \xB7 Enter run \xB7 Esc close \xB7 Tab corpus";
3507
+ var HELP_ENTRIES = [
3508
+ { keys: "/command", desc: "run a Noir sub-command (e.g. /status, /sync, /task next)" },
3509
+ { keys: "Enter", desc: "run the typed /command" },
3510
+ { keys: "Esc", desc: "back: clear input \u2192 dismiss output \u2192 quit" },
3511
+ { keys: "q", desc: "quit (when the input is empty)" },
3512
+ { keys: "\u2191 / \u2193", desc: "scroll the output pane (dashboard) \xB7 navigate the list (palette)" },
3513
+ { keys: "?", desc: "palette \u2014 help & keybindings (this list)" },
3514
+ { keys: "h", desc: "palette \u2014 commands (quick actions + all commands)" },
3515
+ { keys: "Ctrl+K", desc: "palette \u2014 all commands (fuzzy search)" },
3516
+ { keys: "Ctrl+F", desc: "palette \u2014 find in the dispatched output" },
3517
+ { keys: "Tab", desc: "switch the palette corpus" },
3518
+ { keys: "y / n", desc: "approve / decline a destructive command prompt" },
3519
+ { keys: "Ctrl+C", desc: "force exit" }
3520
+ ];
3521
+
3522
+ // src/tui/Footer.tsx
3231
3523
  import { jsx as jsx2 } from "react/jsx-runtime";
3232
3524
  function Footer({ running }) {
3233
- if (running) {
3234
- return /* @__PURE__ */ jsx2(Text2, { children: c.dim("running\u2026 (Ctrl+C to force exit)") });
3235
- }
3236
- return /* @__PURE__ */ jsx2(Text2, { children: c.dim(
3237
- "? help \xB7 q/Esc quit \xB7 \u2191/\u2193 scroll \xB7 Enter run \xB7 Ctrl+K palette \xB7 Ctrl+F find \xB7 Ctrl+C exit"
3238
- ) });
3525
+ return /* @__PURE__ */ jsx2(Text2, { children: c.dim(running ? RUNNING_HINT : FOOTER_HINT) });
3239
3526
  }
3240
3527
 
3241
3528
  // src/tui/format.ts
@@ -3266,105 +3553,15 @@ function Header({ tagline }) {
3266
3553
  ] });
3267
3554
  }
3268
3555
 
3269
- // src/tui/HomeMenu.tsx
3270
- import { Box as Box3, Text as Text4, useInput } from "ink";
3271
- import { useState } from "react";
3272
-
3273
- // src/tui/Panel.tsx
3274
- import { Box as Box2 } from "ink";
3275
- import { jsx as jsx4 } from "react/jsx-runtime";
3276
- function Panel({ children, maxWidth }) {
3277
- const full = contentWidth() + 4;
3278
- const width = maxWidth != null ? Math.min(full, maxWidth) : full;
3279
- return /* @__PURE__ */ jsx4(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, width, children });
3280
- }
3281
-
3282
- // src/tui/HomeMenu.tsx
3283
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
3284
- function flatten(sections) {
3285
- const rows = [];
3286
- for (const section of sections) {
3287
- for (const action of section.items) {
3288
- rows.push({ section, action });
3289
- }
3290
- }
3291
- return rows;
3292
- }
3293
- function HomeMenu({ sections, onSelect, onClose }) {
3294
- const [active, setActive] = useState(0);
3295
- const rows = flatten(sections);
3296
- const activeRow = rows[Math.min(Math.max(active, 0), Math.max(0, rows.length - 1))];
3297
- useInput((input, key) => {
3298
- if (key.escape || key.tab) {
3299
- onClose();
3300
- return;
3301
- }
3302
- if (key.return) {
3303
- if (activeRow) onSelect(activeRow.action);
3304
- return;
3305
- }
3306
- if (key.upArrow) {
3307
- setActive((i) => Math.max(0, i - 1));
3308
- return;
3309
- }
3310
- if (key.downArrow) {
3311
- setActive((i) => Math.min(rows.length - 1, i + 1));
3312
- return;
3313
- }
3314
- if (input === "k") {
3315
- setActive((i) => Math.max(0, i - 1));
3316
- return;
3317
- }
3318
- if (input === "j") {
3319
- setActive((i) => Math.min(rows.length - 1, i + 1));
3320
- return;
3321
- }
3322
- });
3323
- const rowsEl = [];
3324
- let lastSectionId = null;
3325
- for (let i = 0; i < rows.length; i++) {
3326
- const row = rows[i];
3327
- if (!row) continue;
3328
- const { section, action } = row;
3329
- if (section.id !== lastSectionId) {
3330
- rowsEl.push(/* @__PURE__ */ jsx5(Text4, { children: c.dim(`\u2500\u2500 ${section.label} \u2500\u2500`) }, `sec:${section.id}`));
3331
- lastSectionId = section.id;
3332
- }
3333
- const isActive = i === activeRowIndex(active, rows);
3334
- const prefix = isActive ? "\u25B8 " : " ";
3335
- rowsEl.push(
3336
- /* @__PURE__ */ jsxs3(Text4, { children: [
3337
- prefix,
3338
- isActive ? c.accent(action.label) : action.label,
3339
- /* @__PURE__ */ jsx5(Text4, { children: c.dim(` ${action.hint}`) })
3340
- ] }, action.id)
3341
- );
3342
- }
3343
- return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
3344
- /* @__PURE__ */ jsxs3(Panel, { children: [
3345
- /* @__PURE__ */ jsx5(Box3, { paddingX: 1, children: /* @__PURE__ */ jsxs3(Text4, { children: [
3346
- c.bold("\u25B8 home "),
3347
- /* @__PURE__ */ jsx5(Text4, { children: c.dim(`quick actions (${rows.length})`) })
3348
- ] }) }),
3349
- rowsEl.map((row) => /* @__PURE__ */ jsx5(Box3, { paddingX: 1, children: row }, row.key ?? "row"))
3350
- ] }),
3351
- /* @__PURE__ */ jsx5(Text4, { children: c.dim("\u2191/\u2193 navigate \xB7 Enter run \xB7 Esc close") })
3352
- ] });
3353
- }
3354
- function activeRowIndex(active, rows) {
3355
- return Math.min(Math.max(active, 0), Math.max(0, rows.length - 1));
3356
- }
3357
-
3358
3556
  // src/tui/hooks/useInputBuffer.ts
3359
- import { useRef, useState as useState2 } from "react";
3557
+ import { useRef, useState } from "react";
3360
3558
  function useInputBuffer() {
3361
- const [buffer, setBuffer] = useState2("");
3559
+ const [buffer, setBuffer] = useState("");
3362
3560
  const historyRef = useRef([]);
3363
3561
  const cursorRef = useRef(-1);
3364
3562
  function pushHistory(text) {
3365
3563
  if (text.length === 0) return;
3366
- const history = historyRef.current;
3367
- if (history.length > 0 && history[0] === text) return;
3564
+ const history = historyRef.current.filter((e) => e !== text);
3368
3565
  historyRef.current = [text, ...history];
3369
3566
  }
3370
3567
  function recall(dir) {
@@ -3383,12 +3580,16 @@ function useInputBuffer() {
3383
3580
  setBuffer(() => "");
3384
3581
  cursorRef.current = -1;
3385
3582
  }
3386
- return { buffer, setBuffer, pushHistory, recall, clear };
3583
+ function seed(entries) {
3584
+ historyRef.current = [...entries];
3585
+ cursorRef.current = -1;
3586
+ }
3587
+ return { buffer, setBuffer, pushHistory, recall, clear, seed };
3387
3588
  }
3388
3589
 
3389
3590
  // src/tui/OutputPane.tsx
3390
- import { Text as Text5 } from "ink";
3391
- import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
3591
+ import { Text as Text4 } from "ink";
3592
+ import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
3392
3593
  function toRows(visible, baseIndex) {
3393
3594
  const rows = [];
3394
3595
  const seen = /* @__PURE__ */ new Map();
@@ -3422,13 +3623,13 @@ function OutputPane({
3422
3623
  }) {
3423
3624
  const width = contentWidth();
3424
3625
  if (lines.length === 0) {
3425
- return /* @__PURE__ */ jsx6(Text5, { children: c.dim("(no output \u2014 type a /command, or ? for help)") });
3626
+ return /* @__PURE__ */ jsx4(Text4, { children: c.dim("(no output \u2014 type a /command, or ? for help)") });
3426
3627
  }
3427
3628
  const maxOffset = Math.max(0, lines.length - height);
3428
3629
  const offset = Math.min(Math.max(0, scrollOffset), maxOffset);
3429
3630
  const rows = toRows(lines.slice(offset, offset + height), offset);
3430
- return /* @__PURE__ */ jsxs4(Fragment, { children: [
3431
- title !== void 0 ? /* @__PURE__ */ jsx6(Text5, { wrap: "truncate-end", children: c.dim(`\u2500\u2500 ${title} \u2500\u2500`) }) : null,
3631
+ return /* @__PURE__ */ jsxs3(Fragment, { children: [
3632
+ title !== void 0 ? /* @__PURE__ */ jsx4(Text4, { wrap: "truncate-end", children: c.dim(`\u2500\u2500 ${title} \u2500\u2500`) }) : null,
3432
3633
  rows.map((row) => {
3433
3634
  const truncated = row.text.length > width ? `${row.text.slice(0, Math.max(1, width - 1))}\u2026` : row.text;
3434
3635
  let body = truncated;
@@ -3437,32 +3638,32 @@ function OutputPane({
3437
3638
  } else if (highlightQuery !== void 0 && highlightQuery.length > 0) {
3438
3639
  body = highlightFirst(truncated, highlightQuery);
3439
3640
  }
3440
- return /* @__PURE__ */ jsx6(Text5, { wrap: "truncate-end", children: body }, row.key);
3641
+ return /* @__PURE__ */ jsx4(Text4, { wrap: "truncate-end", children: body }, row.key);
3441
3642
  })
3442
3643
  ] });
3443
3644
  }
3444
3645
 
3445
3646
  // src/tui/overlays/ConfirmOverlay.tsx
3446
- import { Box as Box4, Text as Text6 } from "ink";
3447
- import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
3647
+ import { Box as Box3, Text as Text5 } from "ink";
3648
+
3649
+ // src/tui/Panel.tsx
3650
+ import { Box as Box2 } from "ink";
3651
+ import { jsx as jsx5 } from "react/jsx-runtime";
3652
+ function Panel({ children, maxWidth }) {
3653
+ const full = contentWidth() + 4;
3654
+ const width = maxWidth != null ? Math.min(full, maxWidth) : full;
3655
+ return /* @__PURE__ */ jsx5(Box2, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, width, children });
3656
+ }
3657
+
3658
+ // src/tui/overlays/ConfirmOverlay.tsx
3659
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
3448
3660
  function ConfirmOverlay({ argv }) {
3449
- return /* @__PURE__ */ jsx7(Panel, { children: /* @__PURE__ */ jsx7(Box4, { paddingX: 1, children: /* @__PURE__ */ jsxs5(Text6, { children: [
3661
+ return /* @__PURE__ */ jsx6(Panel, { children: /* @__PURE__ */ jsx6(Box3, { paddingX: 1, children: /* @__PURE__ */ jsxs4(Text5, { children: [
3450
3662
  c.warn(`! run /${argv.join(" ")}? (y/N)`),
3451
- /* @__PURE__ */ jsx7(Text6, { children: c.dim(" [y approve \xB7 n/Esc back to palette]") })
3663
+ /* @__PURE__ */ jsx6(Text5, { children: c.dim(" [y approve \xB7 n/Esc back to palette]") })
3452
3664
  ] }) }) });
3453
3665
  }
3454
3666
 
3455
- // src/tui/overlays/SearchMode.ts
3456
- function computeMatches(lines, query) {
3457
- if (query.length === 0) return [];
3458
- const needle = query.toLowerCase();
3459
- const out = [];
3460
- for (let i = 0; i < lines.length; i++) {
3461
- if ((lines[i] ?? "").toLowerCase().includes(needle)) out.push(i);
3462
- }
3463
- return out;
3464
- }
3465
-
3466
3667
  // src/tui/palette/fuzzyMatch.ts
3467
3668
  var WEIGHTS = {
3468
3669
  label: 1,
@@ -3556,141 +3757,208 @@ var handRolledMatcher = {
3556
3757
  };
3557
3758
 
3558
3759
  // src/tui/palette/Palette.tsx
3559
- import { Box as Box5, Text as Text7, useInput as useInput2 } from "ink";
3560
- import { useState as useState3 } from "react";
3561
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
3760
+ import { Box as Box4, Text as Text6 } from "ink";
3761
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
3562
3762
  var PALETTE_WIDTH = 64;
3563
3763
  var VISIBLE_ROWS = 10;
3564
- var MATCH_LIMIT = 20;
3565
- function rankPalette(query, matcher, commands, recent) {
3566
- if (query.length === 0) {
3567
- const recents = recentDeduped(recent, commands);
3568
- const recentIds = new Set(recents.map((r) => r.id));
3569
- const rest = commands.filter((cmd) => !recentIds.has(cmd.id));
3570
- return [
3571
- ...recents.map((cmd) => ({ cmd, indices: [] })),
3572
- ...rest.map((cmd) => ({ cmd, indices: [] }))
3573
- ];
3574
- }
3575
- return matcher.search(query, commands, MATCH_LIMIT).map((m) => ({
3576
- cmd: m.item,
3577
- indices: m.matchedIndices
3578
- }));
3579
- }
3580
- function truncateLabel(label) {
3581
- const max = Math.max(20, PALETTE_WIDTH - 8);
3582
- return label.length > max ? `${label.slice(0, max - 1)}\u2026` : label;
3583
- }
3584
3764
  function highlight(label, matchedIndices) {
3765
+ if (matchedIndices.length === 0) return /* @__PURE__ */ jsx7(Fragment2, { children: label });
3585
3766
  const set = new Set(matchedIndices);
3586
3767
  const chars = [];
3587
3768
  for (let i = 0; i < label.length; i++) {
3588
3769
  const ch = label[i] ?? "";
3589
- chars.push(set.has(i) ? /* @__PURE__ */ jsx8(Text7, { children: c.accent(ch) }, i) : /* @__PURE__ */ jsx8(Text7, { children: ch }, i));
3770
+ chars.push(set.has(i) ? /* @__PURE__ */ jsx7(Text6, { children: c.accent(ch) }, i) : /* @__PURE__ */ jsx7(Text6, { children: ch }, i));
3590
3771
  }
3591
- return /* @__PURE__ */ jsx8(Fragment2, { children: chars });
3772
+ return /* @__PURE__ */ jsx7(Fragment2, { children: chars });
3592
3773
  }
3593
- function Palette({
3594
- commands,
3595
- matcher,
3596
- recent,
3597
- onSelect,
3598
- onClose
3599
- }) {
3600
- const [query, setQuery] = useState3("");
3601
- const [activeIndex, setActiveIndex] = useState3(0);
3602
- const ranked = rankPalette(query, matcher, commands, recent);
3603
- const visible = ranked.slice(0, VISIBLE_ROWS);
3604
- const active = Math.min(Math.max(activeIndex, 0), Math.max(0, visible.length - 1));
3605
- const recents = recentDeduped(recent, commands);
3606
- useInput2((input, key) => {
3607
- if (key.escape) {
3608
- onClose();
3609
- return;
3610
- }
3611
- if (key.return) {
3612
- const hit = visible[active];
3613
- if (hit) onSelect(hit.cmd);
3614
- return;
3615
- }
3616
- if (key.upArrow) {
3617
- setActiveIndex((i) => Math.max(0, i - 1));
3618
- return;
3619
- }
3620
- if (key.downArrow) {
3621
- setActiveIndex((i) => i + 1);
3622
- return;
3623
- }
3624
- if (key.backspace || key.delete) {
3625
- setQuery((q) => q.slice(0, -1));
3626
- setActiveIndex(0);
3627
- return;
3628
- }
3629
- if (key.ctrl) return;
3630
- if (input.length > 0) {
3631
- setQuery((q) => q + input);
3632
- setActiveIndex(0);
3633
- }
3634
- });
3635
- const rows = [
3636
- /* @__PURE__ */ jsx8(Box5, { paddingX: 1, children: /* @__PURE__ */ jsxs6(Text7, { children: [
3774
+ function truncate2(label) {
3775
+ const max = Math.max(20, PALETTE_WIDTH - 8);
3776
+ return label.length > max ? `${label.slice(0, max - 1)}\u2026` : label;
3777
+ }
3778
+ function placeholder(corpus) {
3779
+ if (corpus === "commands") return "type to search commands";
3780
+ if (corpus === "output") return "type to filter output";
3781
+ return "keybindings";
3782
+ }
3783
+ function Palette({ corpus, query, active, rows }) {
3784
+ const visible = rows.slice(0, VISIBLE_ROWS);
3785
+ const activeIndex = Math.min(Math.max(active, 0), Math.max(0, visible.length - 1));
3786
+ const elements = [
3787
+ /* @__PURE__ */ jsx7(Box4, { paddingX: 1, children: /* @__PURE__ */ jsxs5(Text6, { children: [
3637
3788
  c.bold("\u25B8 palette "),
3638
- /* @__PURE__ */ jsx8(Text7, { children: c.dim(`search (${visible.length} shown)`) })
3789
+ /* @__PURE__ */ jsx7(Text6, { children: c.dim(`(${corpus} \xB7 ${visible.length} shown)`) })
3639
3790
  ] }) }, "header"),
3640
- /* @__PURE__ */ jsx8(Box5, { paddingX: 1, children: /* @__PURE__ */ jsxs6(Text7, { children: [
3791
+ /* @__PURE__ */ jsx7(Box4, { paddingX: 1, children: /* @__PURE__ */ jsxs5(Text6, { children: [
3641
3792
  c.dim("> "),
3642
- query,
3643
- /* @__PURE__ */ jsx8(Text7, { children: c.dim("\u258C") })
3793
+ query.length > 0 ? query : c.dim(placeholder(corpus)),
3794
+ /* @__PURE__ */ jsx7(Text6, { children: c.dim("\u258C") })
3644
3795
  ] }) }, "query")
3645
3796
  ];
3646
- let lastCategory = null;
3647
- for (const { cmd, indices } of visible) {
3648
- const isRecent = recents.some((r) => r.id === cmd.id);
3649
- if (query.length === 0) {
3650
- if (isRecent && lastCategory !== "recent") {
3651
- rows.push(
3652
- /* @__PURE__ */ jsx8(Box5, { paddingX: 1, children: /* @__PURE__ */ jsx8(Text7, { children: c.dim("\u2500\u2500 recent \u2500\u2500") }) }, "cat:recent")
3653
- );
3654
- lastCategory = "recent";
3655
- } else if (!isRecent && cmd.category !== lastCategory) {
3656
- rows.push(
3657
- /* @__PURE__ */ jsx8(Box5, { paddingX: 1, children: /* @__PURE__ */ jsx8(Text7, { children: c.dim(`\u2500\u2500 ${cmd.category} \u2500\u2500`) }) }, `cat:${cmd.category}`)
3658
- );
3659
- lastCategory = cmd.category;
3660
- }
3797
+ let lastGroup = null;
3798
+ for (let i = 0; i < visible.length; i++) {
3799
+ const row = visible[i];
3800
+ if (!row) continue;
3801
+ if (row.group !== null && row.group !== lastGroup) {
3802
+ elements.push(
3803
+ /* @__PURE__ */ jsx7(Box4, { paddingX: 1, children: /* @__PURE__ */ jsx7(Text6, { children: c.dim(`\u2500\u2500 ${row.group} \u2500\u2500`) }) }, `cat:${row.group}`)
3804
+ );
3805
+ lastGroup = row.group;
3661
3806
  }
3662
- const prefix = cmd.id === visible[active]?.cmd.id ? "\u25B8 " : " ";
3663
- rows.push(
3664
- /* @__PURE__ */ jsx8(Box5, { paddingX: 1, children: /* @__PURE__ */ jsxs6(Text7, { children: [
3665
- isRecent && query.length === 0 ? /* @__PURE__ */ jsx8(Text7, { children: c.dim("\u21BA ") }) : null,
3666
- prefix,
3667
- highlight(truncateLabel(cmd.label), indices),
3668
- /* @__PURE__ */ jsx8(Text7, { children: c.dim(` ${cmd.description}`) })
3669
- ] }) }, cmd.id)
3807
+ const isActive = i === activeIndex;
3808
+ elements.push(
3809
+ /* @__PURE__ */ jsx7(Box4, { paddingX: 1, children: /* @__PURE__ */ jsxs5(Text6, { children: [
3810
+ isActive ? "\u25B8 " : " ",
3811
+ isActive ? c.accent(truncate2(row.primary)) : highlight(truncate2(row.primary), row.indices),
3812
+ /* @__PURE__ */ jsx7(Text6, { children: c.dim(` ${row.secondary}`) })
3813
+ ] }) }, row.key)
3670
3814
  );
3671
3815
  }
3672
- return /* @__PURE__ */ jsxs6(Box5, { flexDirection: "column", children: [
3673
- /* @__PURE__ */ jsx8(Panel, { maxWidth: PALETTE_WIDTH, children: rows }),
3674
- /* @__PURE__ */ jsx8(Text7, { children: c.dim("\u2191/\u2193 navigate \xB7 Enter run \xB7 Esc close") })
3816
+ return /* @__PURE__ */ jsxs5(Box4, { flexDirection: "column", children: [
3817
+ /* @__PURE__ */ jsx7(Panel, { maxWidth: PALETTE_WIDTH, children: elements }),
3818
+ /* @__PURE__ */ jsx7(Text6, { children: c.dim(LIST_NAV_HINT) })
3675
3819
  ] });
3676
3820
  }
3821
+
3822
+ // src/tui/palette/output.ts
3823
+ function computeMatches(lines, query) {
3824
+ if (query.length === 0) return [];
3825
+ const needle = query.toLowerCase();
3826
+ const out = [];
3827
+ for (let i = 0; i < lines.length; i++) {
3828
+ if ((lines[i] ?? "").toLowerCase().includes(needle)) out.push(i);
3829
+ }
3830
+ return out;
3831
+ }
3832
+
3833
+ // src/tui/palette/rows.ts
3834
+ var CORPORA = ["commands", "output", "help"];
3835
+ var MATCH_LIMIT = 20;
3836
+ function flattenHome(sections) {
3837
+ const out = [];
3838
+ for (const section of sections) {
3839
+ for (const action of section.items) out.push({ group: section.label, action });
3840
+ }
3841
+ return out;
3842
+ }
3677
3843
  function recentDeduped(recent, commands) {
3678
3844
  const known = new Set(commands.map((cmd) => cmd.id));
3679
3845
  return recent.filter((cmd) => known.has(cmd.id));
3680
3846
  }
3847
+ function buildPaletteRows(input) {
3848
+ const { corpus, query, commands, matcher, recent, homeSections, outputLines } = input;
3849
+ if (corpus === "help") {
3850
+ return HELP_ENTRIES.map((e) => ({
3851
+ key: `help:${e.keys}`,
3852
+ primary: e.keys,
3853
+ secondary: e.desc,
3854
+ argv: null,
3855
+ destructive: false,
3856
+ indices: [],
3857
+ group: "keybindings"
3858
+ }));
3859
+ }
3860
+ if (corpus === "output") {
3861
+ const matches = computeMatches(outputLines, query);
3862
+ if (matches.length === 0) {
3863
+ if (outputLines.length === 0) {
3864
+ return [
3865
+ {
3866
+ key: "empty",
3867
+ primary: "(no output)",
3868
+ secondary: "run a /command first",
3869
+ argv: null,
3870
+ destructive: false,
3871
+ indices: [],
3872
+ group: null
3873
+ }
3874
+ ];
3875
+ }
3876
+ return outputLines.map((line, i) => ({
3877
+ key: `out:${i}`,
3878
+ primary: line,
3879
+ secondary: String(i + 1),
3880
+ argv: null,
3881
+ destructive: false,
3882
+ indices: [],
3883
+ group: query.length === 0 ? null : "matches"
3884
+ }));
3885
+ }
3886
+ return matches.map((i) => ({
3887
+ key: `out:${i}`,
3888
+ primary: outputLines[i] ?? "",
3889
+ secondary: String(i + 1),
3890
+ argv: null,
3891
+ destructive: false,
3892
+ indices: [],
3893
+ group: "matches"
3894
+ }));
3895
+ }
3896
+ if (query.length === 0) {
3897
+ const rows = [];
3898
+ const recents = recentDeduped(recent, commands);
3899
+ const recentIds = new Set(recents.map((r) => r.id));
3900
+ for (const cmd of recents) {
3901
+ rows.push({
3902
+ key: `recent:${cmd.id}`,
3903
+ primary: cmd.label,
3904
+ secondary: cmd.description,
3905
+ argv: cmd.argv,
3906
+ destructive: cmd.destructive,
3907
+ indices: [],
3908
+ group: "recent"
3909
+ });
3910
+ }
3911
+ const homeIds = /* @__PURE__ */ new Set();
3912
+ for (const { group, action } of flattenHome(homeSections)) {
3913
+ const argv = action.dispatch ?? [action.id];
3914
+ homeIds.add(action.id);
3915
+ rows.push({
3916
+ key: `home:${action.id}`,
3917
+ primary: action.label,
3918
+ secondary: action.hint,
3919
+ argv,
3920
+ destructive: action.destructive ?? false,
3921
+ indices: [],
3922
+ group
3923
+ });
3924
+ }
3925
+ for (const cmd of commands) {
3926
+ if (recentIds.has(cmd.id) || homeIds.has(cmd.id)) continue;
3927
+ rows.push({
3928
+ key: `cmd:${cmd.id}`,
3929
+ primary: cmd.label,
3930
+ secondary: cmd.description,
3931
+ argv: cmd.argv,
3932
+ destructive: cmd.destructive,
3933
+ indices: [],
3934
+ group: cmd.category
3935
+ });
3936
+ }
3937
+ return rows;
3938
+ }
3939
+ return matcher.search(query, commands, MATCH_LIMIT).map((m) => ({
3940
+ key: `cmd:${m.item.id}`,
3941
+ primary: m.item.label,
3942
+ secondary: m.item.description,
3943
+ argv: m.item.argv,
3944
+ destructive: m.item.destructive,
3945
+ indices: m.matchedIndices,
3946
+ group: null
3947
+ }));
3948
+ }
3681
3949
 
3682
3950
  // src/tui/StatusBar.tsx
3683
- import { Text as Text8 } from "ink";
3684
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
3951
+ import { Text as Text7 } from "ink";
3952
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
3685
3953
  function Cell({
3686
3954
  label,
3687
3955
  value,
3688
3956
  accent
3689
3957
  }) {
3690
- return /* @__PURE__ */ jsxs7(Text8, { children: [
3691
- /* @__PURE__ */ jsx9(Text8, { children: c.dim(`${label}:`) }),
3958
+ return /* @__PURE__ */ jsxs6(Text7, { children: [
3959
+ /* @__PURE__ */ jsx8(Text7, { children: c.dim(`${label}:`) }),
3692
3960
  " ",
3693
- /* @__PURE__ */ jsx9(Text8, { children: accent ? c.accent(value) : value })
3961
+ /* @__PURE__ */ jsx8(Text7, { children: accent ? c.accent(value) : value })
3694
3962
  ] });
3695
3963
  }
3696
3964
  function StatusBar({ payload, loading }) {
@@ -3699,14 +3967,14 @@ function StatusBar({ payload, loading }) {
3699
3967
  const phase = payload?.workflow?.phase ?? (loading ? "\u2026" : "idle");
3700
3968
  const daemonRunning = payload?.daemon.running === true;
3701
3969
  const daemon = payload === null && loading ? "\u2026" : daemonRunning ? "up" : "down";
3702
- return /* @__PURE__ */ jsxs7(Text8, { children: [
3703
- /* @__PURE__ */ jsx9(Cell, { label: "host", value: host, accent: true }),
3704
- /* @__PURE__ */ jsx9(Text8, { children: c.dim(" \xB7 ") }),
3705
- /* @__PURE__ */ jsx9(Cell, { label: "mode", value: mode }),
3706
- /* @__PURE__ */ jsx9(Text8, { children: c.dim(" \xB7 ") }),
3707
- /* @__PURE__ */ jsx9(Cell, { label: "phase", value: phase }),
3708
- /* @__PURE__ */ jsx9(Text8, { children: c.dim(" \xB7 ") }),
3709
- /* @__PURE__ */ jsxs7(Text8, { children: [
3970
+ return /* @__PURE__ */ jsxs6(Text7, { children: [
3971
+ /* @__PURE__ */ jsx8(Cell, { label: "host", value: host, accent: true }),
3972
+ /* @__PURE__ */ jsx8(Text7, { children: c.dim(" \xB7 ") }),
3973
+ /* @__PURE__ */ jsx8(Cell, { label: "mode", value: mode }),
3974
+ /* @__PURE__ */ jsx8(Text7, { children: c.dim(" \xB7 ") }),
3975
+ /* @__PURE__ */ jsx8(Cell, { label: "phase", value: phase }),
3976
+ /* @__PURE__ */ jsx8(Text7, { children: c.dim(" \xB7 ") }),
3977
+ /* @__PURE__ */ jsxs6(Text7, { children: [
3710
3978
  c.dim("daemon:"),
3711
3979
  " ",
3712
3980
  daemonRunning ? c.ok(daemon) : c.warn(daemon)
@@ -3715,7 +3983,7 @@ function StatusBar({ payload, loading }) {
3715
3983
  }
3716
3984
 
3717
3985
  // src/tui/App.tsx
3718
- import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
3986
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
3719
3987
  function App({
3720
3988
  deps,
3721
3989
  initialPayload = null,
@@ -3723,31 +3991,35 @@ function App({
3723
3991
  initialMode = { kind: "dashboard" }
3724
3992
  }) {
3725
3993
  const { exit } = useApp();
3726
- const { buffer, setBuffer, pushHistory, recall, clear } = useInputBuffer();
3727
- const [mode, setMode] = useState4(initialMode);
3728
- const [payload, setPayload] = useState4(initialPayload);
3729
- const [loading, setLoading] = useState4(initialPayload === null);
3730
- const [output, setOutput] = useState4(null);
3731
- const [searchLines, setSearchLines] = useState4([]);
3732
- const [scrollOffset, setScrollOffset] = useState4(0);
3733
- const [help, setHelp] = useState4(false);
3734
- const [running, setRunning] = useState4(false);
3735
- const [notice, setNotice] = useState4(null);
3736
- const [pending, setPending] = useState4(null);
3737
- const [recent, setRecent] = useState4([]);
3738
- const [homeSections, setHomeSections] = useState4([]);
3994
+ const { buffer, setBuffer, pushHistory, recall, clear, seed } = useInputBuffer();
3995
+ const [mode, setMode] = useState2(initialMode);
3996
+ const [payload, setPayload] = useState2(initialPayload);
3997
+ const [loading, setLoading] = useState2(initialPayload === null);
3998
+ const [output, setOutput] = useState2(null);
3999
+ const [searchLines, setSearchLines] = useState2([]);
4000
+ const [scrollOffset, setScrollOffset] = useState2(0);
4001
+ const [running, setRunning] = useState2(false);
4002
+ const [notice, setNotice] = useState2(null);
4003
+ const [pending, setPending] = useState2(null);
4004
+ const [recent, setRecent] = useState2([]);
4005
+ const [homeSections, setHomeSections] = useState2([]);
4006
+ const [paletteQuery, setPaletteQuery] = useState2("");
4007
+ const [paletteActive, setPaletteActive] = useState2(0);
3739
4008
  useEffect(() => {
3740
4009
  if (!deps.loadRecent) return;
3741
4010
  const loader = deps.loadRecent;
3742
4011
  let cancelled = false;
3743
4012
  void loader().then((entries) => {
3744
- if (!cancelled) setRecent(entries);
4013
+ if (!cancelled) {
4014
+ setRecent(entries);
4015
+ seed(entries.map((c2) => `/${c2.argv.join(" ")}`));
4016
+ }
3745
4017
  }).catch(() => {
3746
4018
  });
3747
4019
  return () => {
3748
4020
  cancelled = true;
3749
4021
  };
3750
- }, [deps.loadRecent]);
4022
+ }, [deps.loadRecent, seed]);
3751
4023
  useEffect(() => {
3752
4024
  let cancelled = false;
3753
4025
  void resolveSections(deps.commands ?? []).then((sections) => {
@@ -3787,7 +4059,7 @@ function App({
3787
4059
  if (pending === null) return;
3788
4060
  const argv = pending;
3789
4061
  let active = true;
3790
- const run2 = async () => {
4062
+ const run3 = async () => {
3791
4063
  const captured = await captureProcessOutput(() => deps.dispatch(argv));
3792
4064
  if (!active) return;
3793
4065
  const combined = `${captured.stdout}${captured.stderr}`;
@@ -3800,7 +4072,7 @@ function App({
3800
4072
  setPending(null);
3801
4073
  setScrollOffset(0);
3802
4074
  };
3803
- void run2();
4075
+ void run3();
3804
4076
  return () => {
3805
4077
  active = false;
3806
4078
  };
@@ -3810,13 +4082,43 @@ function App({
3810
4082
  setRunning(true);
3811
4083
  setPending([...argv]);
3812
4084
  }
4085
+ function handleRun(argv, destructive) {
4086
+ if (destructive) {
4087
+ setMode({ kind: "confirm", argv: [...argv] });
4088
+ return;
4089
+ }
4090
+ dispatchCmd([...argv]);
4091
+ void recordRuns([...argv]);
4092
+ pushHistory(`/${argv.join(" ")}`);
4093
+ setMode({ kind: "dashboard" });
4094
+ }
4095
+ function recordRuns(argv) {
4096
+ const recorder = deps.record;
4097
+ if (recorder) void recorder(argv);
4098
+ }
4099
+ function openPalette(corpus) {
4100
+ setPaletteQuery("");
4101
+ setPaletteActive(0);
4102
+ setMode({ kind: "palette", corpus });
4103
+ setNotice(null);
4104
+ }
4105
+ function openOutputSearch() {
4106
+ if (output === null) return;
4107
+ setSearchLines(output.lines);
4108
+ openPalette("output");
4109
+ }
4110
+ function nextCorpus(current) {
4111
+ const idx = CORPORA.indexOf(current);
4112
+ const candidates = [...CORPORA.slice(idx + 1), ...CORPORA.slice(0, idx + 1)];
4113
+ for (const corpus of candidates) {
4114
+ if (corpus === "output" && output === null) continue;
4115
+ return corpus;
4116
+ }
4117
+ return "commands";
4118
+ }
3813
4119
  function handleDashboardInput(input, key) {
3814
4120
  if (running) return;
3815
4121
  setNotice(null);
3816
- if (help) {
3817
- if (input === "?" || key.escape || input === "q") setHelp(false);
3818
- return;
3819
- }
3820
4122
  if (key.return) {
3821
4123
  submit();
3822
4124
  return;
@@ -3855,11 +4157,11 @@ function App({
3855
4157
  }
3856
4158
  if (key.ctrl) {
3857
4159
  if (input === "k") {
3858
- openPalette();
4160
+ openPalette("commands");
3859
4161
  return;
3860
4162
  }
3861
4163
  if (input === "f" && output !== null) {
3862
- openSearch();
4164
+ openOutputSearch();
3863
4165
  return;
3864
4166
  }
3865
4167
  return;
@@ -3871,11 +4173,11 @@ function App({
3871
4173
  return;
3872
4174
  }
3873
4175
  if (input === "?") {
3874
- setHelp(true);
4176
+ openPalette("help");
3875
4177
  return;
3876
4178
  }
3877
4179
  if (input === "h") {
3878
- setMode({ kind: "home" });
4180
+ openPalette("commands");
3879
4181
  return;
3880
4182
  }
3881
4183
  }
@@ -3886,114 +4188,77 @@ function App({
3886
4188
  setBuffer((b) => b + input);
3887
4189
  }
3888
4190
  }
3889
- function openPalette() {
3890
- setMode({ kind: "palette" });
3891
- setNotice(null);
3892
- }
3893
- function openSearch() {
3894
- if (output === null) return;
3895
- setSearchLines(output.lines);
3896
- setMode({ kind: "search", query: "", matches: [], active: -1 });
3897
- setNotice(null);
3898
- }
3899
- function handlePaletteSelect(cmd) {
3900
- if (cmd.destructive) {
3901
- setMode({ kind: "confirm", argv: [...cmd.argv] });
4191
+ function handlePaletteInput(input, key) {
4192
+ if (mode.kind !== "palette") return;
4193
+ if (key.escape) {
4194
+ setMode({ kind: "dashboard" });
3902
4195
  return;
3903
4196
  }
3904
- dispatchCmd([...cmd.argv]);
3905
- void recordRuns([...cmd.argv]);
3906
- setMode({ kind: "dashboard" });
3907
- }
3908
- function handleHomeSelect(action) {
3909
- const argv = action.dispatch ?? [action.id];
3910
- if (action.destructive) {
3911
- setMode({ kind: "confirm", argv: [...argv] });
4197
+ if (key.tab) {
4198
+ setMode({ kind: "palette", corpus: nextCorpus(mode.corpus) });
4199
+ setPaletteQuery("");
4200
+ setPaletteActive(0);
3912
4201
  return;
3913
4202
  }
3914
- dispatchCmd([...argv]);
3915
- void recordRuns([...argv]);
3916
- setMode({ kind: "dashboard" });
3917
- }
3918
- function handleConfirmApprove() {
3919
- if (mode.kind !== "confirm") return;
3920
- dispatchCmd([...mode.argv]);
3921
- void recordRuns([...mode.argv]);
3922
- setMode({ kind: "dashboard" });
3923
- }
3924
- function handleConfirmDecline() {
3925
- setMode({ kind: "palette" });
3926
- }
3927
- function recordRuns(argv) {
3928
- const recorder = deps.record;
3929
- if (recorder) void recorder(argv);
3930
- }
3931
- function handleConfirmInput(input, key) {
3932
- if (key.escape || input === "n" || input === "N") {
3933
- handleConfirmDecline();
4203
+ if (mode.corpus === "help") {
4204
+ if (key.upArrow) setPaletteActive((a) => Math.max(0, a - 1));
4205
+ else if (key.downArrow) setPaletteActive((a) => a + 1);
3934
4206
  return;
3935
4207
  }
3936
- if (input === "y" || input === "Y") {
3937
- handleConfirmApprove();
4208
+ if (key.return) {
4209
+ if (mode.corpus === "commands") {
4210
+ const row = paletteRows[paletteActive];
4211
+ if (row?.argv) handleRun(row.argv, row.destructive);
4212
+ } else {
4213
+ setPaletteActive((a) => a + 1);
4214
+ }
3938
4215
  return;
3939
4216
  }
3940
- }
3941
- function handleSearchInput(input, key) {
3942
- if (mode.kind !== "search") return;
3943
- if (key.escape) {
3944
- setMode({ kind: "dashboard" });
4217
+ if (key.upArrow) {
4218
+ setPaletteActive((a) => Math.max(0, a - 1));
3945
4219
  return;
3946
4220
  }
3947
- if (key.backspace || key.delete) {
3948
- const nextQuery = mode.query.slice(0, -1);
3949
- setMode(searchFor(nextQuery));
4221
+ if (key.downArrow) {
4222
+ setPaletteActive((a) => a + 1);
3950
4223
  return;
3951
4224
  }
3952
- if (key.ctrl) return;
3953
- if (key.return) {
3954
- advanceActive(1);
4225
+ if (key.backspace || key.delete) {
4226
+ setPaletteQuery((q) => q.slice(0, -1));
4227
+ setPaletteActive(0);
3955
4228
  return;
3956
4229
  }
3957
- if (input === "n" && mode.matches.length > 0) {
3958
- advanceActive(1);
3959
- return;
4230
+ if (key.ctrl) return;
4231
+ if (input.length > 0) {
4232
+ setPaletteQuery((q) => q + input);
4233
+ setPaletteActive(0);
3960
4234
  }
3961
- if (input === "N" && mode.matches.length > 0) {
3962
- advanceActive(-1);
4235
+ }
4236
+ function handleConfirmInput(input, key) {
4237
+ if (key.escape || input === "n" || input === "N") {
4238
+ setPaletteQuery("");
4239
+ setPaletteActive(0);
4240
+ setMode({ kind: "palette", corpus: "commands" });
3963
4241
  return;
3964
4242
  }
3965
- if (input.length > 0) {
3966
- setMode(searchFor(mode.query + input));
4243
+ if (input === "y" || input === "Y") {
4244
+ if (mode.kind !== "confirm") return;
4245
+ dispatchCmd([...mode.argv]);
4246
+ void recordRuns([...mode.argv]);
4247
+ pushHistory(`/${mode.argv.join(" ")}`);
4248
+ setMode({ kind: "dashboard" });
3967
4249
  }
3968
4250
  }
3969
- function searchFor(query) {
3970
- const matches = computeMatches(searchLines, query);
3971
- return { kind: "search", query, matches, active: matches.length > 0 ? 0 : -1 };
3972
- }
3973
- function advanceActive(delta) {
3974
- if (mode.kind !== "search") return;
3975
- const n = mode.matches.length;
3976
- if (n === 0) return;
3977
- const next = (mode.active + delta + n) % n;
3978
- setMode({ ...mode, active: next });
3979
- }
3980
- useInput3((input, key) => {
4251
+ useInput((input, key) => {
3981
4252
  switch (mode.kind) {
3982
4253
  case "dashboard":
3983
4254
  handleDashboardInput(input, key);
3984
4255
  return;
3985
4256
  case "palette":
4257
+ handlePaletteInput(input, key);
3986
4258
  return;
3987
- // Palette renders its own useInput; no App-level keys here
3988
- case "home":
3989
- return;
3990
- // HomeMenu renders its own useInput (S4)
3991
4259
  case "confirm":
3992
4260
  handleConfirmInput(input, key);
3993
4261
  return;
3994
- case "search":
3995
- handleSearchInput(input, key);
3996
- return;
3997
4262
  }
3998
4263
  });
3999
4264
  function submit() {
@@ -4009,140 +4274,75 @@ function App({
4009
4274
  setNotice("empty /command");
4010
4275
  return;
4011
4276
  }
4277
+ if (isDestructive(argv)) {
4278
+ setMode({ kind: "confirm", argv });
4279
+ return;
4280
+ }
4012
4281
  pushHistory(text);
4013
4282
  dispatchCmd(argv);
4014
4283
  void recordRuns(argv);
4015
4284
  }
4016
- if (help) {
4017
- return /* @__PURE__ */ jsx10(Help, {});
4018
- }
4285
+ const paletteCorpus = mode.kind === "palette" ? mode.corpus : "commands";
4286
+ const paletteRows = buildPaletteRows({
4287
+ corpus: paletteCorpus,
4288
+ query: paletteQuery,
4289
+ commands: deps.commands ?? [],
4290
+ matcher: deps.matcher ?? handRolledMatcher,
4291
+ recent,
4292
+ homeSections,
4293
+ outputLines: searchLines
4294
+ });
4019
4295
  if (mode.kind === "palette") {
4020
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
4021
- /* @__PURE__ */ jsx10(Header, { tagline: "palette" }),
4022
- /* @__PURE__ */ jsx10(
4296
+ return /* @__PURE__ */ jsxs7(Box5, { flexDirection: "column", children: [
4297
+ /* @__PURE__ */ jsx9(Header, { tagline: `palette \xB7 ${mode.corpus}` }),
4298
+ /* @__PURE__ */ jsx9(
4023
4299
  Palette,
4024
4300
  {
4025
- commands: deps.commands ?? [],
4026
- matcher: deps.matcher ?? handRolledMatcher,
4027
- recent,
4028
- onSelect: handlePaletteSelect,
4029
- onClose: () => setMode({ kind: "dashboard" })
4030
- }
4031
- ),
4032
- /* @__PURE__ */ jsx10(Footer, { running: false })
4033
- ] });
4034
- }
4035
- if (mode.kind === "home") {
4036
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
4037
- /* @__PURE__ */ jsx10(Header, { tagline: "home" }),
4038
- /* @__PURE__ */ jsx10(
4039
- HomeMenu,
4040
- {
4041
- sections: homeSections,
4042
- onSelect: handleHomeSelect,
4043
- onClose: () => setMode({ kind: "dashboard" })
4301
+ corpus: mode.corpus,
4302
+ query: paletteQuery,
4303
+ active: paletteActive,
4304
+ rows: paletteRows
4044
4305
  }
4045
4306
  ),
4046
- /* @__PURE__ */ jsx10(Footer, { running: false })
4307
+ /* @__PURE__ */ jsx9(Footer, { running: false })
4047
4308
  ] });
4048
4309
  }
4049
4310
  if (mode.kind === "confirm") {
4050
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
4051
- /* @__PURE__ */ jsx10(Header, { tagline: "confirm" }),
4052
- /* @__PURE__ */ jsx10(ConfirmOverlay, { argv: mode.argv }),
4053
- /* @__PURE__ */ jsx10(Footer, { running: false })
4054
- ] });
4055
- }
4056
- if (mode.kind === "search") {
4057
- const activeLine = mode.active >= 0 ? mode.matches[mode.active] : void 0;
4058
- const matchCount = mode.matches.length;
4059
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
4060
- /* @__PURE__ */ jsx10(Header, { tagline: "search" }),
4061
- /* @__PURE__ */ jsx10(Panel, { children: /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(
4062
- OutputPane,
4063
- {
4064
- lines: searchLines,
4065
- scrollOffset,
4066
- title: output?.title,
4067
- highlightQuery: mode.query,
4068
- activeLine
4069
- }
4070
- ) }) }),
4071
- /* @__PURE__ */ jsx10(Box6, { borderStyle: "round", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsxs8(Text9, { children: [
4072
- c.dim("search: "),
4073
- mode.query.length > 0 ? mode.query : c.dim("(type to filter output)"),
4074
- /* @__PURE__ */ jsx10(Text9, { children: c.dim(" \u258C") })
4075
- ] }) }),
4076
- /* @__PURE__ */ jsx10(Text9, { children: c.dim(
4077
- matchCount === 0 ? "no matches \xB7 Esc exit" : `${mode.active + 1}/${matchCount} matches \xB7 n/Enter next \xB7 N prev \xB7 Esc exit`
4078
- ) }),
4079
- /* @__PURE__ */ jsx10(Footer, { running: false })
4311
+ return /* @__PURE__ */ jsxs7(Box5, { flexDirection: "column", children: [
4312
+ /* @__PURE__ */ jsx9(Header, { tagline: "confirm" }),
4313
+ /* @__PURE__ */ jsx9(ConfirmOverlay, { argv: mode.argv }),
4314
+ /* @__PURE__ */ jsx9(Footer, { running: false })
4080
4315
  ] });
4081
4316
  }
4082
4317
  const snapshotLines = formatStatusPayload(payload);
4083
4318
  const paneLines = output?.lines ?? snapshotLines ?? [];
4084
4319
  const paneTitle = output?.title ?? "snapshot";
4085
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
4086
- /* @__PURE__ */ jsx10(Header, { tagline: "dashboard" }),
4087
- /* @__PURE__ */ jsxs8(Panel, { children: [
4088
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(StatusBar, { payload, loading }) }),
4089
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: divider() }) }),
4090
- /* @__PURE__ */ jsx10(Box6, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx10(OutputPane, { lines: paneLines, scrollOffset, title: paneTitle }) })
4320
+ return /* @__PURE__ */ jsxs7(Box5, { flexDirection: "column", children: [
4321
+ /* @__PURE__ */ jsx9(Header, { tagline: "dashboard" }),
4322
+ /* @__PURE__ */ jsxs7(Panel, { children: [
4323
+ /* @__PURE__ */ jsx9(Box5, { paddingX: 1, children: /* @__PURE__ */ jsx9(StatusBar, { payload, loading }) }),
4324
+ /* @__PURE__ */ jsx9(Box5, { paddingX: 1, children: /* @__PURE__ */ jsx9(Text8, { children: divider() }) }),
4325
+ /* @__PURE__ */ jsx9(Box5, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ jsx9(OutputPane, { lines: paneLines, scrollOffset, title: paneTitle }) })
4091
4326
  ] }),
4092
- /* @__PURE__ */ jsx10(CommandInput, { buffer, running }),
4093
- notice !== null ? /* @__PURE__ */ jsx10(Text9, { children: c.dim(notice) }) : null,
4094
- /* @__PURE__ */ jsx10(Footer, { running })
4095
- ] });
4096
- }
4097
- function Help() {
4098
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
4099
- /* @__PURE__ */ jsx10(Header, { tagline: "help" }),
4100
- /* @__PURE__ */ jsxs8(Panel, { children: [
4101
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.bold("Keybindings") }) }),
4102
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" /&lt;command&gt; run a Noir sub-command (e.g. /status, /sync, /task next)") }) }),
4103
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" Enter run the typed /command") }) }),
4104
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" Esc back: clear input \u2192 dismiss output \u2192 quit") }) }),
4105
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" q quit (when the input is empty)") }) }),
4106
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" h open the curated home quick-actions (home menu)") }) }),
4107
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" \u2191 / \u2193 scroll the output pane") }) }),
4108
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" ? toggle this help") }) }),
4109
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" Ctrl+K open the command palette") }) }),
4110
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" Ctrl+F find in the dispatched output pane") }) }),
4111
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" n / N next / previous match in search (Enter = next)") }) }),
4112
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" y / n approve / decline a destructive command prompt") }) }),
4113
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" Ctrl+C force exit") }) }),
4114
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: divider() }) }),
4115
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.bold("Home menu (h)") }) }),
4116
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" The curated quick actions mirror the bare-`noir` home menu:") }) }),
4117
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(
4118
- " Status &amp; context \xB7 Memory \xB7 Workflow \xB7 Setup &amp; maintenance \xB7 Dashboard"
4119
- ) }) }),
4120
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: divider() }) }),
4121
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.bold("Commands") }) }),
4122
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" Dispatched through the same routing as `noir` at the prompt.") }) }),
4123
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(
4124
- " /status /sync /doctor /context search &lt;q&gt; /task next /memory recall &lt;q&gt;"
4125
- ) }) }),
4126
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" Commands that need their own interactive prompts (e.g. a /sync with a") }) }),
4127
- /* @__PURE__ */ jsx10(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text9, { children: c.dim(" conflict) are best run directly \u2014 exit first with q.") }) })
4128
- ] }),
4129
- /* @__PURE__ */ jsx10(Text9, { children: c.dim("press ? / Esc / q to close this help") })
4327
+ /* @__PURE__ */ jsx9(CommandInput, { buffer, running }),
4328
+ notice !== null ? /* @__PURE__ */ jsx9(Text8, { children: c.dim(notice) }) : null,
4329
+ /* @__PURE__ */ jsx9(Footer, { running })
4130
4330
  ] });
4131
4331
  }
4132
4332
 
4133
4333
  // src/tui/palette/history.ts
4134
- import { existsSync, mkdirSync as mkdirSync2, readFileSync } from "fs";
4334
+ import { existsSync, mkdirSync as mkdirSync3, readFileSync } from "fs";
4135
4335
  import { homedir } from "os";
4136
- import { join as join2 } from "path";
4336
+ import { join as join3 } from "path";
4137
4337
  import { atomicWriteFile } from "@noir-ai/core";
4138
4338
  var MAX_ENTRIES = 50;
4139
4339
  var TUI_HISTORY_DISABLE_ENV = "NOIR_DISABLE_TUI_HISTORY";
4140
4340
  var testHome = null;
4141
4341
  function homeDir() {
4142
- return testHome ?? join2(homedir(), ".noir");
4342
+ return testHome ?? join3(homedir(), ".noir");
4143
4343
  }
4144
4344
  function historyFile(projectId) {
4145
- return join2(homeDir(), projectId, "tui-history.json");
4345
+ return join3(homeDir(), projectId, "tui-history.json");
4146
4346
  }
4147
4347
  function isHistoryEnabled() {
4148
4348
  const v = process.env[TUI_HISTORY_DISABLE_ENV];
@@ -4177,8 +4377,8 @@ function recordRecent(projectId, argv) {
4177
4377
  const current = loadRecent(projectId).filter((e) => e.id !== entry.id);
4178
4378
  const next = [entry, ...current].slice(0, MAX_ENTRIES);
4179
4379
  try {
4180
- const dir = join2(homeDir(), projectId);
4181
- mkdirSync2(dir, { recursive: true });
4380
+ const dir = join3(homeDir(), projectId);
4381
+ mkdirSync3(dir, { recursive: true });
4182
4382
  atomicWriteFile(historyFile(projectId), `${JSON.stringify(next, null, 2)}
4183
4383
  `);
4184
4384
  } catch {
@@ -4186,7 +4386,7 @@ function recordRecent(projectId, argv) {
4186
4386
  }
4187
4387
 
4188
4388
  // src/tui/index.tsx
4189
- import { jsx as jsx11 } from "react/jsx-runtime";
4389
+ import { jsx as jsx10 } from "react/jsx-runtime";
4190
4390
  function defaultFetchStatus(opts) {
4191
4391
  return async () => {
4192
4392
  try {
@@ -4199,13 +4399,15 @@ function defaultFetchStatus(opts) {
4199
4399
  async function runTui(opts, dispatch) {
4200
4400
  process.stdout.write("\x1B[2J\x1B[H");
4201
4401
  const deps = await buildTuiDeps(opts, dispatch);
4202
- const instance = render(/* @__PURE__ */ jsx11(App, { deps }));
4402
+ const instance = render(/* @__PURE__ */ jsx10(App, { deps }));
4203
4403
  await instance.waitUntilExit();
4204
4404
  }
4205
4405
  async function runPalette(opts, dispatch) {
4206
4406
  process.stdout.write("\x1B[2J\x1B[H");
4207
4407
  const deps = await buildTuiDeps(opts, dispatch);
4208
- const instance = render(/* @__PURE__ */ jsx11(App, { deps, initialMode: { kind: "palette" } }));
4408
+ const instance = render(
4409
+ /* @__PURE__ */ jsx10(App, { deps, initialMode: { kind: "palette", corpus: "commands" } })
4410
+ );
4209
4411
  await instance.waitUntilExit();
4210
4412
  }
4211
4413
  async function buildTuiDeps(opts, dispatch) {