deepclause-sdk 0.1.2 → 0.1.3

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 (54) hide show
  1. package/dist/cli/repl.d.ts +19 -4
  2. package/dist/cli/repl.d.ts.map +1 -1
  3. package/dist/cli/repl.js +267 -44
  4. package/dist/cli/repl.js.map +1 -1
  5. package/dist/repl/backend.d.ts.map +1 -1
  6. package/dist/repl/backend.js +18 -5
  7. package/dist/repl/backend.js.map +1 -1
  8. package/dist/repl/render.d.ts +52 -2
  9. package/dist/repl/render.d.ts.map +1 -1
  10. package/dist/repl/render.js +33 -12
  11. package/dist/repl/render.js.map +1 -1
  12. package/dist/repl/status.d.ts +3 -1
  13. package/dist/repl/status.d.ts.map +1 -1
  14. package/dist/repl/status.js +4 -0
  15. package/dist/repl/status.js.map +1 -1
  16. package/dist/repl/tui/autocomplete.d.ts +19 -0
  17. package/dist/repl/tui/autocomplete.d.ts.map +1 -0
  18. package/dist/repl/tui/autocomplete.js +41 -0
  19. package/dist/repl/tui/autocomplete.js.map +1 -0
  20. package/dist/repl/tui/document.d.ts +51 -0
  21. package/dist/repl/tui/document.d.ts.map +1 -0
  22. package/dist/repl/tui/document.js +160 -0
  23. package/dist/repl/tui/document.js.map +1 -0
  24. package/dist/repl/tui/picker.d.ts +31 -0
  25. package/dist/repl/tui/picker.d.ts.map +1 -0
  26. package/dist/repl/tui/picker.js +43 -0
  27. package/dist/repl/tui/picker.js.map +1 -0
  28. package/dist/repl/tui/renderer.d.ts +98 -0
  29. package/dist/repl/tui/renderer.d.ts.map +1 -0
  30. package/dist/repl/tui/renderer.js +284 -0
  31. package/dist/repl/tui/renderer.js.map +1 -0
  32. package/dist/repl/tui/status-bar.d.ts +4 -2
  33. package/dist/repl/tui/status-bar.d.ts.map +1 -1
  34. package/dist/repl/tui/status-bar.js +80 -24
  35. package/dist/repl/tui/status-bar.js.map +1 -1
  36. package/dist/repl/tui/terminal.d.ts +28 -0
  37. package/dist/repl/tui/terminal.d.ts.map +1 -0
  38. package/dist/repl/tui/terminal.js +94 -0
  39. package/dist/repl/tui/terminal.js.map +1 -0
  40. package/dist/repl/tui/theme.d.ts +60 -0
  41. package/dist/repl/tui/theme.d.ts.map +1 -1
  42. package/dist/repl/tui/theme.js +142 -0
  43. package/dist/repl/tui/theme.js.map +1 -1
  44. package/dist/repl/tui/tool-pane.d.ts +68 -0
  45. package/dist/repl/tui/tool-pane.d.ts.map +1 -0
  46. package/dist/repl/tui/tool-pane.js +257 -0
  47. package/dist/repl/tui/tool-pane.js.map +1 -0
  48. package/dist/repl/tui/working-line.d.ts +23 -0
  49. package/dist/repl/tui/working-line.d.ts.map +1 -0
  50. package/dist/repl/tui/working-line.js +58 -0
  51. package/dist/repl/tui/working-line.js.map +1 -0
  52. package/dist/types.d.ts +7 -0
  53. package/dist/types.d.ts.map +1 -1
  54. package/package.json +1 -1
@@ -8,9 +8,10 @@
8
8
  */
9
9
  import type { Command } from 'commander';
10
10
  import { type HarnessRepl } from '../repl/index.js';
11
- import { ReplRenderer } from '../repl/render.js';
11
+ import { type ReplEventRenderer } from '../repl/render.js';
12
12
  import { type ReplStatusSnapshot } from '../repl/status.js';
13
13
  import { type ReplToolset } from '../repl/toolset.js';
14
+ import { type ModelResolver } from '../repl/model-resolver.js';
14
15
  import type { ImageBackend } from '../types.js';
15
16
  import { type DiagramMode, type GraphicsMode } from '../visualize/index.js';
16
17
  export interface ReplCliOptions {
@@ -85,6 +86,11 @@ export type ReplInput = {
85
86
  } | {
86
87
  kind: 'model';
87
88
  model?: string;
89
+ } | {
90
+ kind: 'reasoning';
91
+ level?: string;
92
+ } | {
93
+ kind: 'show-reasoning';
88
94
  } | {
89
95
  kind: 'export';
90
96
  path?: string;
@@ -141,7 +147,7 @@ export declare function classifyReplInput(line: string): ReplInput;
141
147
  /** Run one goal and render its solutions. Returns a process exit code. */
142
148
  export declare function runGoalWithRenderer(repl: HarnessRepl, goal: string, options: {
143
149
  first?: boolean;
144
- renderer: ReplRenderer;
150
+ renderer: ReplEventRenderer;
145
151
  signal?: AbortSignal;
146
152
  }): Promise<number>;
147
153
  /**
@@ -153,7 +159,8 @@ export declare function runGoalWithRenderer(repl: HarnessRepl, goal: string, opt
153
159
  export declare function buildCliOnInput(options: ReplCliOptions): ((prompt: string, choices?: string[]) => Promise<string | undefined>) | undefined;
154
160
  export interface ReplRuntime {
155
161
  repl: HarnessRepl;
156
- renderer: ReplRenderer;
162
+ /** String renderer for plain/json output, or the TUI's component renderer. */
163
+ renderer: ReplEventRenderer;
157
164
  workspace: string;
158
165
  promptPrefix: string;
159
166
  toolset: ReplToolset;
@@ -165,6 +172,14 @@ export interface ReplRuntime {
165
172
  /** Active model capabilities (pi backend), used by the status bar. */
166
173
  contextWindow?: number;
167
174
  reasoning: boolean;
175
+ /** How much model reasoning the renderer shows (the picker changes this live). */
176
+ reasoningMode: 'off' | 'summary' | 'full';
177
+ /** Models this session can switch to, for the model picker. */
178
+ modelResolver?: ModelResolver;
179
+ /** The backend, so a picker can change the reasoning level live. */
180
+ llmBackend?: import('../types.js').LLMBackend;
181
+ /** Set the reasoning level shown in the status bar. */
182
+ setThinkingLevel?: (level: string | undefined) => void;
168
183
  thinkingLevel?: string;
169
184
  /** Session id of this run. */
170
185
  sessionId: string;
@@ -198,7 +213,7 @@ export declare function dispatchReplInput(runtime: ReplRuntime, input: ReplInput
198
213
  exportPath?: string;
199
214
  signal?: AbortSignal;
200
215
  }): Promise<boolean>;
201
- export declare function runInteractiveGoal(repl: HarnessRepl, renderer: ReplRenderer, goal: string, options: ReplCliOptions, ask: (query: string) => Promise<string | null>, signal?: AbortSignal): Promise<void>;
216
+ export declare function runInteractiveGoal(repl: HarnessRepl, renderer: ReplEventRenderer, goal: string, options: ReplCliOptions, ask: (query: string) => Promise<string | null>, signal?: AbortSignal): Promise<void>;
202
217
  /**
203
218
  * Interactive TUI for the REPL: a scrolling transcript, a fixed editor, and a
204
219
  * fixed status bar below the input. It reuses `ReplRenderer` (rich mode) for all
@@ -1 +1 @@
1
- {"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../src/cli/repl.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"repl.d.ts","sourceRoot":"","sources":["../../src/cli/repl.ts"],"names":[],"mappings":"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"}
package/dist/cli/repl.js CHANGED
@@ -14,14 +14,20 @@ import { createHarnessRepl } from '../repl/index.js';
14
14
  import { HistoryStore } from '../repl/history.js';
15
15
  import { resolveReplJudgeSelection } from '../repl/judge.js';
16
16
  import { ReplRenderer } from '../repl/render.js';
17
+ import { TranscriptDocument } from '../repl/tui/document.js';
18
+ import { ChoicePicker } from '../repl/tui/picker.js';
19
+ import { TuiRenderer } from '../repl/tui/renderer.js';
20
+ import { WorkingLine } from '../repl/tui/working-line.js';
21
+ import { createReplAutocomplete } from '../repl/tui/autocomplete.js';
22
+ import { initTuiTheme, scrollbarStyles } from '../repl/tui/theme.js';
23
+ import { SizedTerminal } from '../repl/tui/terminal.js';
17
24
  import { ReplStatusTracker } from '../repl/status.js';
18
- import { LineSink, TranscriptComponent } from '../repl/tui/transcript.js';
19
25
  import { StatusBarComponent } from '../repl/tui/status-bar.js';
20
- import { Container, ProcessTerminal, Spacer, TuiMainScreen, matchesKey } from '@earendil-works/pi-tui';
26
+ import { Container, ScrollView, Spacer, TuiAltScreen, VStack, matchesKey, } from '@earendil-works/pi-tui';
21
27
  import { editorTheme, formatInputAnswer } from '../repl/tui/theme.js';
22
28
  import { PromptEditor } from '../repl/tui/editor.js';
23
29
  import { loadProjectContext } from '../repl/project-context.js';
24
- import { createPiBackend } from '../repl/backend.js';
30
+ import { createPiBackend, THINKING_LEVELS } from '../repl/backend.js';
25
31
  import { createReplToolset } from '../repl/toolset.js';
26
32
  import { takeStartupPrologMessages } from '../prolog/loader.js';
27
33
  import { createPiImageBackend } from '../repl/imagegen.js';
@@ -113,6 +119,12 @@ export function classifyReplInput(line) {
113
119
  return { kind: 'export', path: arg || undefined };
114
120
  case 'model':
115
121
  return { kind: 'model', model: arg || undefined };
122
+ case 'reasoning':
123
+ case 'reasoning-level':
124
+ return { kind: 'reasoning', level: arg || undefined };
125
+ case 'show-reasoning':
126
+ case 'reasoning-summary':
127
+ return { kind: 'show-reasoning' };
116
128
  case 'freeze':
117
129
  return { kind: 'freeze' };
118
130
  case 'unfreeze':
@@ -484,6 +496,10 @@ export async function createReplRuntime(options) {
484
496
  backend: backendKind,
485
497
  contextWindow,
486
498
  reasoning,
499
+ reasoningMode: options.showReasoning ?? config.repl.showReasoning,
500
+ modelResolver,
501
+ llmBackend,
502
+ setThinkingLevel: (level) => statusTracker.setThinkingLevel(level),
487
503
  thinkingLevel,
488
504
  sessionId: repl.session.getSessionId(),
489
505
  harnessName,
@@ -782,18 +798,15 @@ async function executeLoad(runtime, files) {
782
798
  }
783
799
  /** Non-interactive mode: read classified inputs line by line until EOF. */
784
800
  /**
785
- * Route Ctrl-C to the active turn. While a turn is running the first Ctrl-C
786
- * aborts it (the session then saves the harness to a temp file); a Ctrl-C while
787
- * idle exits.
801
+ * Ctrl-C exits, in every mode: this is the plain/readline path, where there is no
802
+ * raw key to distinguish Escape from an editing sequence, so the TUI's
803
+ * "Esc interrupts, Ctrl-C exits" cannot be mirrored exactly. A running turn is
804
+ * aborted first, so nothing keeps writing after the UI is gone.
788
805
  */
789
- function installSigint(runtime, holder, rl, onExit) {
806
+ function installSigint(holder, rl, onExit) {
790
807
  const handler = () => {
791
- if (holder.turn) {
792
- holder.turn.abort(new Error('cancelled by user'));
793
- runtime.renderer.writeErrorLine('(cancelling current turn… press Ctrl-C again to exit)');
794
- }
795
- else if (onExit) {
796
- // Save the harness before exiting (Ctrl-C while idle).
808
+ holder.turn?.abort(new Error('cancelled by user'));
809
+ if (onExit) {
797
810
  void onExit().finally(() => process.exit(130));
798
811
  }
799
812
  else {
@@ -811,7 +824,7 @@ async function runNonInteractive(runtime, options, onExit) {
811
824
  let exitCode = 0;
812
825
  const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });
813
826
  const holder = { turn: null };
814
- const removeSigint = installSigint(runtime, holder, rl, onExit);
827
+ const removeSigint = installSigint(holder, rl, onExit);
815
828
  try {
816
829
  for await (const line of rl) {
817
830
  const input = classifyReplInput(line);
@@ -881,7 +894,7 @@ async function runInteractive(runtime, options, onExit) {
881
894
  return answer === null ? undefined : answer;
882
895
  });
883
896
  const holder = { turn: null };
884
- const removeSigint = installSigint(runtime, holder, rl, onExit);
897
+ const removeSigint = installSigint(holder, rl, onExit);
885
898
  try {
886
899
  while (true) {
887
900
  const line = await ask(promptPrefix);
@@ -976,10 +989,33 @@ export async function runInteractiveGoal(repl, renderer, goal, options, ask, sig
976
989
  * input prompts never fight the main prompt for stdin.
977
990
  */
978
991
  export async function runTui(runtime, options, onExit, loaded = []) {
979
- const terminal = new ProcessTerminal();
980
- const tui = new TuiMainScreen(terminal);
981
- const transcript = new TranscriptComponent();
982
- const sink = new LineSink(transcript);
992
+ initTuiTheme();
993
+ // Sized, because pi-tui falls back to a hard 24 rows when the stream reports no
994
+ // size, which leaves the bottom of a taller terminal untouched.
995
+ const terminal = new SizedTerminal();
996
+ // Fullscreen: an application-owned viewport (ScrollView) with the editor and
997
+ // status bar pinned in a dock. In this mode pi-tui hands scrolling, mouse wheel,
998
+ // text selection and search to the viewport, and reassigns layout on resize —
999
+ // which is what makes the transcript scroll inside the app and the input box
1000
+ // behave when the terminal changes size.
1001
+ const tui = new TuiAltScreen(terminal, undefined, undefined, {
1002
+ mouse: true,
1003
+ wheelScrollLines: 'auto',
1004
+ copyOnSelect: true,
1005
+ });
1006
+ const document = new TranscriptDocument();
1007
+ const transcript = document;
1008
+ // The TUI renders components rather than lines: tool calls become panes and
1009
+ // streamed text becomes markdown. There is no external sink to wire.
1010
+ const working = new WorkingLine(tui);
1011
+ const renderer = new TuiRenderer({
1012
+ document,
1013
+ onWorking: (label) => {
1014
+ working.set(label);
1015
+ tui.requestRender();
1016
+ },
1017
+ });
1018
+ runtime.renderer = renderer;
983
1019
  const runState = { running: false, steerable: false };
984
1020
  /** Lines typed during a goal query, consumed by the solution pager. */
985
1021
  const bufferedInput = [];
@@ -993,12 +1029,14 @@ export async function runTui(runtime, options, onExit, loaded = []) {
993
1029
  isRunning: () => runState.running,
994
1030
  });
995
1031
  transcript.onChange = () => tui.requestRender();
996
- // Redirect the renderer into the transcript (including error lines).
997
- runtime.renderer.setSink((text) => sink.write(text), (text) => sink.write(text));
998
- runtime.renderer.setImageSink((lines) => transcript.appendImageLines(lines));
999
- runtime.renderer.setWidth(terminal.columns);
1032
+ renderer.setImageSink((lines) => document.appendImageLines(lines));
1033
+ renderer.setWidth(terminal.columns);
1034
+ // pi-tui re-renders on resize but does not invalidate the component tree, and a
1035
+ // component that caches by width (a tool pane, markdown) would draw stale lines.
1000
1036
  const onResize = () => {
1001
- runtime.renderer.setWidth(terminal.columns);
1037
+ terminal.refreshSize();
1038
+ renderer.setWidth(terminal.columns);
1039
+ tui.invalidate();
1002
1040
  tui.requestRender();
1003
1041
  };
1004
1042
  process.stdout.on('resize', onResize);
@@ -1027,7 +1065,9 @@ export async function runTui(runtime, options, onExit, loaded = []) {
1027
1065
  }
1028
1066
  else if (activePrompt === runtime.promptPrefix) {
1029
1067
  // A user turn: separate it from the previous output and from the
1030
- // response that follows; render it as a pi-style blue block.
1068
+ // response that follows; render it as a pi-style blue block. The previous
1069
+ // turn's text block is closed first, so the new response starts below this.
1070
+ runtime.renderer.endTextBlock?.();
1031
1071
  if (transcript.getLines().length > 0)
1032
1072
  transcript.append('');
1033
1073
  transcript.append(text, 'user');
@@ -1038,6 +1078,7 @@ export async function runTui(runtime, options, onExit, loaded = []) {
1038
1078
  else if (text.trim()) {
1039
1079
  // No pending ask means a turn is running.
1040
1080
  if (runState.steerable) {
1081
+ runtime.renderer.endTextBlock?.();
1041
1082
  transcript.append(text, 'steer');
1042
1083
  runtime.repl.steer(text);
1043
1084
  }
@@ -1059,6 +1100,7 @@ export async function runTui(runtime, options, onExit, loaded = []) {
1059
1100
  };
1060
1101
  const createEditor = () => {
1061
1102
  editor = new PromptEditor(tui, editorTheme());
1103
+ editor.setAutocompleteProvider(createReplAutocomplete(runtime.workspace));
1062
1104
  editor.onSubmit = handleSubmit;
1063
1105
  editorContainer.clear();
1064
1106
  editorContainer.addChild(editor);
@@ -1097,40 +1139,206 @@ export async function runTui(runtime, options, onExit, loaded = []) {
1097
1139
  const answer = await ask(`${prompt}${suffix}`, true);
1098
1140
  return answer === null ? undefined : answer;
1099
1141
  });
1100
- const cancelTurn = () => {
1101
- if (holder.turn) {
1102
- holder.turn.abort(new Error('cancelled by user'));
1103
- transcript.append('(cancelling current turn… press Ctrl-C again to exit)');
1104
- tui.requestRender();
1105
- return;
1106
- }
1142
+ /** Interrupt the running turn, if any. Returns whether there was one. */
1143
+ const interruptTurn = () => {
1144
+ if (!holder.turn)
1145
+ return false;
1146
+ holder.turn.abort(new Error('cancelled by user'));
1147
+ transcript.append('(interrupted — Ctrl-C exits)');
1148
+ tui.requestRender();
1149
+ return true;
1150
+ };
1151
+ /** Leave the REPL, aborting anything still running so nothing outlives the UI. */
1152
+ const exitRepl = () => {
1153
+ holder.turn?.abort(new Error('cancelled by user'));
1107
1154
  void (onExit ? onExit() : Promise.resolve()).finally(() => {
1108
1155
  tui.stop();
1109
1156
  process.exit(130);
1110
1157
  });
1111
1158
  };
1112
- // Ctrl-C aborts the current turn; Ctrl-D (on an empty editor) exits. Use pi's
1113
- // key matcher so kitty/meta key encodings are recognized, not just raw bytes.
1159
+ // Esc interrupts, Ctrl-C exits, Ctrl-D (on an empty editor) exits. Escape is
1160
+ // left alone while a completion list or an overlay owns it, so it keeps its
1161
+ // usual meaning there. Use pi's key matcher so kitty/meta encodings are
1162
+ // recognized, not just raw bytes.
1114
1163
  tui.addInputListener((data) => {
1115
1164
  if (matchesKey(data, 'ctrl+c')) {
1116
- cancelTurn();
1165
+ exitRepl();
1117
1166
  return { consume: true };
1118
1167
  }
1119
1168
  if (matchesKey(data, 'ctrl+d') && !holder.turn && (editor?.getText().length ?? 0) === 0) {
1120
- cancelTurn();
1169
+ exitRepl();
1121
1170
  return { consume: true };
1122
1171
  }
1172
+ if (matchesKey(data, 'escape')
1173
+ && !tui.hasOverlay()
1174
+ && !(editor?.isShowingAutocomplete() ?? false)) {
1175
+ if (interruptTurn())
1176
+ return { consume: true };
1177
+ return undefined;
1178
+ }
1179
+ // ctrl+o expands or collapses the most recent tool call, the way pi reveals
1180
+ // full tool output on demand.
1181
+ if (matchesKey(data, 'ctrl+o')) {
1182
+ if (renderer.toggleLastPane())
1183
+ return { consume: true };
1184
+ return undefined;
1185
+ }
1123
1186
  return undefined;
1124
1187
  });
1125
- // Fallback for terminals/raw-mode setups that deliver SIGINT instead of \x03.
1126
- process.on('SIGINT', cancelTurn);
1188
+ // Fallback for terminals/raw-mode setups that deliver SIGINT instead of \x03:
1189
+ // the terminal's own Ctrl-C, which now exits.
1190
+ process.on('SIGINT', exitRepl);
1127
1191
  createEditor();
1128
1192
  for (const line of runtime.inputHistory)
1129
1193
  editor?.addToHistory(line);
1130
- tui.addChild(transcript);
1131
- tui.addChild(new Spacer(1));
1132
- tui.addChild(editorContainer);
1133
- tui.addChild(statusBar);
1194
+ /**
1195
+ * Show a chooser over the transcript and resolve when it closes.
1196
+ *
1197
+ * The overlay owns input while it is up (pi-tui routes keys to the focused
1198
+ * overlay), so the editor does not see the keys the list is using.
1199
+ */
1200
+ const choose = (build) => {
1201
+ return new Promise((resolve) => {
1202
+ let handle;
1203
+ const done = (picked) => {
1204
+ handle?.hide();
1205
+ if (editor)
1206
+ tui.setFocus(editor);
1207
+ tui.requestRender();
1208
+ resolve(picked);
1209
+ };
1210
+ const picker = build(done);
1211
+ handle = tui.showOverlay(picker, {
1212
+ anchor: 'center',
1213
+ width: '70%',
1214
+ maxHeight: picker.contentHeight + 2,
1215
+ });
1216
+ });
1217
+ };
1218
+ const pickReasoningMode = async () => {
1219
+ const modes = ['off', 'summary', 'full'];
1220
+ const picked = await choose((done) => new ChoicePicker({
1221
+ title: 'Reasoning',
1222
+ hint: 'How much of the model\'s thinking to show in the transcript',
1223
+ current: runtime.reasoningMode,
1224
+ items: [
1225
+ { value: 'off', label: 'off', description: 'hide reasoning entirely' },
1226
+ { value: 'summary', label: 'summary', description: 'a bounded preview' },
1227
+ { value: 'full', label: 'full', description: 'everything the model emits' },
1228
+ ].filter((item) => modes.includes(item.value)),
1229
+ onSelect: (item) => done(item.value),
1230
+ onCancel: () => done(),
1231
+ }));
1232
+ if (!picked)
1233
+ return;
1234
+ const mode = picked;
1235
+ runtime.reasoningMode = mode;
1236
+ runtime.renderer.setReasoningMode?.(mode);
1237
+ await persistSetting({ repl: { showReasoning: mode } }, 'reasoning display');
1238
+ renderer.writeLine(`reasoning display: ${mode}`);
1239
+ };
1240
+ /**
1241
+ * The model's reasoning level (its thinking effort), as opposed to how much of
1242
+ * that reasoning is displayed — `/show-reasoning` is the other one.
1243
+ */
1244
+ /**
1245
+ * Persist a setting, but never let that failure stop it applying.
1246
+ *
1247
+ * `updateConfig` requires an existing config file (`deepclause init`), and a
1248
+ * picker that threw before applying the change left the selection doing nothing at
1249
+ * all — which read as "I cannot adjust the reasoning".
1250
+ */
1251
+ const persistSetting = async (patch, label) => {
1252
+ try {
1253
+ await updateConfig(runtime.workspace, patch);
1254
+ }
1255
+ catch (error) {
1256
+ const message = error instanceof Error ? error.message : String(error);
1257
+ renderer.writeLine(`${label} applied to this session, but not saved: ${message.split('\n')[0]}`);
1258
+ }
1259
+ };
1260
+ const pickReasoningLevel = async (level) => {
1261
+ const backend = runtime.llmBackend;
1262
+ const supported = (backend?.supportedThinkingLevels?.() ?? THINKING_LEVELS.slice());
1263
+ const apply = async (chosen) => {
1264
+ backend?.setThinkingLevel?.(chosen);
1265
+ runtime.thinkingLevel = chosen;
1266
+ runtime.setThinkingLevel?.(chosen);
1267
+ await persistSetting({ backend: { kind: 'pi', thinkingLevel: chosen } }, 'reasoning level');
1268
+ renderer.writeLine(`reasoning level: ${chosen}`);
1269
+ };
1270
+ if (level) {
1271
+ if (!supported.includes(level)) {
1272
+ renderer.writeLine(`unknown reasoning level '${level}'; supported: ${supported.join(', ')}`);
1273
+ return;
1274
+ }
1275
+ await apply(level);
1276
+ return;
1277
+ }
1278
+ const chosen = await choose((done) => new ChoicePicker({
1279
+ title: 'Reasoning level',
1280
+ hint: 'How hard the model is asked to think (not how much is shown)',
1281
+ current: runtime.thinkingLevel,
1282
+ items: supported.map((value) => ({
1283
+ value,
1284
+ label: value,
1285
+ description: value === 'off' ? 'no reasoning requested' : undefined,
1286
+ })),
1287
+ onSelect: (item) => done(item.value),
1288
+ onCancel: () => done(),
1289
+ }));
1290
+ if (chosen)
1291
+ await apply(chosen);
1292
+ };
1293
+ const pickModel = async () => {
1294
+ const catalog = runtime.modelResolver?.catalog() ?? [];
1295
+ if (catalog.length === 0) {
1296
+ renderer.writeLine(`model: ${runtime.model} (no catalog to choose from; set one in config)`);
1297
+ return;
1298
+ }
1299
+ const current = runtime.model.includes(':') ? runtime.model : undefined;
1300
+ const picked = await choose((done) => new ChoicePicker({
1301
+ title: 'Model',
1302
+ hint: 'The default for this workspace (applies to the next session)',
1303
+ current,
1304
+ items: catalog.map((entry) => ({
1305
+ value: entry.model,
1306
+ label: entry.model,
1307
+ description: entry.inputCost !== undefined
1308
+ ? `$${entry.inputCost}/$${entry.outputCost ?? '?'} per 1M in/out`
1309
+ : (entry.contextWindow ? `${Math.round(entry.contextWindow / 1000)}k context` : undefined),
1310
+ })),
1311
+ onSelect: (item) => done(item.value),
1312
+ onCancel: () => done(),
1313
+ }));
1314
+ if (!picked)
1315
+ return;
1316
+ await persistSetting({ backend: { kind: 'pi', model: picked } }, 'model');
1317
+ renderer.writeLine(`default model set to ${picked} (takes effect on the next start)`);
1318
+ };
1319
+ // Transcript grows and scrolls; everything below it stays put.
1320
+ const scroll = new ScrollView(document, {
1321
+ follow: 'end',
1322
+ primary: true,
1323
+ scrollbar: 'auto',
1324
+ scrollbarTrackStyle: scrollbarStyles.track,
1325
+ scrollbarThumbStyle: scrollbarStyles.thumb,
1326
+ });
1327
+ // Margins: a blank row above the transcript and one between it and the editor, so
1328
+ // the first and last line of output are not jammed against the chrome. Fixed
1329
+ // spacers rather than padding inside the scroll view: they do not scroll away and
1330
+ // do not consume scrollback.
1331
+ const dock = new VStack([
1332
+ { component: working, basis: 'auto', grow: 0, shrink: 1, minSize: 0 },
1333
+ { component: new Spacer(1), basis: 'auto', grow: 0, shrink: 1, minSize: 1 },
1334
+ { component: editorContainer, basis: 'auto', grow: 0, shrink: 1, minSize: 1 },
1335
+ { component: statusBar, basis: 'auto', grow: 0, shrink: 1, minSize: 1 },
1336
+ ]);
1337
+ tui.setLayoutRoot(new VStack([
1338
+ { component: new Spacer(1), basis: 'auto', grow: 0, shrink: 1, minSize: 1 },
1339
+ { component: scroll, basis: 0, grow: 1, shrink: 1, minSize: 1 },
1340
+ { component: dock, basis: 'auto', grow: 0, shrink: 1, minSize: 1 },
1341
+ ]));
1134
1342
  tui.start();
1135
1343
  await printBanner(runtime, loaded);
1136
1344
  tui.requestRender();
@@ -1142,6 +1350,20 @@ export async function runTui(runtime, options, onExit, loaded = []) {
1142
1350
  const input = classifyReplInput(line);
1143
1351
  if (input.kind === 'halt')
1144
1352
  break;
1353
+ // The pickers are overlays over the transcript, so they need the TUI and are
1354
+ // handled here rather than in the command dispatcher the readline path shares.
1355
+ if (input.kind === 'model' && !input.model) {
1356
+ await pickModel();
1357
+ continue;
1358
+ }
1359
+ if (input.kind === 'reasoning') {
1360
+ await pickReasoningLevel(input.level);
1361
+ continue;
1362
+ }
1363
+ if (input.kind === 'show-reasoning') {
1364
+ await pickReasoningMode();
1365
+ continue;
1366
+ }
1145
1367
  const controller = new AbortController();
1146
1368
  holder.turn = controller;
1147
1369
  runState.running = true;
@@ -1171,8 +1393,9 @@ export async function runTui(runtime, options, onExit, loaded = []) {
1171
1393
  }
1172
1394
  finally {
1173
1395
  tui.stop();
1174
- process.off('SIGINT', cancelTurn);
1396
+ process.off('SIGINT', exitRepl);
1175
1397
  process.stdout.off('resize', onResize);
1398
+ working.stop();
1176
1399
  // Restore stdout so the exit footer and any later output are visible.
1177
1400
  runtime.renderer.setSink((text) => process.stdout.write(text), (text) => process.stderr.write(text));
1178
1401
  }
@@ -1239,7 +1462,7 @@ export async function startRepl(options) {
1239
1462
  if (goals.length > 0) {
1240
1463
  let exitCode = 0;
1241
1464
  const controller = new AbortController();
1242
- const removeSigint = installSigint(runtime, { turn: controller });
1465
+ const removeSigint = installSigint({ turn: controller });
1243
1466
  try {
1244
1467
  for (const goal of goals) {
1245
1468
  if (controller.signal.aborted)