@kolisachint/hoocode-agent 0.5.55 → 0.5.57

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 (80) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/core/keybindings.d.ts +12 -12
  3. package/dist/core/keybindings.d.ts.map +1 -1
  4. package/dist/core/keybindings.js +17 -16
  5. package/dist/core/keybindings.js.map +1 -1
  6. package/dist/core/session-identity.d.ts +11 -0
  7. package/dist/core/session-identity.d.ts.map +1 -1
  8. package/dist/core/session-identity.js +16 -0
  9. package/dist/core/session-identity.js.map +1 -1
  10. package/dist/core/settings-defaults.d.ts +1 -1
  11. package/dist/core/settings-defaults.d.ts.map +1 -1
  12. package/dist/core/settings-defaults.js +1 -1
  13. package/dist/core/settings-defaults.js.map +1 -1
  14. package/dist/core/settings-manager.d.ts +1 -1
  15. package/dist/core/settings-manager.d.ts.map +1 -1
  16. package/dist/core/settings-manager.js +1 -1
  17. package/dist/core/settings-manager.js.map +1 -1
  18. package/dist/core/settings-types.d.ts +1 -1
  19. package/dist/core/settings-types.d.ts.map +1 -1
  20. package/dist/core/settings-types.js.map +1 -1
  21. package/dist/core/tool-output-view.d.ts +28 -10
  22. package/dist/core/tool-output-view.d.ts.map +1 -1
  23. package/dist/core/tool-output-view.js +24 -9
  24. package/dist/core/tool-output-view.js.map +1 -1
  25. package/dist/core/tools/bash.d.ts.map +1 -1
  26. package/dist/core/tools/bash.js +2 -2
  27. package/dist/core/tools/bash.js.map +1 -1
  28. package/dist/core/tools/plugins.d.ts.map +1 -1
  29. package/dist/core/tools/plugins.js +2 -1
  30. package/dist/core/tools/plugins.js.map +1 -1
  31. package/dist/core/tools/read.d.ts.map +1 -1
  32. package/dist/core/tools/read.js +2 -1
  33. package/dist/core/tools/read.js.map +1 -1
  34. package/dist/core/tools/search.d.ts.map +1 -1
  35. package/dist/core/tools/search.js +2 -1
  36. package/dist/core/tools/search.js.map +1 -1
  37. package/dist/core/tools/webfetch.d.ts.map +1 -1
  38. package/dist/core/tools/webfetch.js +2 -1
  39. package/dist/core/tools/webfetch.js.map +1 -1
  40. package/dist/core/tools/websearch.d.ts.map +1 -1
  41. package/dist/core/tools/websearch.js +2 -1
  42. package/dist/core/tools/websearch.js.map +1 -1
  43. package/dist/core/tools/write.d.ts.map +1 -1
  44. package/dist/core/tools/write.js +2 -1
  45. package/dist/core/tools/write.js.map +1 -1
  46. package/dist/modes/interactive/command-executor.d.ts.map +1 -1
  47. package/dist/modes/interactive/command-executor.js +5 -5
  48. package/dist/modes/interactive/command-executor.js.map +1 -1
  49. package/dist/modes/interactive/components/footer.d.ts +1 -1
  50. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  51. package/dist/modes/interactive/components/footer.js +3 -2
  52. package/dist/modes/interactive/components/footer.js.map +1 -1
  53. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  54. package/dist/modes/interactive/components/settings-selector.js +1 -1
  55. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  56. package/dist/modes/interactive/components/tool-chain-summary.d.ts +2 -1
  57. package/dist/modes/interactive/components/tool-chain-summary.d.ts.map +1 -1
  58. package/dist/modes/interactive/components/tool-chain-summary.js +2 -1
  59. package/dist/modes/interactive/components/tool-chain-summary.js.map +1 -1
  60. package/dist/modes/interactive/components/tool-chain.d.ts +2 -9
  61. package/dist/modes/interactive/components/tool-chain.d.ts.map +1 -1
  62. package/dist/modes/interactive/components/tool-chain.js +3 -20
  63. package/dist/modes/interactive/components/tool-chain.js.map +1 -1
  64. package/dist/modes/interactive/components/tool-execution.d.ts +16 -20
  65. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  66. package/dist/modes/interactive/components/tool-execution.js +38 -37
  67. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  68. package/dist/modes/interactive/components/tool-signal.d.ts.map +1 -1
  69. package/dist/modes/interactive/components/tool-signal.js +1 -1
  70. package/dist/modes/interactive/components/tool-signal.js.map +1 -1
  71. package/dist/modes/interactive/interactive-mode.d.ts +39 -20
  72. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  73. package/dist/modes/interactive/interactive-mode.js +77 -69
  74. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  75. package/docs/keybindings.md +5 -1
  76. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  77. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  78. package/examples/extensions/sandbox/package.json +1 -1
  79. package/examples/extensions/with-deps/package.json +1 -1
  80. package/package.json +4 -4
@@ -26,11 +26,12 @@ import { formatTokens } from "../../core/format-tokens.js";
26
26
  import { KeybindingsManager } from "../../core/keybindings.js";
27
27
  import { measurePromptSurface, measureToolSchemaTokens } from "../../core/light.js";
28
28
  import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
29
+ import { cycleSessionColorSlot, sessionColorName } from "../../core/session-identity.js";
29
30
  import { SessionManager } from "../../core/session-manager.js";
30
31
  import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
31
32
  import { startupProgress } from "../../core/startup-progress.js";
32
33
  import { taskStore } from "../../core/task-store.js";
33
- 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";
34
35
  import { settleDanglingMainTasks } from "../../core/tools/todo.js";
35
36
  import { buildCompactWordmark } from "../../core/wordmark.js";
36
37
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
@@ -201,10 +202,21 @@ export class InteractiveMode {
201
202
  });
202
203
  // Tool execution tracking: toolCallId -> component
203
204
  pendingTools = new Map();
204
- // Tool output expansion state
205
- toolOutputExpanded = false;
206
- // Persisted view dial (radar / glance / full). See core/tool-output-view.ts.
207
- toolOutputView = "glance";
205
+ // Persisted view dial (radar / peek / full). See core/tool-output-view.ts.
206
+ toolOutputView = DEFAULT_TOOL_OUTPUT_VIEW;
207
+ // Where `app.tools.expand` came from, so the same key goes back there. Kept
208
+ // in memory only: the jump is the momentary look, the dial is the decision.
209
+ viewBeforeJump;
210
+ /**
211
+ * Whether everything foldable is currently open.
212
+ *
213
+ * Not a state of its own any more — it is the top of the view dial. The
214
+ * header, compaction and branch summaries and skill blocks follow the dial
215
+ * for the same reason tool bodies do: `full` means nothing is held back.
216
+ */
217
+ get toolOutputExpanded() {
218
+ return this.toolOutputView === MAX_TOOL_OUTPUT_VIEW;
219
+ }
208
220
  // The chain currently collecting tool calls, if the agent is mid-run.
209
221
  openChain;
210
222
  /** The newest tool block in the transcript; radar marks it. */
@@ -707,9 +719,8 @@ export class InteractiveMode {
707
719
  hint("app.thinking.cycle", "to cycle thinking level"),
708
720
  rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "to cycle models"),
709
721
  hint("app.model.select", "to select model"),
710
- hint("app.tools.expand", "to expand all tool output"),
711
- hint("app.tools.unfoldOne", "to open one chain or block (repeat to peel back)"),
712
- hint("app.view.cycleForward", "to cycle tool output (radar/glance/full)"),
722
+ hint("app.tools.expand", "to jump to full output and back"),
723
+ hint("app.view.cycleForward", "to cycle tool output (radar/peek/full)"),
713
724
  hint("app.thinking.toggle", "to expand thinking"),
714
725
  hint("app.tasks.cycleView", "to cycle task panel view"),
715
726
  ...(this.teamFocus.connected ? [hint("app.team.focus", "to focus team roster")] : []),
@@ -1505,11 +1516,9 @@ export class InteractiveMode {
1505
1516
  // Global debug handler on TUI (works regardless of focus)
1506
1517
  this.ui.onDebug = () => this.commandExecutor.handleDebug();
1507
1518
  this.defaultEditor.onAction("app.model.select", () => this.modelController.showModelSelector());
1508
- this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion());
1519
+ this.defaultEditor.onAction("app.tools.expand", () => this.jumpToFullView());
1509
1520
  this.defaultEditor.onAction("app.view.cycleForward", () => this.cycleToolOutputView("forward"));
1510
1521
  this.defaultEditor.onAction("app.view.cycleBackward", () => this.cycleToolOutputView("backward"));
1511
- this.defaultEditor.onAction("app.tools.unfoldOne", () => this.stepToolOutput("unfold"));
1512
- this.defaultEditor.onAction("app.tools.foldOne", () => this.stepToolOutput("fold"));
1513
1522
  this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
1514
1523
  this.defaultEditor.onAction("app.tasks.cycleView", () => {
1515
1524
  this.taskPanel.cycleView();
@@ -1529,6 +1538,8 @@ export class InteractiveMode {
1529
1538
  this.editor.setText("/cd ");
1530
1539
  this.ui.requestRender();
1531
1540
  });
1541
+ this.defaultEditor.onAction("app.session.color.cycleForward", () => this.cycleSessionColor("forward"));
1542
+ this.defaultEditor.onAction("app.session.color.cycleBackward", () => this.cycleSessionColor("backward"));
1532
1543
  this.defaultEditor.onAction("app.settings.open", () => this.showSettingsSelector());
1533
1544
  this.defaultEditor.onAction("app.hotkeys.open", () => this.commandExecutor.handleHotkeys());
1534
1545
  this.defaultEditor.onAction("app.mode.cycle", () => void this.cycleAgentMode());
@@ -2019,7 +2030,6 @@ export class InteractiveMode {
2019
2030
  imageWidthCells: this.settingsManager.getImageWidthCells(),
2020
2031
  view: this.toolOutputView,
2021
2032
  }, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
2022
- component.setExpanded(this.toolOutputExpanded);
2023
2033
  this.attachToolBlock(component);
2024
2034
  this.pendingTools.set(content.id, component);
2025
2035
  }
@@ -2086,7 +2096,6 @@ export class InteractiveMode {
2086
2096
  imageWidthCells: this.settingsManager.getImageWidthCells(),
2087
2097
  view: this.toolOutputView,
2088
2098
  }, this.getRegisteredToolDefinition(event.toolName), this.ui, this.sessionManager.getCwd());
2089
- component.setExpanded(this.toolOutputExpanded);
2090
2099
  this.attachToolBlock(component);
2091
2100
  this.pendingTools.set(event.toolCallId, component);
2092
2101
  }
@@ -2385,7 +2394,6 @@ export class InteractiveMode {
2385
2394
  imageWidthCells: this.settingsManager.getImageWidthCells(),
2386
2395
  view: this.toolOutputView,
2387
2396
  }, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
2388
- component.setExpanded(this.toolOutputExpanded);
2389
2397
  this.attachToolBlock(component);
2390
2398
  if (message.stopReason === "aborted" || message.stopReason === "error") {
2391
2399
  let errorMessage;
@@ -2711,66 +2719,35 @@ export class InteractiveMode {
2711
2719
  }
2712
2720
  return blocks;
2713
2721
  }
2714
- /** Every chain in the transcript, in order. */
2715
- transcriptChains() {
2716
- return this.chatContainer.children.filter((child) => child instanceof ToolChainComponent);
2717
- }
2718
- toggleToolOutputExpansion() {
2719
- this.setToolsExpanded(!this.toolOutputExpanded);
2720
- }
2721
2722
  /**
2722
- * Open or close one tool block instead of all of them.
2723
+ * Jump to the full view, or back to where the jump started.
2723
2724
  *
2724
- * `ctrl+o` is all-or-nothing, which left the caret on every glance and
2725
- * radar row advertising something no key could do. This is the missing half:
2726
- * unfold walks back from the newest block to the first one still folded,
2727
- * fold walks back to the most recently opened one.
2725
+ * The dial's top stop is the whole result with nothing trimmed, which is what
2726
+ * "expand" always meant so expand is not a second state layered over the
2727
+ * dial any more, it is a jump along it. From any stop the first press lands
2728
+ * on `full`; the next press returns to the stop you came from (`peek` if the
2729
+ * jump started there, so the key is never a no-op).
2728
2730
  *
2729
- * Working from the tail is not a shortcut, it is the only thing this view can
2730
- * honestly offer. The transcript is bottom-anchored and has no app-level
2731
- * scrolling — anything far enough up is in the terminal's own scrollback,
2732
- * where this process cannot put a cursor or scroll to a selection. So the
2733
- * key peels backwards through what is actually on screen, and each block it
2734
- * opens is its own marker for where you have got to.
2731
+ * Only `app.view.cycleForward` writes the setting. This key deliberately does
2732
+ * not: the dial is the decision you keep, the jump is the look you take.
2735
2733
  */
2736
- stepToolOutput(direction) {
2737
- const wantOpen = direction === "unfold";
2738
- // Radar's unit is the chain: one press turns the newest summary line back
2739
- // into the calls it stands for. Its per-call bodies are a glance/full
2740
- // question, so the step stops there rather than cascading.
2741
- if (this.toolOutputView === "radar") {
2742
- const chains = this.transcriptChains();
2743
- for (let i = chains.length - 1; i >= 0; i--) {
2744
- if (chains[i].isEmpty || chains[i].isOpened === wantOpen)
2745
- continue;
2746
- // A chain of one is never summarised, so there is nothing for unfold to
2747
- // reveal; skipping it keeps the press moving to a chain that will change.
2748
- if (wantOpen && !chains[i].isSummarised)
2749
- continue;
2750
- chains[i].setOpened(wantOpen);
2751
- this.ui.requestRender();
2752
- return;
2753
- }
2754
- this.showStatus(wantOpen ? "No collapsed chains below this point" : "No open chains below this point");
2734
+ jumpToFullView() {
2735
+ if (this.toolOutputView === MAX_TOOL_OUTPUT_VIEW) {
2736
+ const back = this.viewBeforeJump ?? DEFAULT_TOOL_OUTPUT_VIEW;
2737
+ this.viewBeforeJump = undefined;
2738
+ this.applyToolOutputView(back, { persist: false });
2755
2739
  return;
2756
2740
  }
2757
- const blocks = this.transcriptToolBlocks();
2758
- for (let i = blocks.length - 1; i >= 0; i--) {
2759
- const block = blocks[i];
2760
- if (block.isRevealed() === wantOpen)
2761
- continue;
2762
- block.setExpanded(wantOpen);
2763
- this.ui.requestRender();
2764
- return;
2765
- }
2766
- this.showStatus(wantOpen ? "No folded tool output below this point" : "No unfolded tool output below this point");
2741
+ this.viewBeforeJump = this.toolOutputView;
2742
+ this.applyToolOutputView(MAX_TOOL_OUTPUT_VIEW, { persist: false });
2767
2743
  }
2768
2744
  /**
2769
- * Move the view dial one stop and report where it landed.
2745
+ * Move the view dial one stop; the footer shows where it landed.
2770
2746
  *
2771
- * The dial is the persistent decision ("how much do I ever want to see"),
2772
- * which is why it saves; `app.tools.expand` stays the momentary one ("open
2773
- * what is in front of me"), which is why it does not.
2747
+ * This is the persistent decision ("how much do I ever want to see"), which
2748
+ * is why it saves. `app.tools.expand` jumps along the same dial without
2749
+ * saving, which is what keeps "the look I am taking now" from overwriting
2750
+ * "the view I live in".
2774
2751
  */
2775
2752
  cycleToolOutputView(direction) {
2776
2753
  const next = cycleToolOutputView(this.toolOutputView, direction);
@@ -2799,10 +2776,17 @@ export class InteractiveMode {
2799
2776
  return "omit";
2800
2777
  return this.hideThinkingBlock ? "label" : "full";
2801
2778
  }
2802
- applyToolOutputView(view) {
2779
+ applyToolOutputView(view, options = {}) {
2803
2780
  const previousThinking = this.thinkingDisplayForView();
2781
+ const wasExpanded = this.toolOutputExpanded;
2804
2782
  this.toolOutputView = view;
2805
- this.settingsManager.setToolOutputView(view);
2783
+ if (options.persist !== false) {
2784
+ this.settingsManager.setToolOutputView(view);
2785
+ // Anything that moves the dial deliberately — the cycle key, the
2786
+ // settings pane — is a new home stop, so the jump has nowhere stale to
2787
+ // return to.
2788
+ this.viewBeforeJump = undefined;
2789
+ }
2806
2790
  this.footer.setToolOutputView(view);
2807
2791
  const thinking = this.thinkingDisplayForView();
2808
2792
  for (const child of this.chatContainer.children) {
@@ -2817,10 +2801,18 @@ export class InteractiveMode {
2817
2801
  if (this.streamingComponent && thinking !== previousThinking) {
2818
2802
  this.streamingComponent.setThinkingDisplay(thinking);
2819
2803
  }
2804
+ // The header and the summary blocks are not tool output, but "full" means
2805
+ // nothing is held back, so they open and close with the dial's top stop.
2806
+ if (this.toolOutputExpanded !== wasExpanded)
2807
+ this.setToolsExpanded(this.toolOutputExpanded);
2820
2808
  this.ui.requestRender();
2821
2809
  }
2810
+ /**
2811
+ * Open or close everything that folds but is not a tool call — the header,
2812
+ * compaction and branch summaries, skill blocks. Tool blocks and chains are
2813
+ * not in this sweep: they take the view dial directly.
2814
+ */
2822
2815
  setToolsExpanded(expanded) {
2823
- this.toolOutputExpanded = expanded;
2824
2816
  const activeHeader = this.chrome.customHeader ?? this.builtInHeader;
2825
2817
  if (isExpandable(activeHeader)) {
2826
2818
  activeHeader.setExpanded(expanded);
@@ -2859,6 +2851,22 @@ export class InteractiveMode {
2859
2851
  const next = modes[(index + 1) % modes.length];
2860
2852
  await this.session.prompt(`/mode ${next}`);
2861
2853
  }
2854
+ /**
2855
+ * Step the session chip to the next colour slot, no picker involved.
2856
+ *
2857
+ * The picker exists for choosing a colour; this exists for telling two
2858
+ * terminals apart, which is a different job — you press the key until the two
2859
+ * chips stop looking alike, and the chip repaints on each press because
2860
+ * setSessionColor writes the slot and the session_info event refreshes the
2861
+ * identity. The status line names the slot as well, since the point of the
2862
+ * names is that `/color <name>` takes them.
2863
+ */
2864
+ cycleSessionColor(direction) {
2865
+ const slot = cycleSessionColorSlot(this.sessionManager.getSessionColorSlot(), direction);
2866
+ this.session.setSessionColor(slot);
2867
+ const name = sessionColorName(slot);
2868
+ this.showStatus(`Session color: ${name ?? slot}`);
2869
+ }
2862
2870
  toggleThinkingBlockVisibility() {
2863
2871
  this.hideThinkingBlock = !this.hideThinkingBlock;
2864
2872
  this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
@@ -3522,7 +3530,7 @@ export class InteractiveMode {
3522
3530
  const selector = new SessionColorSelectorComponent(this.sessionManager.getDisplayName(), originalSlot, (slot) => {
3523
3531
  done();
3524
3532
  this.session.setSessionColor(slot);
3525
- this.showStatus(`Session color set to ${slot}`);
3533
+ this.showStatus(`Session color: ${sessionColorName(slot) ?? slot}`);
3526
3534
  }, () => {
3527
3535
  done();
3528
3536
  // Cancelling must put back the colour the session actually has,