@kolisachint/hoocode-agent 0.5.57 → 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.
Files changed (45) hide show
  1. package/CHANGELOG.md +163 -0
  2. package/dist/core/agent-session.d.ts +2 -2
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +4 -3
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/extensions/runner.d.ts.map +1 -1
  7. package/dist/core/extensions/runner.js +4 -1
  8. package/dist/core/extensions/runner.js.map +1 -1
  9. package/dist/core/keybindings.d.ts +162 -98
  10. package/dist/core/keybindings.d.ts.map +1 -1
  11. package/dist/core/keybindings.js +222 -106
  12. package/dist/core/keybindings.js.map +1 -1
  13. package/dist/modes/interactive/command-executor.d.ts.map +1 -1
  14. package/dist/modes/interactive/command-executor.js +95 -62
  15. package/dist/modes/interactive/command-executor.js.map +1 -1
  16. package/dist/modes/interactive/components/ask-options.d.ts +20 -6
  17. package/dist/modes/interactive/components/ask-options.d.ts.map +1 -1
  18. package/dist/modes/interactive/components/ask-options.js +35 -13
  19. package/dist/modes/interactive/components/ask-options.js.map +1 -1
  20. package/dist/modes/interactive/components/keybinding-hints.d.ts +9 -0
  21. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  22. package/dist/modes/interactive/components/keybinding-hints.js +12 -0
  23. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  24. package/dist/modes/interactive/components/task-panel.d.ts +2 -2
  25. package/dist/modes/interactive/components/task-panel.d.ts.map +1 -1
  26. package/dist/modes/interactive/components/task-panel.js +24 -11
  27. package/dist/modes/interactive/components/task-panel.js.map +1 -1
  28. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  29. package/dist/modes/interactive/components/tree-selector.js +29 -12
  30. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  31. package/dist/modes/interactive/interactive-mode.d.ts +17 -0
  32. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  33. package/dist/modes/interactive/interactive-mode.js +85 -31
  34. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  35. package/dist/modes/interactive/model-controller.d.ts +3 -0
  36. package/dist/modes/interactive/model-controller.d.ts.map +1 -1
  37. package/dist/modes/interactive/model-controller.js +1 -1
  38. package/dist/modes/interactive/model-controller.js.map +1 -1
  39. package/docs/keybindings.md +247 -80
  40. package/docs/terminal-setup.md +8 -3
  41. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  42. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  43. package/examples/extensions/sandbox/package.json +1 -1
  44. package/examples/extensions/with-deps/package.json +1 -1
  45. package/package.json +4 -4
@@ -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;
@@ -461,6 +463,7 @@ export class InteractiveMode {
461
463
  },
462
464
  showSelector: (create) => this.showSelector(create),
463
465
  showStatus: (message) => this.showStatus(message),
466
+ showDialStep: (backward, message) => this.showDialStep(backward, message),
464
467
  showError: (message) => this.showError(message),
465
468
  showWarning: (message) => this.showWarning(message),
466
469
  showNotice: (title, body) => this.showNotice(title, body),
@@ -709,33 +712,53 @@ export class InteractiveMode {
709
712
  : theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
710
713
  // Build startup instructions using keybinding hint helpers
711
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}`);
712
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"),
713
756
  hint("app.interrupt", "to interrupt"),
714
757
  hint("app.clear", "to clear"),
715
758
  rawKeyHint(`${keyText("app.clear")} twice`, "to exit"),
716
759
  hint("app.exit", "to exit (empty)"),
717
760
  hint("app.suspend", "to suspend"),
718
761
  keyHint("tui.editor.deleteToLineEnd", "to delete to end"),
719
- hint("app.thinking.cycle", "to cycle thinking level"),
720
- rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "to cycle models"),
721
- hint("app.model.select", "to select model"),
722
- hint("app.tools.expand", "to jump to full output and back"),
723
- hint("app.view.cycleForward", "to cycle tool output (radar/peek/full)"),
724
- hint("app.thinking.toggle", "to expand thinking"),
725
- hint("app.tasks.cycleView", "to cycle task panel view"),
726
- ...(this.teamFocus.connected ? [hint("app.team.focus", "to focus team roster")] : []),
727
- hint("app.mode.cycle", "to cycle agent mode"),
728
- hint("app.session.changeDirectory", "to change working directory"),
729
- hint("app.settings.open", "for settings"),
730
- hint("app.hotkeys.open", "for all shortcuts"),
731
- hint("app.editor.external", "for external editor"),
732
- rawKeyHint("/", "for commands"),
733
- rawKeyHint("!", "to run bash"),
734
- rawKeyHint("!!", "to run bash (no context)"),
735
- hint("app.message.followUp", "to queue follow-up"),
736
- hint("app.message.dequeue", "to edit all queued messages"),
737
- hint("app.clipboard.pasteImage", "to paste image"),
738
- rawKeyHint("drop files", "to attach"),
739
762
  ].join("\n");
740
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}.`);
741
764
  this.builtInHeader = new ExpandableText(() => logo(), () => `${logo()}\n${expandedInstructions}\n\n${onboarding}`, this.getStartupExpansionState(), 1, 0);
@@ -1510,7 +1533,8 @@ export class InteractiveMode {
1510
1533
  this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
1511
1534
  this.defaultEditor.onCtrlD = () => this.handleCtrlD();
1512
1535
  this.defaultEditor.onAction("app.suspend", () => this.handleCtrlZ());
1513
- this.defaultEditor.onAction("app.thinking.cycle", () => this.cycleThinkingLevel());
1536
+ this.defaultEditor.onAction("app.thinking.cycleForward", () => this.cycleThinkingLevel("forward"));
1537
+ this.defaultEditor.onAction("app.thinking.cycleBackward", () => this.cycleThinkingLevel("backward"));
1514
1538
  this.defaultEditor.onAction("app.model.cycleForward", () => this.modelController.cycleModel("forward"));
1515
1539
  this.defaultEditor.onAction("app.model.cycleBackward", () => this.modelController.cycleModel("backward"));
1516
1540
  // Global debug handler on TUI (works regardless of focus)
@@ -1520,8 +1544,11 @@ export class InteractiveMode {
1520
1544
  this.defaultEditor.onAction("app.view.cycleForward", () => this.cycleToolOutputView("forward"));
1521
1545
  this.defaultEditor.onAction("app.view.cycleBackward", () => this.cycleToolOutputView("backward"));
1522
1546
  this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
1523
- this.defaultEditor.onAction("app.tasks.cycleView", () => {
1524
- 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");
1525
1552
  });
1526
1553
  this.defaultEditor.onAction("app.team.focus", () => this.teamFocus.enterFocus());
1527
1554
  this.defaultEditor.onAction("app.editor.external", () => this.openExternalEditor());
@@ -1542,7 +1569,8 @@ export class InteractiveMode {
1542
1569
  this.defaultEditor.onAction("app.session.color.cycleBackward", () => this.cycleSessionColor("backward"));
1543
1570
  this.defaultEditor.onAction("app.settings.open", () => this.showSettingsSelector());
1544
1571
  this.defaultEditor.onAction("app.hotkeys.open", () => this.commandExecutor.handleHotkeys());
1545
- this.defaultEditor.onAction("app.mode.cycle", () => void this.cycleAgentMode());
1572
+ this.defaultEditor.onAction("app.mode.cycleForward", () => void this.cycleAgentMode("forward"));
1573
+ this.defaultEditor.onAction("app.mode.cycleBackward", () => void this.cycleAgentMode("backward"));
1546
1574
  this.defaultEditor.onChange = (text) => {
1547
1575
  const wasBashMode = this.isBashMode;
1548
1576
  this.isBashMode = text.trimStart().startsWith("!");
@@ -2267,6 +2295,28 @@ export class InteractiveMode {
2267
2295
  * If multiple status messages are emitted back-to-back (without anything else being added to the chat),
2268
2296
  * we update the previous status line instead of appending new ones to avoid log spam.
2269
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
+ }
2270
2320
  showStatus(message) {
2271
2321
  const children = this.chatContainer.children;
2272
2322
  const last = children.length > 0 ? children[children.length - 1] : undefined;
@@ -2650,15 +2700,15 @@ export class InteractiveMode {
2650
2700
  }
2651
2701
  this.ui.requestRender();
2652
2702
  }
2653
- cycleThinkingLevel() {
2654
- const newLevel = this.session.cycleThinkingLevel();
2703
+ cycleThinkingLevel(direction) {
2704
+ const newLevel = this.session.cycleThinkingLevel(direction);
2655
2705
  if (newLevel === undefined) {
2656
2706
  this.showStatus("Current model does not support thinking");
2657
2707
  }
2658
2708
  else {
2659
2709
  this.footer.invalidate();
2660
2710
  this.updateEditorBorderColor();
2661
- this.showStatus(`Thinking level: ${newLevel}`);
2711
+ this.showDialStep("app.thinking.cycleBackward", `Thinking level: ${newLevel}`);
2662
2712
  }
2663
2713
  }
2664
2714
  /**
@@ -2834,7 +2884,7 @@ export class InteractiveMode {
2834
2884
  * is missing (a `--light` run strips the extension), the key says so instead
2835
2885
  * of guessing.
2836
2886
  */
2837
- async cycleAgentMode() {
2887
+ async cycleAgentMode(direction) {
2838
2888
  const command = this.session.extensionRunner.getCommand("mode");
2839
2889
  if (!command) {
2840
2890
  this.showWarning("Modes are not available in this session");
@@ -2847,8 +2897,12 @@ export class InteractiveMode {
2847
2897
  return;
2848
2898
  }
2849
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.
2850
2903
  const index = modes.indexOf(current);
2851
- const next = modes[(index + 1) % modes.length];
2904
+ const step = direction === "forward" ? 1 : -1;
2905
+ const next = modes[(index + step + modes.length) % modes.length];
2852
2906
  await this.session.prompt(`/mode ${next}`);
2853
2907
  }
2854
2908
  /**
@@ -2865,7 +2919,7 @@ export class InteractiveMode {
2865
2919
  const slot = cycleSessionColorSlot(this.sessionManager.getSessionColorSlot(), direction);
2866
2920
  this.session.setSessionColor(slot);
2867
2921
  const name = sessionColorName(slot);
2868
- this.showStatus(`Session color: ${name ?? slot}`);
2922
+ this.showDialStep("app.session.color.cycleBackward", `Session color: ${name ?? slot}`);
2869
2923
  }
2870
2924
  toggleThinkingBlockVisibility() {
2871
2925
  this.hideThinkingBlock = !this.hideThinkingBlock;