@kolisachint/hoocode-agent 0.5.56 → 0.5.58
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/CHANGELOG.md +196 -0
- package/dist/core/agent-session.d.ts +2 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +4 -3
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +4 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/keybindings.d.ts +162 -108
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +225 -120
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/settings-defaults.d.ts +1 -1
- package/dist/core/settings-defaults.d.ts.map +1 -1
- package/dist/core/settings-defaults.js +1 -1
- package/dist/core/settings-defaults.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/settings-types.d.ts +1 -1
- package/dist/core/settings-types.d.ts.map +1 -1
- package/dist/core/settings-types.js.map +1 -1
- package/dist/core/tool-output-view.d.ts +28 -10
- package/dist/core/tool-output-view.d.ts.map +1 -1
- package/dist/core/tool-output-view.js +24 -9
- package/dist/core/tool-output-view.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +2 -2
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/plugins.d.ts.map +1 -1
- package/dist/core/tools/plugins.js +2 -1
- package/dist/core/tools/plugins.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +2 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/search.d.ts.map +1 -1
- package/dist/core/tools/search.js +2 -1
- package/dist/core/tools/search.js.map +1 -1
- package/dist/core/tools/webfetch.d.ts.map +1 -1
- package/dist/core/tools/webfetch.js +2 -1
- package/dist/core/tools/webfetch.js.map +1 -1
- package/dist/core/tools/websearch.d.ts.map +1 -1
- package/dist/core/tools/websearch.js +2 -1
- package/dist/core/tools/websearch.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/modes/interactive/command-executor.d.ts.map +1 -1
- package/dist/modes/interactive/command-executor.js +95 -65
- package/dist/modes/interactive/command-executor.js.map +1 -1
- package/dist/modes/interactive/components/ask-options.d.ts +20 -6
- package/dist/modes/interactive/components/ask-options.d.ts.map +1 -1
- package/dist/modes/interactive/components/ask-options.js +35 -13
- package/dist/modes/interactive/components/ask-options.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +3 -2
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +9 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +12 -0
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/task-panel.d.ts +2 -2
- package/dist/modes/interactive/components/task-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/task-panel.js +24 -11
- package/dist/modes/interactive/components/task-panel.js.map +1 -1
- package/dist/modes/interactive/components/tool-chain-summary.d.ts +2 -1
- package/dist/modes/interactive/components/tool-chain-summary.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-chain-summary.js +2 -1
- package/dist/modes/interactive/components/tool-chain-summary.js.map +1 -1
- package/dist/modes/interactive/components/tool-chain.d.ts +2 -9
- package/dist/modes/interactive/components/tool-chain.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-chain.js +3 -20
- package/dist/modes/interactive/components/tool-chain.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +16 -20
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +38 -37
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-signal.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-signal.js +1 -1
- package/dist/modes/interactive/components/tool-signal.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +29 -12
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +45 -20
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +140 -97
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/model-controller.d.ts +3 -0
- package/dist/modes/interactive/model-controller.d.ts.map +1 -1
- package/dist/modes/interactive/model-controller.js +1 -1
- package/dist/modes/interactive/model-controller.js.map +1 -1
- package/docs/keybindings.md +247 -78
- package/docs/terminal-setup.md +8 -3
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -31,7 +31,7 @@ import { SessionManager } from "../../core/session-manager.js";
|
|
|
31
31
|
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
32
32
|
import { startupProgress } from "../../core/startup-progress.js";
|
|
33
33
|
import { taskStore } from "../../core/task-store.js";
|
|
34
|
-
import { cycleToolOutputView } from "../../core/tool-output-view.js";
|
|
34
|
+
import { cycleToolOutputView, DEFAULT_TOOL_OUTPUT_VIEW, MAX_TOOL_OUTPUT_VIEW, } from "../../core/tool-output-view.js";
|
|
35
35
|
import { settleDanglingMainTasks } from "../../core/tools/todo.js";
|
|
36
36
|
import { buildCompactWordmark } from "../../core/wordmark.js";
|
|
37
37
|
import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
|
|
@@ -51,7 +51,7 @@ import { CustomEditor } from "./components/custom-editor.js";
|
|
|
51
51
|
import { CustomMessageComponent } from "./components/custom-message.js";
|
|
52
52
|
import { DynamicBorder } from "./components/dynamic-border.js";
|
|
53
53
|
import { FooterComponent } from "./components/footer.js";
|
|
54
|
-
import { keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
|
|
54
|
+
import { appKeyLabel, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
|
|
55
55
|
import { renderSessionChip } from "./components/session-chip.js";
|
|
56
56
|
import { SessionColorSelectorComponent } from "./components/session-color-selector.js";
|
|
57
57
|
import { SessionSelectorComponent } from "./components/session-selector.js";
|
|
@@ -185,6 +185,8 @@ export class InteractiveMode {
|
|
|
185
185
|
startupNoticesShown = false;
|
|
186
186
|
// Status line tracking (for mutating immediately-sequential status updates)
|
|
187
187
|
lastStatusSpacer = undefined;
|
|
188
|
+
/** Dial reverses already named this session; see showDialStep. */
|
|
189
|
+
dialReverseTaught = new Set();
|
|
188
190
|
lastStatusText = undefined;
|
|
189
191
|
// Streaming message tracking
|
|
190
192
|
streamingComponent = undefined;
|
|
@@ -202,10 +204,21 @@ export class InteractiveMode {
|
|
|
202
204
|
});
|
|
203
205
|
// Tool execution tracking: toolCallId -> component
|
|
204
206
|
pendingTools = new Map();
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
//
|
|
208
|
-
|
|
207
|
+
// Persisted view dial (radar / peek / full). See core/tool-output-view.ts.
|
|
208
|
+
toolOutputView = DEFAULT_TOOL_OUTPUT_VIEW;
|
|
209
|
+
// Where `app.tools.expand` came from, so the same key goes back there. Kept
|
|
210
|
+
// in memory only: the jump is the momentary look, the dial is the decision.
|
|
211
|
+
viewBeforeJump;
|
|
212
|
+
/**
|
|
213
|
+
* Whether everything foldable is currently open.
|
|
214
|
+
*
|
|
215
|
+
* Not a state of its own any more — it is the top of the view dial. The
|
|
216
|
+
* header, compaction and branch summaries and skill blocks follow the dial
|
|
217
|
+
* for the same reason tool bodies do: `full` means nothing is held back.
|
|
218
|
+
*/
|
|
219
|
+
get toolOutputExpanded() {
|
|
220
|
+
return this.toolOutputView === MAX_TOOL_OUTPUT_VIEW;
|
|
221
|
+
}
|
|
209
222
|
// The chain currently collecting tool calls, if the agent is mid-run.
|
|
210
223
|
openChain;
|
|
211
224
|
/** The newest tool block in the transcript; radar marks it. */
|
|
@@ -450,6 +463,7 @@ export class InteractiveMode {
|
|
|
450
463
|
},
|
|
451
464
|
showSelector: (create) => this.showSelector(create),
|
|
452
465
|
showStatus: (message) => this.showStatus(message),
|
|
466
|
+
showDialStep: (backward, message) => this.showDialStep(backward, message),
|
|
453
467
|
showError: (message) => this.showError(message),
|
|
454
468
|
showWarning: (message) => this.showWarning(message),
|
|
455
469
|
showNotice: (title, body) => this.showNotice(title, body),
|
|
@@ -698,34 +712,53 @@ export class InteractiveMode {
|
|
|
698
712
|
: theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
|
|
699
713
|
// Build startup instructions using keybinding hint helpers
|
|
700
714
|
const hint = (keybinding, description) => keyHint(keybinding, description);
|
|
715
|
+
// A dial as one line in one shape: the key steps it forward, the same
|
|
716
|
+
// key with shift steps it back. The banner is where that rule is first
|
|
717
|
+
// read, and it is the rule covering the most-pressed keys in the app,
|
|
718
|
+
// so the dials are listed together rather than scattered by topic.
|
|
719
|
+
// appKeyLabel, not keyText: the thinking dial answers to shift+tab as well,
|
|
720
|
+
// and splicing that into the middle of `alt+t/shift+alt+t` would hide the
|
|
721
|
+
// one shape these six lines exist to show.
|
|
722
|
+
const dial = (forward, backward, subject) => rawKeyHint(`${appKeyLabel(forward)}/${appKeyLabel(backward)}`, `to step ${subject}`);
|
|
723
|
+
// Grouped the way the map itself is (see core/keybindings.ts): by what you
|
|
724
|
+
// are trying to do, not by what the widget is. A flat list of thirty
|
|
725
|
+
// hints is a wall nobody reads; five short groups with a heading each is
|
|
726
|
+
// something a person can skim once and place a key in later.
|
|
727
|
+
const group = (title) => theme.fg("dim", `\n${title}`);
|
|
701
728
|
const expandedInstructions = [
|
|
729
|
+
group("Compose — the message in your hands"),
|
|
730
|
+
hint("app.editor.external", "for external editor"),
|
|
731
|
+
hint("app.input.voiceTranscribe", "to speak instead of type"),
|
|
732
|
+
hint("app.clipboard.pasteImage", "to paste image"),
|
|
733
|
+
hint("app.message.followUp", "to queue follow-up"),
|
|
734
|
+
hint("app.message.dequeue", "to edit all queued messages"),
|
|
735
|
+
rawKeyHint("drop files", "to attach"),
|
|
736
|
+
rawKeyHint("/", "for commands"),
|
|
737
|
+
rawKeyHint("!", "to run bash"),
|
|
738
|
+
rawKeyHint("!!", "to run bash (no context)"),
|
|
739
|
+
group("Steer — what the agent is before it runs"),
|
|
740
|
+
dial("app.mode.cycleForward", "app.mode.cycleBackward", "agent mode (ask/plan/build/debug)"),
|
|
741
|
+
dial("app.model.cycleForward", "app.model.cycleBackward", "model — /model to pick one"),
|
|
742
|
+
dial("app.thinking.cycleForward", "app.thinking.cycleBackward", "thinking level"),
|
|
743
|
+
group("Read — what you see of what it did"),
|
|
744
|
+
dial("app.view.cycleForward", "app.view.cycleBackward", "tool output (radar/peek/full)"),
|
|
745
|
+
dial("app.tasks.cycleForward", "app.tasks.cycleBackward", "task panel view"),
|
|
746
|
+
hint("app.tools.expand", "to jump to full output and back"),
|
|
747
|
+
hint("app.thinking.toggle", "to show or hide thinking"),
|
|
748
|
+
...(this.teamFocus.connected ? [hint("app.team.focus", "to focus team roster")] : []),
|
|
749
|
+
group("Go — sessions and places"),
|
|
750
|
+
hint("app.session.resume", "to resume a session"),
|
|
751
|
+
hint("app.session.changeDirectory", "to change working directory"),
|
|
752
|
+
dial("app.session.color.cycleForward", "app.session.color.cycleBackward", "session colour"),
|
|
753
|
+
hint("app.settings.open", "for settings"),
|
|
754
|
+
hint("app.hotkeys.open", "for all shortcuts"),
|
|
755
|
+
group("Flow — getting out, getting back"),
|
|
702
756
|
hint("app.interrupt", "to interrupt"),
|
|
703
757
|
hint("app.clear", "to clear"),
|
|
704
758
|
rawKeyHint(`${keyText("app.clear")} twice`, "to exit"),
|
|
705
759
|
hint("app.exit", "to exit (empty)"),
|
|
706
760
|
hint("app.suspend", "to suspend"),
|
|
707
761
|
keyHint("tui.editor.deleteToLineEnd", "to delete to end"),
|
|
708
|
-
hint("app.thinking.cycle", "to cycle thinking level"),
|
|
709
|
-
rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "to cycle models"),
|
|
710
|
-
hint("app.model.select", "to select model"),
|
|
711
|
-
hint("app.tools.expand", "to expand all tool output"),
|
|
712
|
-
hint("app.tools.unfoldOne", "to open one chain or block (repeat to peel back)"),
|
|
713
|
-
hint("app.view.cycleForward", "to cycle tool output (radar/glance/full)"),
|
|
714
|
-
hint("app.thinking.toggle", "to expand thinking"),
|
|
715
|
-
hint("app.tasks.cycleView", "to cycle task panel view"),
|
|
716
|
-
...(this.teamFocus.connected ? [hint("app.team.focus", "to focus team roster")] : []),
|
|
717
|
-
hint("app.mode.cycle", "to cycle agent mode"),
|
|
718
|
-
hint("app.session.changeDirectory", "to change working directory"),
|
|
719
|
-
hint("app.settings.open", "for settings"),
|
|
720
|
-
hint("app.hotkeys.open", "for all shortcuts"),
|
|
721
|
-
hint("app.editor.external", "for external editor"),
|
|
722
|
-
rawKeyHint("/", "for commands"),
|
|
723
|
-
rawKeyHint("!", "to run bash"),
|
|
724
|
-
rawKeyHint("!!", "to run bash (no context)"),
|
|
725
|
-
hint("app.message.followUp", "to queue follow-up"),
|
|
726
|
-
hint("app.message.dequeue", "to edit all queued messages"),
|
|
727
|
-
hint("app.clipboard.pasteImage", "to paste image"),
|
|
728
|
-
rawKeyHint("drop files", "to attach"),
|
|
729
762
|
].join("\n");
|
|
730
763
|
const onboarding = theme.fg("dim", `${APP_NAME} can explain its own features and look up its docs. Ask it how to use or extend ${APP_NAME}.`);
|
|
731
764
|
this.builtInHeader = new ExpandableText(() => logo(), () => `${logo()}\n${expandedInstructions}\n\n${onboarding}`, this.getStartupExpansionState(), 1, 0);
|
|
@@ -1500,20 +1533,22 @@ export class InteractiveMode {
|
|
|
1500
1533
|
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
1501
1534
|
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
1502
1535
|
this.defaultEditor.onAction("app.suspend", () => this.handleCtrlZ());
|
|
1503
|
-
this.defaultEditor.onAction("app.thinking.
|
|
1536
|
+
this.defaultEditor.onAction("app.thinking.cycleForward", () => this.cycleThinkingLevel("forward"));
|
|
1537
|
+
this.defaultEditor.onAction("app.thinking.cycleBackward", () => this.cycleThinkingLevel("backward"));
|
|
1504
1538
|
this.defaultEditor.onAction("app.model.cycleForward", () => this.modelController.cycleModel("forward"));
|
|
1505
1539
|
this.defaultEditor.onAction("app.model.cycleBackward", () => this.modelController.cycleModel("backward"));
|
|
1506
1540
|
// Global debug handler on TUI (works regardless of focus)
|
|
1507
1541
|
this.ui.onDebug = () => this.commandExecutor.handleDebug();
|
|
1508
1542
|
this.defaultEditor.onAction("app.model.select", () => this.modelController.showModelSelector());
|
|
1509
|
-
this.defaultEditor.onAction("app.tools.expand", () => this.
|
|
1543
|
+
this.defaultEditor.onAction("app.tools.expand", () => this.jumpToFullView());
|
|
1510
1544
|
this.defaultEditor.onAction("app.view.cycleForward", () => this.cycleToolOutputView("forward"));
|
|
1511
1545
|
this.defaultEditor.onAction("app.view.cycleBackward", () => this.cycleToolOutputView("backward"));
|
|
1512
|
-
this.defaultEditor.onAction("app.tools.unfoldOne", () => this.stepToolOutput("unfold"));
|
|
1513
|
-
this.defaultEditor.onAction("app.tools.foldOne", () => this.stepToolOutput("fold"));
|
|
1514
1546
|
this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
|
|
1515
|
-
this.defaultEditor.onAction("app.tasks.
|
|
1516
|
-
this.taskPanel.cycleView();
|
|
1547
|
+
this.defaultEditor.onAction("app.tasks.cycleForward", () => {
|
|
1548
|
+
this.taskPanel.cycleView("forward");
|
|
1549
|
+
});
|
|
1550
|
+
this.defaultEditor.onAction("app.tasks.cycleBackward", () => {
|
|
1551
|
+
this.taskPanel.cycleView("backward");
|
|
1517
1552
|
});
|
|
1518
1553
|
this.defaultEditor.onAction("app.team.focus", () => this.teamFocus.enterFocus());
|
|
1519
1554
|
this.defaultEditor.onAction("app.editor.external", () => this.openExternalEditor());
|
|
@@ -1534,7 +1569,8 @@ export class InteractiveMode {
|
|
|
1534
1569
|
this.defaultEditor.onAction("app.session.color.cycleBackward", () => this.cycleSessionColor("backward"));
|
|
1535
1570
|
this.defaultEditor.onAction("app.settings.open", () => this.showSettingsSelector());
|
|
1536
1571
|
this.defaultEditor.onAction("app.hotkeys.open", () => this.commandExecutor.handleHotkeys());
|
|
1537
|
-
this.defaultEditor.onAction("app.mode.
|
|
1572
|
+
this.defaultEditor.onAction("app.mode.cycleForward", () => void this.cycleAgentMode("forward"));
|
|
1573
|
+
this.defaultEditor.onAction("app.mode.cycleBackward", () => void this.cycleAgentMode("backward"));
|
|
1538
1574
|
this.defaultEditor.onChange = (text) => {
|
|
1539
1575
|
const wasBashMode = this.isBashMode;
|
|
1540
1576
|
this.isBashMode = text.trimStart().startsWith("!");
|
|
@@ -2022,7 +2058,6 @@ export class InteractiveMode {
|
|
|
2022
2058
|
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2023
2059
|
view: this.toolOutputView,
|
|
2024
2060
|
}, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
|
|
2025
|
-
component.setExpanded(this.toolOutputExpanded);
|
|
2026
2061
|
this.attachToolBlock(component);
|
|
2027
2062
|
this.pendingTools.set(content.id, component);
|
|
2028
2063
|
}
|
|
@@ -2089,7 +2124,6 @@ export class InteractiveMode {
|
|
|
2089
2124
|
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2090
2125
|
view: this.toolOutputView,
|
|
2091
2126
|
}, this.getRegisteredToolDefinition(event.toolName), this.ui, this.sessionManager.getCwd());
|
|
2092
|
-
component.setExpanded(this.toolOutputExpanded);
|
|
2093
2127
|
this.attachToolBlock(component);
|
|
2094
2128
|
this.pendingTools.set(event.toolCallId, component);
|
|
2095
2129
|
}
|
|
@@ -2261,6 +2295,28 @@ export class InteractiveMode {
|
|
|
2261
2295
|
* If multiple status messages are emitted back-to-back (without anything else being added to the chat),
|
|
2262
2296
|
* we update the previous status line instead of appending new ones to avoid log spam.
|
|
2263
2297
|
*/
|
|
2298
|
+
/**
|
|
2299
|
+
* Report a dial step, and name the key that steps it back — once.
|
|
2300
|
+
*
|
|
2301
|
+
* The instant after someone moves a dial is the one moment they are primed to
|
|
2302
|
+
* learn its other half: they have just used one direction and can feel the
|
|
2303
|
+
* missing one. So the reverse rides along with the first step of each dial in
|
|
2304
|
+
* a session and never again. A hint that repeats forever stops being read,
|
|
2305
|
+
* and its cost falls entirely on the people who already know it.
|
|
2306
|
+
*
|
|
2307
|
+
* Only the three dials that announce their new value in the transcript come
|
|
2308
|
+
* through here. The other three announce themselves where they live — the
|
|
2309
|
+
* task panel prints its own key in its header, and the mode chip and view
|
|
2310
|
+
* glyph change in place in the footer.
|
|
2311
|
+
*/
|
|
2312
|
+
showDialStep(backward, message) {
|
|
2313
|
+
if (this.dialReverseTaught.has(backward)) {
|
|
2314
|
+
this.showStatus(message);
|
|
2315
|
+
return;
|
|
2316
|
+
}
|
|
2317
|
+
this.dialReverseTaught.add(backward);
|
|
2318
|
+
this.showStatus(theme.fg("dim", message) + theme.fg("halftone", ` ${keyText(backward)} steps back`));
|
|
2319
|
+
}
|
|
2264
2320
|
showStatus(message) {
|
|
2265
2321
|
const children = this.chatContainer.children;
|
|
2266
2322
|
const last = children.length > 0 ? children[children.length - 1] : undefined;
|
|
@@ -2388,7 +2444,6 @@ export class InteractiveMode {
|
|
|
2388
2444
|
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2389
2445
|
view: this.toolOutputView,
|
|
2390
2446
|
}, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
|
|
2391
|
-
component.setExpanded(this.toolOutputExpanded);
|
|
2392
2447
|
this.attachToolBlock(component);
|
|
2393
2448
|
if (message.stopReason === "aborted" || message.stopReason === "error") {
|
|
2394
2449
|
let errorMessage;
|
|
@@ -2645,15 +2700,15 @@ export class InteractiveMode {
|
|
|
2645
2700
|
}
|
|
2646
2701
|
this.ui.requestRender();
|
|
2647
2702
|
}
|
|
2648
|
-
cycleThinkingLevel() {
|
|
2649
|
-
const newLevel = this.session.cycleThinkingLevel();
|
|
2703
|
+
cycleThinkingLevel(direction) {
|
|
2704
|
+
const newLevel = this.session.cycleThinkingLevel(direction);
|
|
2650
2705
|
if (newLevel === undefined) {
|
|
2651
2706
|
this.showStatus("Current model does not support thinking");
|
|
2652
2707
|
}
|
|
2653
2708
|
else {
|
|
2654
2709
|
this.footer.invalidate();
|
|
2655
2710
|
this.updateEditorBorderColor();
|
|
2656
|
-
this.
|
|
2711
|
+
this.showDialStep("app.thinking.cycleBackward", `Thinking level: ${newLevel}`);
|
|
2657
2712
|
}
|
|
2658
2713
|
}
|
|
2659
2714
|
/**
|
|
@@ -2714,66 +2769,35 @@ export class InteractiveMode {
|
|
|
2714
2769
|
}
|
|
2715
2770
|
return blocks;
|
|
2716
2771
|
}
|
|
2717
|
-
/** Every chain in the transcript, in order. */
|
|
2718
|
-
transcriptChains() {
|
|
2719
|
-
return this.chatContainer.children.filter((child) => child instanceof ToolChainComponent);
|
|
2720
|
-
}
|
|
2721
|
-
toggleToolOutputExpansion() {
|
|
2722
|
-
this.setToolsExpanded(!this.toolOutputExpanded);
|
|
2723
|
-
}
|
|
2724
2772
|
/**
|
|
2725
|
-
*
|
|
2773
|
+
* Jump to the full view, or back to where the jump started.
|
|
2726
2774
|
*
|
|
2727
|
-
*
|
|
2728
|
-
*
|
|
2729
|
-
*
|
|
2730
|
-
*
|
|
2775
|
+
* The dial's top stop is the whole result with nothing trimmed, which is what
|
|
2776
|
+
* "expand" always meant — so expand is not a second state layered over the
|
|
2777
|
+
* dial any more, it is a jump along it. From any stop the first press lands
|
|
2778
|
+
* on `full`; the next press returns to the stop you came from (`peek` if the
|
|
2779
|
+
* jump started there, so the key is never a no-op).
|
|
2731
2780
|
*
|
|
2732
|
-
*
|
|
2733
|
-
*
|
|
2734
|
-
* scrolling — anything far enough up is in the terminal's own scrollback,
|
|
2735
|
-
* where this process cannot put a cursor or scroll to a selection. So the
|
|
2736
|
-
* key peels backwards through what is actually on screen, and each block it
|
|
2737
|
-
* opens is its own marker for where you have got to.
|
|
2781
|
+
* Only `app.view.cycleForward` writes the setting. This key deliberately does
|
|
2782
|
+
* not: the dial is the decision you keep, the jump is the look you take.
|
|
2738
2783
|
*/
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
if (this.toolOutputView === "radar") {
|
|
2745
|
-
const chains = this.transcriptChains();
|
|
2746
|
-
for (let i = chains.length - 1; i >= 0; i--) {
|
|
2747
|
-
if (chains[i].isEmpty || chains[i].isOpened === wantOpen)
|
|
2748
|
-
continue;
|
|
2749
|
-
// A chain of one is never summarised, so there is nothing for unfold to
|
|
2750
|
-
// reveal; skipping it keeps the press moving to a chain that will change.
|
|
2751
|
-
if (wantOpen && !chains[i].isSummarised)
|
|
2752
|
-
continue;
|
|
2753
|
-
chains[i].setOpened(wantOpen);
|
|
2754
|
-
this.ui.requestRender();
|
|
2755
|
-
return;
|
|
2756
|
-
}
|
|
2757
|
-
this.showStatus(wantOpen ? "No collapsed chains below this point" : "No open chains below this point");
|
|
2784
|
+
jumpToFullView() {
|
|
2785
|
+
if (this.toolOutputView === MAX_TOOL_OUTPUT_VIEW) {
|
|
2786
|
+
const back = this.viewBeforeJump ?? DEFAULT_TOOL_OUTPUT_VIEW;
|
|
2787
|
+
this.viewBeforeJump = undefined;
|
|
2788
|
+
this.applyToolOutputView(back, { persist: false });
|
|
2758
2789
|
return;
|
|
2759
2790
|
}
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
const block = blocks[i];
|
|
2763
|
-
if (block.isRevealed() === wantOpen)
|
|
2764
|
-
continue;
|
|
2765
|
-
block.setExpanded(wantOpen);
|
|
2766
|
-
this.ui.requestRender();
|
|
2767
|
-
return;
|
|
2768
|
-
}
|
|
2769
|
-
this.showStatus(wantOpen ? "No folded tool output below this point" : "No unfolded tool output below this point");
|
|
2791
|
+
this.viewBeforeJump = this.toolOutputView;
|
|
2792
|
+
this.applyToolOutputView(MAX_TOOL_OUTPUT_VIEW, { persist: false });
|
|
2770
2793
|
}
|
|
2771
2794
|
/**
|
|
2772
|
-
* Move the view dial one stop
|
|
2795
|
+
* Move the view dial one stop; the footer shows where it landed.
|
|
2773
2796
|
*
|
|
2774
|
-
*
|
|
2775
|
-
*
|
|
2776
|
-
*
|
|
2797
|
+
* This is the persistent decision ("how much do I ever want to see"), which
|
|
2798
|
+
* is why it saves. `app.tools.expand` jumps along the same dial without
|
|
2799
|
+
* saving, which is what keeps "the look I am taking now" from overwriting
|
|
2800
|
+
* "the view I live in".
|
|
2777
2801
|
*/
|
|
2778
2802
|
cycleToolOutputView(direction) {
|
|
2779
2803
|
const next = cycleToolOutputView(this.toolOutputView, direction);
|
|
@@ -2802,10 +2826,17 @@ export class InteractiveMode {
|
|
|
2802
2826
|
return "omit";
|
|
2803
2827
|
return this.hideThinkingBlock ? "label" : "full";
|
|
2804
2828
|
}
|
|
2805
|
-
applyToolOutputView(view) {
|
|
2829
|
+
applyToolOutputView(view, options = {}) {
|
|
2806
2830
|
const previousThinking = this.thinkingDisplayForView();
|
|
2831
|
+
const wasExpanded = this.toolOutputExpanded;
|
|
2807
2832
|
this.toolOutputView = view;
|
|
2808
|
-
|
|
2833
|
+
if (options.persist !== false) {
|
|
2834
|
+
this.settingsManager.setToolOutputView(view);
|
|
2835
|
+
// Anything that moves the dial deliberately — the cycle key, the
|
|
2836
|
+
// settings pane — is a new home stop, so the jump has nowhere stale to
|
|
2837
|
+
// return to.
|
|
2838
|
+
this.viewBeforeJump = undefined;
|
|
2839
|
+
}
|
|
2809
2840
|
this.footer.setToolOutputView(view);
|
|
2810
2841
|
const thinking = this.thinkingDisplayForView();
|
|
2811
2842
|
for (const child of this.chatContainer.children) {
|
|
@@ -2820,10 +2851,18 @@ export class InteractiveMode {
|
|
|
2820
2851
|
if (this.streamingComponent && thinking !== previousThinking) {
|
|
2821
2852
|
this.streamingComponent.setThinkingDisplay(thinking);
|
|
2822
2853
|
}
|
|
2854
|
+
// The header and the summary blocks are not tool output, but "full" means
|
|
2855
|
+
// nothing is held back, so they open and close with the dial's top stop.
|
|
2856
|
+
if (this.toolOutputExpanded !== wasExpanded)
|
|
2857
|
+
this.setToolsExpanded(this.toolOutputExpanded);
|
|
2823
2858
|
this.ui.requestRender();
|
|
2824
2859
|
}
|
|
2860
|
+
/**
|
|
2861
|
+
* Open or close everything that folds but is not a tool call — the header,
|
|
2862
|
+
* compaction and branch summaries, skill blocks. Tool blocks and chains are
|
|
2863
|
+
* not in this sweep: they take the view dial directly.
|
|
2864
|
+
*/
|
|
2825
2865
|
setToolsExpanded(expanded) {
|
|
2826
|
-
this.toolOutputExpanded = expanded;
|
|
2827
2866
|
const activeHeader = this.chrome.customHeader ?? this.builtInHeader;
|
|
2828
2867
|
if (isExpandable(activeHeader)) {
|
|
2829
2868
|
activeHeader.setExpanded(expanded);
|
|
@@ -2845,7 +2884,7 @@ export class InteractiveMode {
|
|
|
2845
2884
|
* is missing (a `--light` run strips the extension), the key says so instead
|
|
2846
2885
|
* of guessing.
|
|
2847
2886
|
*/
|
|
2848
|
-
async cycleAgentMode() {
|
|
2887
|
+
async cycleAgentMode(direction) {
|
|
2849
2888
|
const command = this.session.extensionRunner.getCommand("mode");
|
|
2850
2889
|
if (!command) {
|
|
2851
2890
|
this.showWarning("Modes are not available in this session");
|
|
@@ -2858,8 +2897,12 @@ export class InteractiveMode {
|
|
|
2858
2897
|
return;
|
|
2859
2898
|
}
|
|
2860
2899
|
const current = this.footerDataProvider.getActiveMode();
|
|
2900
|
+
// indexOf is -1 when the active mode is not in the list; stepping from
|
|
2901
|
+
// there lands on the first mode going forward and the last going back,
|
|
2902
|
+
// which is the useful answer either way.
|
|
2861
2903
|
const index = modes.indexOf(current);
|
|
2862
|
-
const
|
|
2904
|
+
const step = direction === "forward" ? 1 : -1;
|
|
2905
|
+
const next = modes[(index + step + modes.length) % modes.length];
|
|
2863
2906
|
await this.session.prompt(`/mode ${next}`);
|
|
2864
2907
|
}
|
|
2865
2908
|
/**
|
|
@@ -2876,7 +2919,7 @@ export class InteractiveMode {
|
|
|
2876
2919
|
const slot = cycleSessionColorSlot(this.sessionManager.getSessionColorSlot(), direction);
|
|
2877
2920
|
this.session.setSessionColor(slot);
|
|
2878
2921
|
const name = sessionColorName(slot);
|
|
2879
|
-
this.
|
|
2922
|
+
this.showDialStep("app.session.color.cycleBackward", `Session color: ${name ?? slot}`);
|
|
2880
2923
|
}
|
|
2881
2924
|
toggleThinkingBlockVisibility() {
|
|
2882
2925
|
this.hideThinkingBlock = !this.hideThinkingBlock;
|