@tiens.nguyen/gonext-cli 1.0.357 → 1.0.359

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 (2) hide show
  1. package/gonext-repl.mjs +51 -14
  2. package/package.json +1 -1
package/gonext-repl.mjs CHANGED
@@ -58,15 +58,41 @@ const white = (s) => `\x1b[37m${s}\x1b[0m`;
58
58
  // Left gutter so terminal output doesn't hug the edge (task #128, Phase 1). A terminal has
59
59
  // no CSS padding — a "left margin" is just a space prefix on every printed line, applied
60
60
  // consistently at the prompt, the ● bullets, plain replies, the banner and the live status
61
- // so they share ONE clean left edge. Existing secondary indents already use 2 spaces, so a
62
- // 2-space gutter converges everything to the same column. `gutterLines` insets a multi-line
63
- // block (blank lines left bare — no trailing whitespace).
64
- const GUTTER = " ";
61
+ // so they share ONE clean left edge. 3 spaces reads as a deliberate margin — 2 was too subtle
62
+ // against the window edge (the padding fix). `gutterLines` insets a multi-line block (blank
63
+ // lines left bare — no trailing whitespace).
64
+ const GUTTER = " ";
65
65
  const gutterLines = (s) =>
66
66
  String(s)
67
67
  .split("\n")
68
68
  .map((l) => (l.length ? GUTTER + l : l))
69
69
  .join("\n");
70
+ // The ● action bullets and the final agent answer are ONE block of agent output, so they share
71
+ // one left edge: the bullet's TEXT column (GUTTER + "● " = GUTTER + 2). Without this the answer
72
+ // sat at the bare GUTTER, 2 cols left of the bullet text above it — the ragged jog between the
73
+ // actions and "Done!" (padding fix). Plain replies have no bullets, so they keep the GUTTER
74
+ // margin (aligned with the >> prompt). `outputLines` insets a block to that shared edge.
75
+ const OUTPUT_INDENT = GUTTER + " ";
76
+ const outputLines = (s) =>
77
+ String(s)
78
+ .split("\n")
79
+ .map((l) => (l.length ? OUTPUT_INDENT + l : l))
80
+ .join("\n");
81
+ // Render the model's inline **bold** as ANSI bold (markdown polish). The terminal has no
82
+ // markdown renderer, so a heading like `**What I did:**` was printing its literal asterisks.
83
+ // Only paired ** with non-space content between them is consumed (so a stray `**` or `a ** b`
84
+ // is left untouched); `.` never crosses a newline, so a bold span stays on its own line. Ends
85
+ // with \x1b[22m (bold-off) rather than a full \x1b[0m reset so any surrounding colour/gutter
86
+ // styling survives. Applied to the completed agent answer (a whole string) — the live plain-
87
+ // reply stream isn't marked up because ** can straddle a chunk boundary there.
88
+ const renderMdBold = (s) =>
89
+ String(s).replace(/\*\*(?=\S)(.*?\S)\*\*/g, "\x1b[1m$1\x1b[22m");
90
+ // Vertical rhythm between ● action/answer bullets (task #129). One blank line so the list
91
+ // breathes instead of being cramped. A knob: "" = the old tight list, "\n" = one blank.
92
+ // Applied as a LEADING blank before a bullet when the previous line wasn't already blank —
93
+ // so blanks land BETWEEN bullets, never trailing, and it's driven by `lastWasBlank` (what
94
+ // was actually printed), NOT the status ticker, so it can't reintroduce the #115 race.
95
+ const LINE_SPACING = "\n";
70
96
  // ANSI 90 ("bright black") — a reliable neutral grey across terminal themes, unlike
71
97
  // code 34 (blue) which many dark-theme palettes render as purple/indigo instead.
72
98
  const codeColor = (s) => `\x1b[90m${s}\x1b[0m`;
@@ -1808,12 +1834,13 @@ async function runAgentTurn(history) {
1808
1834
  // GREY expanded lines (if any), then LAST the SPINNER + playful word + token labels
1809
1835
  // (task #83/#84), e.g. " ⠙ Caffeinating… · 12.3k tok · 8.1k max".
1810
1836
  // Gutter the live status block's first line (task #128) so the blinking ● lines up with
1811
- // the finished ● bullets during a turn (no left-edge jump). The sub-lines below already
1812
- // use a 2-space indent = the gutter. Safe for the redraw: clearStatus clears whole lines
1813
- // and the hover/click hit-testing is ROW-based, so a horizontal offset changes nothing.
1837
+ // the finished ● bullets during a turn (no left-edge jump). The sub-lines below use the
1838
+ // same GUTTER so the whole block shares one left edge. Safe for the redraw: clearStatus
1839
+ // clears whole lines and the hover/click hit-testing is ROW-based, so a horizontal offset
1840
+ // changes nothing.
1814
1841
  let out = `${GUTTER}${green(BULLET)} ${hovering ? hoverThought(label) : green(label)}`;
1815
- for (const wl of expLines) out += "\n " + dim(wl);
1816
- out += "\n" + ` ${color256(wc, `${glyph} ${thinkWord}…`)}${tokLabel}`;
1842
+ for (const wl of expLines) out += "\n" + GUTTER + dim(wl);
1843
+ out += "\n" + `${GUTTER}${color256(wc, `${glyph} ${thinkWord}…`)}${tokLabel}`;
1817
1844
  process.stdout.write(out);
1818
1845
  statusLines = drawn;
1819
1846
  statusShown = true;
@@ -2067,7 +2094,12 @@ async function runAgentTurn(history) {
2067
2094
  } else {
2068
2095
  // The remaining out-of-fence ACTION events ("Command passed → …", "Searching code
2069
2096
  // → …", "Server up → …", "Composing answer…") each print as ONE solid bullet —
2070
- // the clean, summarized action list (task #41).
2097
+ // the clean, summarized action list (task #41), now with a blank line between them
2098
+ // for breathing room (task #129). The spacer is a LEADING blank emitted only when
2099
+ // the previous line wasn't already blank → one gap BETWEEN bullets, none before the
2100
+ // first (which follows the prompt) and none trailing. Deterministic (keyed off
2101
+ // lastWasBlank, not the ticker) so #115's status-race stays fixed.
2102
+ if (LINE_SPACING && !lastWasBlank) process.stdout.write(LINE_SPACING);
2071
2103
  process.stdout.write(GUTTER + white(BULLET) + " " + line.trim() + "\n");
2072
2104
  }
2073
2105
  lastWasBlank = false;
@@ -2636,9 +2668,13 @@ async function main() {
2636
2668
  // termination (killed terminal, force-quit) would otherwise lose the whole
2637
2669
  // session even though most of it completed fine.
2638
2670
  await saveSession(cwd, history);
2639
- // A plain-reply answer already streamed live in the loop above — printing it
2640
- // again here would just duplicate it; a blank line is enough for spacing.
2641
- console.log(shown ? "" : `\n\n${gutterLines(answer)}\n`);
2671
+ // A plain-reply answer already streamed live in the loop above (shown=true) — printing
2672
+ // it again here would just duplicate it, so only a spacer blank line. The AGENT answer
2673
+ // (shown=false) is printed here from resultText, indented to the bullet-text column
2674
+ // (outputLines) so it shares the ● bullets' left edge, and with ONE leading blank
2675
+ // (LINE_SPACING) matching the between-bullets rhythm — was a hard "\n\n" double gap at
2676
+ // the seam, bigger than the gaps between bullets above it (the seam padding fix).
2677
+ console.log(shown ? "" : `${LINE_SPACING}${outputLines(renderMdBold(answer))}\n`);
2642
2678
  // Task #104: persistent token footer at the bottom of the screen — this turn's
2643
2679
  // code-model INPUT + this turn's OUTPUT (↓ out), then the model's GLOBAL lifetime
2644
2680
  // output total (all turns for this user + coding server), labelled with the model
@@ -2647,7 +2683,8 @@ async function main() {
2647
2683
  if ((inputTokens ?? 0) > 0 || (turnOutputTokens ?? 0) > 0) {
2648
2684
  const coder = sessionCodingLabel || defaultCodingModel;
2649
2685
  console.log(
2650
- dim("tokens · ↑ in ") +
2686
+ GUTTER +
2687
+ dim("tokens · ↑ in ") +
2651
2688
  fmtTokens(inputTokens ?? 0) +
2652
2689
  dim(" · ") +
2653
2690
  cyan(`↓ out ${fmtTokens(turnOutputTokens ?? 0)}`) +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-cli",
3
- "version": "1.0.357",
3
+ "version": "1.0.359",
4
4
  "description": "GoNext CLI — the gonext terminal plus the local worker that runs agent / OCR / PDF / embedding jobs on your Mac (Ollama / MLX / OpenAI-compatible).",
5
5
  "type": "module",
6
6
  "license": "MIT",