gaunt-sloth 2.0.0-alpha.10 → 2.0.0-alpha.11

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.
Files changed (34) hide show
  1. package/dist/cli.js +4 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/historyCommand.d.ts +10 -6
  4. package/dist/commands/historyCommand.js +41 -11
  5. package/dist/commands/historyCommand.js.map +1 -1
  6. package/dist/commands/modelsCommand.d.ts +25 -0
  7. package/dist/commands/modelsCommand.js +96 -0
  8. package/dist/commands/modelsCommand.js.map +1 -0
  9. package/dist/tui/components/App.js +151 -3
  10. package/dist/tui/components/App.js.map +1 -1
  11. package/dist/tui/components/DebugPanel.d.ts +16 -3
  12. package/dist/tui/components/DebugPanel.js +23 -5
  13. package/dist/tui/components/DebugPanel.js.map +1 -1
  14. package/dist/tui/components/LiveTurn.d.ts +22 -0
  15. package/dist/tui/components/LiveTurn.js +2 -2
  16. package/dist/tui/components/LiveTurn.js.map +1 -1
  17. package/dist/tui/components/NoticeBar.d.ts +19 -0
  18. package/dist/tui/components/NoticeBar.js +23 -0
  19. package/dist/tui/components/NoticeBar.js.map +1 -0
  20. package/dist/tui/components/Transcript.js +6 -1
  21. package/dist/tui/components/Transcript.js.map +1 -1
  22. package/dist/tui/debugRender.d.ts +26 -0
  23. package/dist/tui/debugRender.js +66 -0
  24. package/dist/tui/debugRender.js.map +1 -1
  25. package/dist/tui/debugSearch.d.ts +40 -0
  26. package/dist/tui/debugSearch.js +73 -0
  27. package/dist/tui/debugSearch.js.map +1 -0
  28. package/dist/tui/slashCommands.d.ts +52 -5
  29. package/dist/tui/slashCommands.js +102 -7
  30. package/dist/tui/slashCommands.js.map +1 -1
  31. package/dist/tui/tuiSessionModule.js +54 -18
  32. package/dist/tui/tuiSessionModule.js.map +1 -1
  33. package/dist/tui/types.d.ts +17 -1
  34. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -11,6 +11,7 @@ import { getCommand } from '#src/commands/getCommand.js';
11
11
  import { configCommand } from '#src/commands/configCommand.js';
12
12
  import { historyCommand } from '#src/commands/historyCommand.js';
13
13
  import { insightsCommand } from '#src/commands/insightsCommand.js';
14
+ import { modelsCommand } from '#src/commands/modelsCommand.js';
14
15
  import { argv, getSlothVersion, readStdin } from '@gaunt-sloth/core/utils/systemUtils.js';
15
16
  import { coerceBooleanOrString } from '@gaunt-sloth/core/utils/consoleUtils.js';
16
17
  const program = new Command();
@@ -74,5 +75,8 @@ configCommand(program, cliConfigOverrides);
74
75
  // default or --db) and do not build the LLM, so they stay decoupled from config/provider setup.
75
76
  historyCommand(program);
76
77
  insightsCommand(program);
78
+ // GS2-6 (B16) — model catalog: lists providers/models enriched with models.dev cost/limit metadata.
79
+ // Read-only; enrichment never gates what `/v1/models` reports as callable.
80
+ modelsCommand(program);
77
81
  await readStdin(program);
78
82
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AAG1F,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAEhF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,gCAAgC,CAAC;KAC7C,OAAO,CAAC,eAAe,EAAE,CAAC;KAC1B,MAAM,CACL,WAAW,EACX,2CAA2C;IACzC,kEAAkE;IAClE,0EAA0E,CAC7E;KACA,MAAM,CAAC,qBAAqB,EAAE,mCAAmC,CAAC;KAClE,MAAM,CAAC,mCAAmC,EAAE,gDAAgD,CAAC;KAC7F,MAAM,CACL,oCAAoC,EACpC,0FAA0F,CAC3F;KACA,MAAM,CAAC,OAAO,EAAE,8EAA8E,CAAC;KAC/F,MAAM,CAAC,UAAU,EAAE,kEAAkE,CAAC;KACtF,SAAS,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;AAEpD,MAAM,kBAAkB,GAA+B,EAAE,CAAC;AAE1D,mDAAmD;AACnD,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AAC3B,IAAI,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;IACtC;;;;OAIG;IACH,kBAAkB,CAAC,OAAO,GAAG,IAAI,CAAC;AACpC,CAAC;AACD,IAAI,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;IACrC,2BAA2B;IAC3B,kBAAkB,CAAC,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;AACzE,CAAC;AACD,IAAI,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,EAAE,CAAC;IAC9C,kBAAkB,CAAC,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;AACjF,CAAC;AAED,4FAA4F;AAC5F,4FAA4F;AAC5F,mFAAmF;AACnF,IAAI,OAAO,CAAC,oBAAoB,CAAC,KAAK,CAAC,KAAK,KAAK,EAAE,CAAC;IAClD,kBAAkB,CAAC,GAAG,GAAG,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;AAEhE,qEAAqE;AACrE,oEAAoE;AACpE,6EAA6E;AAC7E,MAAM,OAAO,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;AACnD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;IAC1B,kBAAkB,CAAC,iBAAiB,GAAG,OAAO,CAAC;AACjD,CAAC;AAED,sEAAsE;AACtE,WAAW,CAAC,OAAO,CAAC,CAAC;AACrB,aAAa,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;AAC3C,SAAS,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;AACvC,UAAU,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;AACxC,WAAW,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;AACzC,WAAW,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;AACzC,WAAW,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;AACzC,UAAU,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;AACxC,UAAU,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;AACxC,aAAa,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;AAC3C,mGAAmG;AACnG,gGAAgG;AAChG,cAAc,CAAC,OAAO,CAAC,CAAC;AACxB,eAAe,CAAC,OAAO,CAAC,CAAC;AACzB,oGAAoG;AACpG,2EAA2E;AAC3E,aAAa,CAAC,OAAO,CAAC,CAAC;AAEvB,MAAM,SAAS,CAAC,OAAO,CAAC,CAAC"}
@@ -1,13 +1,17 @@
1
1
  import { Command } from 'commander';
2
2
  /**
3
- * GS2-7 (B20) — the `gth history` command group over the local, opt-in session store.
3
+ * GS2-7 (B20) / GS2-19 — the `gth history` command group over the local, opt-in session store.
4
4
  *
5
- * - `gth history search <query...>` — FTS5 full-text search across past sessions.
6
- * - `gth history list` — the most recent sessions.
5
+ * - `gth history search <query...>` — FTS5 full-text search across past turns; each hit shows the
6
+ * parent conversation it belongs to (GS2-19).
7
+ * - `gth history list` — the most recent conversations (grouped, with turn count + timespan), the
8
+ * top-level unit since GS2-19 (was a flat per-turn list).
9
+ * - `gth history show <id>` — print a whole conversation thread, all turns in order (GS2-19).
7
10
  *
8
- * Both are READ-ONLY and fail-soft: they open the store with `create: false`, so a missing DB
11
+ * All are READ-ONLY and fail-soft: they open the store with `create: false`, so a missing DB
9
12
  * (history never enabled) simply reports "no history yet" instead of materialising an empty file.
10
- * The DB defaults to the global `~/.gsloth/history.db`; `--db <path>` overrides it. Local only —
11
- * nothing here touches the network.
13
+ * (Opening still migrates a pre-GS2-19 DB in place — see {@link HistoryStore} migrate.) The DB
14
+ * defaults to the global `~/.gsloth/history.db`; `--db <path>` overrides it. Local only — nothing
15
+ * here touches the network.
12
16
  */
13
17
  export declare function historyCommand(program: Command): void;
@@ -1,16 +1,20 @@
1
1
  import { openHistoryStore, resolveHistoryDbPath } from '@gaunt-sloth/core/history/historyStore.js';
2
- import { formatHistoryList, formatSearchResults } from '@gaunt-sloth/core/history/historyFormat.js';
2
+ import { formatConversationList, formatConversationThread, formatSearchResults, } from '@gaunt-sloth/core/history/historyFormat.js';
3
3
  import { display, displayInfo, displayWarning } from '@gaunt-sloth/core/utils/consoleUtils.js';
4
4
  /**
5
- * GS2-7 (B20) — the `gth history` command group over the local, opt-in session store.
5
+ * GS2-7 (B20) / GS2-19 — the `gth history` command group over the local, opt-in session store.
6
6
  *
7
- * - `gth history search <query...>` — FTS5 full-text search across past sessions.
8
- * - `gth history list` — the most recent sessions.
7
+ * - `gth history search <query...>` — FTS5 full-text search across past turns; each hit shows the
8
+ * parent conversation it belongs to (GS2-19).
9
+ * - `gth history list` — the most recent conversations (grouped, with turn count + timespan), the
10
+ * top-level unit since GS2-19 (was a flat per-turn list).
11
+ * - `gth history show <id>` — print a whole conversation thread, all turns in order (GS2-19).
9
12
  *
10
- * Both are READ-ONLY and fail-soft: they open the store with `create: false`, so a missing DB
13
+ * All are READ-ONLY and fail-soft: they open the store with `create: false`, so a missing DB
11
14
  * (history never enabled) simply reports "no history yet" instead of materialising an empty file.
12
- * The DB defaults to the global `~/.gsloth/history.db`; `--db <path>` overrides it. Local only —
13
- * nothing here touches the network.
15
+ * (Opening still migrates a pre-GS2-19 DB in place — see {@link HistoryStore} migrate.) The DB
16
+ * defaults to the global `~/.gsloth/history.db`; `--db <path>` overrides it. Local only — nothing
17
+ * here touches the network.
14
18
  */
15
19
  export function historyCommand(program) {
16
20
  const history = program
@@ -41,7 +45,7 @@ export function historyCommand(program) {
41
45
  });
42
46
  history
43
47
  .command('list')
44
- .description('List the most recent recorded sessions')
48
+ .description('List the most recent recorded conversations')
45
49
  .option('--db <path>', 'path to the history DB (defaults to ~/.gsloth/history.db)')
46
50
  .option('--limit <n>', 'maximum results', '20')
47
51
  .action((options) => {
@@ -52,9 +56,35 @@ export function historyCommand(program) {
52
56
  }
53
57
  try {
54
58
  const limit = clampLimit(options.limit);
55
- const results = store.listRecent(limit);
56
- displayInfo('Recent sessions:');
57
- for (const line of formatHistoryList(results))
59
+ const conversations = store.listConversations(limit);
60
+ displayInfo('Recent conversations:');
61
+ for (const line of formatConversationList(conversations))
62
+ display(line);
63
+ }
64
+ finally {
65
+ store.close();
66
+ }
67
+ });
68
+ history
69
+ .command('show')
70
+ .description('Print a whole conversation thread (all turns in order)')
71
+ .argument('<id>', 'conversation id (from `history list` / `history search`)')
72
+ .option('--db <path>', 'path to the history DB (defaults to ~/.gsloth/history.db)')
73
+ .action((idArg, options) => {
74
+ const store = openHistoryStore(resolveHistoryDbPath(options.db), { create: false });
75
+ if (!store) {
76
+ displayWarning('No session history found. Enable it with `history.enabled: true` in your config.');
77
+ return;
78
+ }
79
+ try {
80
+ const id = Number.parseInt(idArg, 10);
81
+ if (!Number.isFinite(id) || id <= 0) {
82
+ displayWarning(`Invalid conversation id "${idArg}".`);
83
+ return;
84
+ }
85
+ const turns = store.getConversationThread(id);
86
+ displayInfo(`Conversation #${id}:`);
87
+ for (const line of formatConversationThread(turns))
58
88
  display(line);
59
89
  }
60
90
  finally {
@@ -1 +1 @@
1
- {"version":3,"file":"historyCommand.js","sourceRoot":"","sources":["../../src/commands/historyCommand.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"historyCommand.js","sourceRoot":"","sources":["../../src/commands/historyCommand.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { Command } from 'commander';
2
+ import { type ProviderId } from '@gaunt-sloth/core/providers/modelDiscovery.js';
3
+ import { type ModelCatalogEntry } from '@gaunt-sloth/core/providers/modelCatalog.js';
4
+ /**
5
+ * GS2-6 (B16) — `gth models`: list the models available on this machine, enriched with
6
+ * cost / context-limit / capability metadata from models.dev.
7
+ *
8
+ * `/v1/models` discovery stays authoritative for *what is callable* (`modelDiscovery.ts`);
9
+ * models.dev only decorates cloud model ids. A cloud model models.dev has never heard of is still
10
+ * listed (unenriched), and if models.dev is unreachable the whole list still prints — just without
11
+ * metadata. Local/self-hosted providers (ollama) get no catalog lookup at all.
12
+ *
13
+ * - `gth models` — list detected providers and their (enriched) models.
14
+ * - `gth models --refresh` — force a models.dev re-fetch past the cache TTL before listing.
15
+ * - `gth models --provider <id>` — scope the listing to a single provider.
16
+ */
17
+ export declare function modelsCommand(program: Command): void;
18
+ /**
19
+ * Render a model's catalog metadata as a compact bracketed suffix, e.g.
20
+ * `[ctx 200k · out 64k · in $5/1M · out $25/1M · tools · reasoning] *`. Returns '' when there is no
21
+ * enrichment (the model is still listed — it is simply callable-but-unenriched). Costs are US$ per
22
+ * 1M tokens (models.dev's unit); the trailing `*` ties back to the attribution footer.
23
+ */
24
+ export declare function formatEnrichment(entry: ModelCatalogEntry | undefined): string;
25
+ export type { ProviderId };
@@ -0,0 +1,96 @@
1
+ import { detectProviders, } from '@gaunt-sloth/core/providers/modelDiscovery.js';
2
+ import { enrichModels, CATALOG_ATTRIBUTION, } from '@gaunt-sloth/core/providers/modelCatalog.js';
3
+ import { display, displayInfo, displayWarning } from '@gaunt-sloth/core/utils/consoleUtils.js';
4
+ /**
5
+ * GS2-6 (B16) — `gth models`: list the models available on this machine, enriched with
6
+ * cost / context-limit / capability metadata from models.dev.
7
+ *
8
+ * `/v1/models` discovery stays authoritative for *what is callable* (`modelDiscovery.ts`);
9
+ * models.dev only decorates cloud model ids. A cloud model models.dev has never heard of is still
10
+ * listed (unenriched), and if models.dev is unreachable the whole list still prints — just without
11
+ * metadata. Local/self-hosted providers (ollama) get no catalog lookup at all.
12
+ *
13
+ * - `gth models` — list detected providers and their (enriched) models.
14
+ * - `gth models --refresh` — force a models.dev re-fetch past the cache TTL before listing.
15
+ * - `gth models --provider <id>` — scope the listing to a single provider.
16
+ */
17
+ export function modelsCommand(program) {
18
+ program
19
+ .command('models')
20
+ .description('List available models, enriched with cost/context metadata from models.dev')
21
+ .option('--refresh', 'Force a models.dev catalog re-fetch past the cache TTL')
22
+ .option('--provider <id>', 'Only list models for a single provider (e.g. anthropic, openai)')
23
+ .action(async (options) => {
24
+ const providers = await detectProviders();
25
+ const filtered = options.provider
26
+ ? providers.filter((p) => p.id === options.provider)
27
+ : providers;
28
+ if (options.provider && filtered.length === 0) {
29
+ displayWarning(`Unknown provider "${options.provider}". Known: ${providers.map((p) => p.id).join(', ')}.`);
30
+ return;
31
+ }
32
+ let anyEnriched = false;
33
+ for (const provider of filtered) {
34
+ const enriched = await enrichModels(provider.id, provider.models, {
35
+ refresh: options.refresh,
36
+ });
37
+ printProvider(provider, enriched);
38
+ if (enriched.some((m) => m.enrichment))
39
+ anyEnriched = true;
40
+ }
41
+ // Only claim attribution when we actually surfaced models.dev metadata.
42
+ if (anyEnriched) {
43
+ display('');
44
+ display(CATALOG_ATTRIBUTION);
45
+ }
46
+ });
47
+ }
48
+ /** Print one provider's header and its (enriched) model list. */
49
+ function printProvider(provider, models) {
50
+ const status = provider.available ? '' : ' (no key detected)';
51
+ displayInfo(`\n${provider.label} [${provider.id}]${status}`);
52
+ if (models.length === 0) {
53
+ display(' (no models discovered)');
54
+ return;
55
+ }
56
+ for (const model of models) {
57
+ const star = model.preferred ? '⭐ ' : ' ';
58
+ const meta = formatEnrichment(model.enrichment);
59
+ display(` ${star}${model.id}${meta ? ` ${meta}` : ''}`);
60
+ }
61
+ }
62
+ /**
63
+ * Render a model's catalog metadata as a compact bracketed suffix, e.g.
64
+ * `[ctx 200k · out 64k · in $5/1M · out $25/1M · tools · reasoning] *`. Returns '' when there is no
65
+ * enrichment (the model is still listed — it is simply callable-but-unenriched). Costs are US$ per
66
+ * 1M tokens (models.dev's unit); the trailing `*` ties back to the attribution footer.
67
+ */
68
+ export function formatEnrichment(entry) {
69
+ if (!entry)
70
+ return '';
71
+ const parts = [];
72
+ if (entry.limit?.context !== undefined)
73
+ parts.push(`ctx ${formatTokens(entry.limit.context)}`);
74
+ if (entry.limit?.output !== undefined)
75
+ parts.push(`out ${formatTokens(entry.limit.output)}`);
76
+ if (entry.cost?.input !== undefined)
77
+ parts.push(`in $${entry.cost.input}/1M`);
78
+ if (entry.cost?.output !== undefined)
79
+ parts.push(`out $${entry.cost.output}/1M`);
80
+ if (entry.toolCall)
81
+ parts.push('tools');
82
+ if (entry.reasoning)
83
+ parts.push('reasoning');
84
+ if (parts.length === 0)
85
+ return '';
86
+ return `[${parts.join(' · ')}] *`;
87
+ }
88
+ /** Human-friendly token count: 200000 → "200k", 1500000 → "1.5M". */
89
+ function formatTokens(n) {
90
+ if (n >= 1_000_000)
91
+ return `${Number((n / 1_000_000).toFixed(1))}M`;
92
+ if (n >= 1_000)
93
+ return `${Math.round(n / 1_000)}k`;
94
+ return String(n);
95
+ }
96
+ //# sourceMappingURL=modelsCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modelsCommand.js","sourceRoot":"","sources":["../../src/commands/modelsCommand.ts"],"names":[],"mappings":"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"}
@@ -6,12 +6,14 @@ import { Transcript } from '#src/tui/components/Transcript.js';
6
6
  import { ApprovalPrompt } from '#src/tui/components/ApprovalPrompt.js';
7
7
  import { LiveTurn } from '#src/tui/components/LiveTurn.js';
8
8
  import { StatusBar } from '#src/tui/components/StatusBar.js';
9
+ import { NoticeBar } from '#src/tui/components/NoticeBar.js';
9
10
  import { PromptInput } from '#src/tui/components/PromptInput.js';
10
11
  import { Rule } from '#src/tui/components/Rule.js';
11
12
  import { ClearBanner } from '#src/tui/components/ClearBanner.js';
12
13
  import { DebugPanel, debugPanelLines, DEBUG_TABS, } from '#src/tui/components/DebugPanel.js';
13
14
  import { autoApproveNotice, createCommandRegistry, dispatchSlashCommand, parseSlashCommand, toolsToggleNotice, } from '#src/tui/slashCommands.js';
14
15
  import { viewportBumpSequence } from '#src/tui/terminal.js';
16
+ import { findMatches, scrollOffsetForLine, stepMatch } from '#src/tui/debugSearch.js';
15
17
  /** Rows of clipping viewport in the docked debug panel (default / restored size). */
16
18
  const DEBUG_VIEWPORT_HEIGHT = 8;
17
19
  /** Rows of chrome around the debug viewport we leave for when it is maximised (panel
@@ -39,6 +41,10 @@ const isPlainExit = (s) => s.trim().toLowerCase() === 'exit';
39
41
  export function App(props) {
40
42
  const { agent, mode, modelDisplayName, readyMessage, exitMessage, initialMessage } = props;
41
43
  const [transcript, setTranscript] = useState([]);
44
+ // Mirror for the slash-command dispatch (memoized without transcript in deps): lets /reasoning
45
+ // read the committed turns' thinking without a stale closure or re-binding the handler (TUI-C18).
46
+ const transcriptRef = useRef([]);
47
+ transcriptRef.current = transcript;
42
48
  const [live, setLive] = useState(null);
43
49
  const [running, setRunning] = useState(false);
44
50
  const [turnCount, setTurnCount] = useState(0);
@@ -54,7 +60,16 @@ export function App(props) {
54
60
  // TUI-C16: the old combined "system + tools" capture is split across two tabs.
55
61
  const [debugSystem, setDebugSystem] = useState([]);
56
62
  const [debugTools, setDebugTools] = useState([]);
63
+ // TUI-C20: the MCP-server overview tab (per-server instructions + prefixed tools).
64
+ const [debugMcp, setDebugMcp] = useState([]);
57
65
  const [debugResponse, setDebugResponse] = useState([]);
66
+ // TUI-C21 — `less`-style incremental search over the focused pane's shared line model. Scoped to
67
+ // debug-pane focus (see the useInput handler): `/` opens the query, `n`/`N` step matches, `Esc`
68
+ // clears. `searchInput` is the "typing the query" mode (owns keys); `searchQuery` is the live
69
+ // query (highlights + count); `searchCurrent` is the 0-based cursor into the match list.
70
+ const [debugSearchInput, setDebugSearchInput] = useState(false);
71
+ const [debugSearchQuery, setDebugSearchQuery] = useState('');
72
+ const [debugSearchCurrent, setDebugSearchCurrent] = useState(0);
58
73
  // Whether tool-call panels show their args/result body. Collapsed by default (compact
59
74
  // summary lines) so the transcript stays readable; Ctrl+T flips the whole turn's detail,
60
75
  // mirroring the docked debug panel's single-key detail toggle.
@@ -108,18 +123,72 @@ export function App(props) {
108
123
  // step nor the arrow step can push the offset past the end (the over-scroll bug that left
109
124
  // PgUp/↑ burning through phantom offset before anything moved — TUI-C11). Single-sourced with
110
125
  // DebugPanel via the exported `debugPanelLines` so the clamp matches exactly what is rendered.
111
- const debugLineCount = useMemo(() => debugPanelLines({
126
+ const debugLines = useMemo(() => debugPanelLines({
112
127
  subagents,
113
128
  historyLines: debugHistory,
114
129
  systemLines: debugSystem,
115
130
  toolsLines: debugTools,
131
+ mcpLines: debugMcp,
116
132
  responseLines: debugResponse,
117
133
  activeTab: debugTab,
118
- }).length, [subagents, debugHistory, debugSystem, debugTools, debugResponse, debugTab]);
134
+ }), [subagents, debugHistory, debugSystem, debugTools, debugMcp, debugResponse, debugTab]);
135
+ const debugLineCount = debugLines.length;
119
136
  const debugMaxOffset = Math.max(0, debugLineCount - debugViewport);
137
+ // Mirror the active tab's lines for the synchronous key handler, so `/`-search can compute
138
+ // matches + the jump offset off the exact lines being rendered without a stale closure (TUI-C21).
139
+ const debugLinesRef = useRef([]);
140
+ debugLinesRef.current = debugLines;
141
+ // Match line-indices for the current query over the active tab's lines — one search, every tab
142
+ // uniformly (it runs over whatever `debugPanelLines` produced). Mirrored into refs so the
143
+ // synchronous key handler (n/N, Esc) reads the current matches + cursor without a stale closure.
144
+ const debugMatches = useMemo(() => findMatches(debugLines, debugSearchQuery), [debugLines, debugSearchQuery]);
145
+ const debugMatchesRef = useRef([]);
146
+ debugMatchesRef.current = debugMatches;
147
+ const debugSearchInputRef = useRef(false);
148
+ debugSearchInputRef.current = debugSearchInput;
149
+ const debugSearchQueryRef = useRef('');
150
+ debugSearchQueryRef.current = debugSearchQuery;
151
+ const debugSearchCurrentRef = useRef(0);
152
+ debugSearchCurrentRef.current = debugSearchCurrent;
153
+ // The absolute line index of the current match (or -1) — DebugPanel paints it distinctly.
154
+ const currentMatchLine = debugMatches.length > 0
155
+ ? debugMatches[Math.min(debugSearchCurrent, debugMatches.length - 1)]
156
+ : -1;
120
157
  // Clamp helper shared by every downward scroll (page + arrow) so the offset never exceeds the
121
158
  // real maximum; the upward floor stays Math.max(0, …).
122
159
  const clampDebugScroll = (next) => Math.min(Math.max(0, next), debugMaxOffset);
160
+ // ── TUI-C21 search helpers (drive state + refs together so the synchronous handler stays fresh) ──
161
+ // Clear the search entirely (query, matches, input mode). Leaves the viewport where it is.
162
+ const clearDebugSearch = () => {
163
+ debugSearchInputRef.current = false;
164
+ debugSearchQueryRef.current = '';
165
+ debugSearchCurrentRef.current = 0;
166
+ setDebugSearchInput(false);
167
+ setDebugSearchQuery('');
168
+ setDebugSearchCurrent(0);
169
+ };
170
+ // Set the query, recompute matches off the current lines, reset the cursor to the first match and
171
+ // jump the viewport to it (reusing TUI-C11's scroll offset). Incremental: called on every keystroke.
172
+ const setDebugSearch = (query) => {
173
+ debugSearchQueryRef.current = query;
174
+ setDebugSearchQuery(query);
175
+ const matches = findMatches(debugLinesRef.current, query);
176
+ debugSearchCurrentRef.current = 0;
177
+ setDebugSearchCurrent(0);
178
+ if (matches.length > 0) {
179
+ setDebugScroll(scrollOffsetForLine(matches[0], debugViewport, debugLinesRef.current.length));
180
+ }
181
+ };
182
+ // Move to the next (+1) / previous (-1) match with wrap-around and jump the viewport to it.
183
+ const stepDebugSearch = (dir) => {
184
+ const matches = debugMatchesRef.current;
185
+ if (matches.length === 0)
186
+ return;
187
+ const next = stepMatch(matches, debugSearchCurrentRef.current, dir);
188
+ debugSearchCurrentRef.current = next;
189
+ setDebugSearchCurrent(next);
190
+ setDebugScroll(scrollOffsetForLine(matches[next], debugViewport, debugLinesRef.current.length));
191
+ };
123
192
  // Built once per session; a plain array so later layers (EXT-5) could append commands.
124
193
  const registry = useMemo(() => createCommandRegistry(), []);
125
194
  const nextId = () => (idRef.current += 1);
@@ -273,9 +342,16 @@ export function App(props) {
273
342
  toolsExpanded: toolsExpandedRef.current,
274
343
  debugVisible: debugVisibleRef.current,
275
344
  configSummary: props.configSummary,
345
+ // TUI-C19 — the actual validation warnings so `/config` renders the details the
346
+ // standing advisory line points at.
347
+ configWarnings: props.advisories,
276
348
  historySummary: props.historySummary,
277
349
  insightsSummary: props.insightsSummary,
278
350
  historySearch: props.historySearch,
351
+ // Committed turns' thinking, in transcript order (index 0 = turn 1), for /reasoning.
352
+ turnReasonings: transcriptRef.current
353
+ .filter((i) => i.kind === 'assistant')
354
+ .map((i) => i.turn.reasoning),
279
355
  }, { duringRun: running });
280
356
  if (result.clearTranscript) {
281
357
  setTranscript([]);
@@ -283,6 +359,7 @@ export function App(props) {
283
359
  setDebugHistory([]);
284
360
  setDebugSystem([]);
285
361
  setDebugTools([]);
362
+ setDebugMcp([]);
286
363
  setDebugResponse([]);
287
364
  // Show visible feedback for the clear. Rendered outside <Static> (see clearedBanner)
288
365
  // so the known index-reset swallow quirk can't eat it (TUI-C12).
@@ -329,10 +406,17 @@ export function App(props) {
329
406
  setDebugFocused(false);
330
407
  debugFocusedRef.current = false;
331
408
  setDebugMaximized(false);
409
+ // Drop any active search so the pane reopens clean (TUI-C21).
410
+ clearDebugSearch();
332
411
  }
333
412
  return next;
334
413
  });
335
414
  }
415
+ // TUI-C18 — /reasoning reprint: commit a reasoning block that reuses the TUI-C15 styling
416
+ // (the original committed turn is frozen in <Static> and can't re-expand in place).
417
+ if (result.reprintReasoning) {
418
+ push({ kind: 'reasoning', ...result.reprintReasoning });
419
+ }
336
420
  // Commit a structured notice (TUI-C14). /tools and /auto-approve own their notices above
337
421
  // (the state-aware copy is committed there), so skip result.notice in those cases.
338
422
  if (result.notice && !result.toggleTools && !result.autoApprove) {
@@ -395,17 +479,80 @@ export function App(props) {
395
479
  return;
396
480
  }
397
481
  if (debugFocusedRef.current) {
482
+ // TUI-C21 — while TYPING the query, the search input OWNS the keyboard: printable chars
483
+ // extend the query, Backspace trims it, Enter confirms (keeps the highlights, leaves input
484
+ // mode), Esc clears. This must come first so `m`/`n`/Tab are typed into the query rather than
485
+ // maximising / navigating / cycling. `/` here is not the app slash line — the prompt is
486
+ // unmounted while the pane is focused, so `/` can only mean "search this pane" (the seam).
487
+ if (debugSearchInputRef.current) {
488
+ if (key.escape) {
489
+ clearDebugSearch();
490
+ return;
491
+ }
492
+ if (key.return) {
493
+ // Confirm: leave typing mode but keep the query + highlights (n/N now navigate).
494
+ debugSearchInputRef.current = false;
495
+ setDebugSearchInput(false);
496
+ return;
497
+ }
498
+ if (key.backspace || key.delete) {
499
+ setDebugSearch(debugSearchQueryRef.current.slice(0, -1));
500
+ return;
501
+ }
502
+ // Ignore navigation keys while typing; only literal characters extend the query.
503
+ if (key.tab ||
504
+ key.upArrow ||
505
+ key.downArrow ||
506
+ key.leftArrow ||
507
+ key.rightArrow ||
508
+ key.pageUp ||
509
+ key.pageDown) {
510
+ return;
511
+ }
512
+ if (input && !key.ctrl && !key.meta) {
513
+ setDebugSearch(debugSearchQueryRef.current + input);
514
+ }
515
+ return;
516
+ }
517
+ // Esc layering: clear an active search first (less-style), else unfocus the pane.
398
518
  if (key.escape) {
519
+ if (debugSearchQueryRef.current) {
520
+ clearDebugSearch();
521
+ return;
522
+ }
399
523
  setDebugFocused(false);
400
524
  debugFocusedRef.current = false;
401
525
  return;
402
526
  }
527
+ // `/` opens the search input (scoped to pane focus — the seam).
528
+ if (input === '/') {
529
+ debugSearchInputRef.current = true;
530
+ debugSearchQueryRef.current = '';
531
+ debugSearchCurrentRef.current = 0;
532
+ setDebugSearchInput(true);
533
+ setDebugSearchQuery('');
534
+ setDebugSearchCurrent(0);
535
+ return;
536
+ }
537
+ // n / N step to the next / previous match with wrap-around (only when a search is active).
538
+ if (input === 'n') {
539
+ stepDebugSearch(1);
540
+ return;
541
+ }
542
+ if (input === 'N') {
543
+ stepDebugSearch(-1);
544
+ return;
545
+ }
403
546
  // Tab cycles sections forward; Shift+Tab steps back to the previous section (Ink reports
404
547
  // a back-tab as key.tab with key.shift). Both reset the scroll to the top of the new section.
405
548
  if (key.tab) {
406
549
  const step = key.shift ? -1 : 1;
407
550
  setDebugTab((t) => DEBUG_TABS[(DEBUG_TABS.indexOf(t) + step + DEBUG_TABS.length) % DEBUG_TABS.length]);
408
551
  setDebugScroll(0);
552
+ // Matches are tab-local; recompute happens via the memo, reset the cursor to the first
553
+ // match of the new tab (the query persists across tabs).
554
+ debugSearchCurrentRef.current = 0;
555
+ setDebugSearchCurrent(0);
409
556
  return;
410
557
  }
411
558
  // 'm' toggles maximise: grow the pane to (most of) the terminal height so long
@@ -478,6 +625,7 @@ export function App(props) {
478
625
  setDebugHistory(capture.text.split('\n'));
479
626
  setDebugSystem(capture.system.split('\n'));
480
627
  setDebugTools(capture.tools.split('\n'));
628
+ setDebugMcp(capture.mcp.split('\n'));
481
629
  }
482
630
  else
483
631
  setDebugResponse(capture.text.split('\n'));
@@ -497,7 +645,7 @@ export function App(props) {
497
645
  // The greeting is an intro, not a permanent fixture: show it only before the first
498
646
  // exchange, so it stops padding the bottom dock once the conversation is underway.
499
647
  const showIntro = !initialMessage && transcript.length === 0 && !live;
500
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Transcript, { items: transcript, toolsExpanded: toolsExpanded }), clearedBanner ? _jsx(ClearBanner, {}) : null, showIntro ? _jsx(Text, { dimColor: true, children: readyMessage.trim() }) : null, live ? _jsx(LiveTurn, { turn: live, toolsExpanded: toolsExpanded, streaming: true }) : null, debugVisible ? (_jsx(DebugPanel, { subagents: subagents, historyLines: debugHistory, systemLines: debugSystem, toolsLines: debugTools, responseLines: debugResponse, activeTab: debugTab, scrollOffset: debugScroll, focused: debugFocused, viewportHeight: debugViewport, maximized: debugMaximized })) : null, pendingApproval ? _jsx(ApprovalPrompt, { pending: pendingApproval.pending }) : null, _jsx(Rule, {}), _jsx(StatusBar, { running: running, mode: mode, modelDisplayName: modelDisplayName, turnCount: turnCount, debugHint: debugVisible && !debugFocused, autoApprove: autoApprove }), !debugFocused && !pendingApproval ? (_jsx(PromptInput, { onSubmit: handleSubmit, commands: registry, onMenuStateChange: (active) => {
648
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Transcript, { items: transcript, toolsExpanded: toolsExpanded }), clearedBanner ? _jsx(ClearBanner, {}) : null, showIntro ? _jsx(Text, { dimColor: true, children: readyMessage.trim() }) : null, live ? _jsx(LiveTurn, { turn: live, toolsExpanded: toolsExpanded, streaming: true }) : null, debugVisible ? (_jsx(DebugPanel, { subagents: subagents, historyLines: debugHistory, systemLines: debugSystem, toolsLines: debugTools, mcpLines: debugMcp, responseLines: debugResponse, activeTab: debugTab, scrollOffset: debugScroll, focused: debugFocused, viewportHeight: debugViewport, maximized: debugMaximized, searchQuery: debugSearchQuery, searchActive: debugSearchInput, matchCount: debugMatches.length, currentMatchIndex: debugSearchCurrent, currentMatchLine: currentMatchLine })) : null, pendingApproval ? _jsx(ApprovalPrompt, { pending: pendingApproval.pending }) : null, _jsx(Rule, {}), _jsx(NoticeBar, { advisories: props.advisories }), _jsx(StatusBar, { running: running, mode: mode, modelDisplayName: modelDisplayName, turnCount: turnCount, debugHint: debugVisible && !debugFocused, autoApprove: autoApprove }), !debugFocused && !pendingApproval ? (_jsx(PromptInput, { onSubmit: handleSubmit, commands: registry, onMenuStateChange: (active) => {
501
649
  slashMenuActiveRef.current = active;
502
650
  } })) : null, _jsx(Text, { dimColor: true, children: exitMessage.trim() }), _jsx(Rule, {})] }));
503
651
  }