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.
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/historyCommand.d.ts +10 -6
- package/dist/commands/historyCommand.js +41 -11
- package/dist/commands/historyCommand.js.map +1 -1
- package/dist/commands/modelsCommand.d.ts +25 -0
- package/dist/commands/modelsCommand.js +96 -0
- package/dist/commands/modelsCommand.js.map +1 -0
- package/dist/tui/components/App.js +151 -3
- package/dist/tui/components/App.js.map +1 -1
- package/dist/tui/components/DebugPanel.d.ts +16 -3
- package/dist/tui/components/DebugPanel.js +23 -5
- package/dist/tui/components/DebugPanel.js.map +1 -1
- package/dist/tui/components/LiveTurn.d.ts +22 -0
- package/dist/tui/components/LiveTurn.js +2 -2
- package/dist/tui/components/LiveTurn.js.map +1 -1
- package/dist/tui/components/NoticeBar.d.ts +19 -0
- package/dist/tui/components/NoticeBar.js +23 -0
- package/dist/tui/components/NoticeBar.js.map +1 -0
- package/dist/tui/components/Transcript.js +6 -1
- package/dist/tui/components/Transcript.js.map +1 -1
- package/dist/tui/debugRender.d.ts +26 -0
- package/dist/tui/debugRender.js +66 -0
- package/dist/tui/debugRender.js.map +1 -1
- package/dist/tui/debugSearch.d.ts +40 -0
- package/dist/tui/debugSearch.js +73 -0
- package/dist/tui/debugSearch.js.map +1 -0
- package/dist/tui/slashCommands.d.ts +52 -5
- package/dist/tui/slashCommands.js +102 -7
- package/dist/tui/slashCommands.js.map +1 -1
- package/dist/tui/tuiSessionModule.js +54 -18
- package/dist/tui/tuiSessionModule.js.map +1 -1
- package/dist/tui/types.d.ts +17 -1
- 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":"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,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;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
|
6
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
11
|
-
*
|
|
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 {
|
|
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
|
|
8
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
13
|
-
*
|
|
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
|
|
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
|
|
56
|
-
displayInfo('Recent
|
|
57
|
-
for (const line of
|
|
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":"AACA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACnG,OAAO,
|
|
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
|
|
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
|
-
})
|
|
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
|
}
|