@tiens.nguyen/gonext-cli 1.0.371 → 1.0.372
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/gonext-repl.mjs +40 -15
- package/package.json +1 -1
package/gonext-repl.mjs
CHANGED
|
@@ -673,6 +673,18 @@ const tokenLineText = () => {
|
|
|
673
673
|
);
|
|
674
674
|
};
|
|
675
675
|
const FOOTER_ROWS = 3; // the rule row + the token row + the mode row
|
|
676
|
+
// The bar's rows as plain printable strings, one PHYSICAL terminal row each. Both text rows
|
|
677
|
+
// can exceed the width (the mode line is ~44 columns, the token row carries a model name) and
|
|
678
|
+
// would then WRAP onto an extra row — every consumer here counts rows with ESC[nA / ESC[nB, so
|
|
679
|
+
// a wrapped row corrupts the redraw (the #109 failure mode). Hence clipVisible on both.
|
|
680
|
+
const footerRows = () => {
|
|
681
|
+
const width = Math.max(20, process.stdout.columns || 80);
|
|
682
|
+
return [
|
|
683
|
+
dim("─".repeat(width)),
|
|
684
|
+
clipVisible(GUTTER + tokenLineText(), width - 1),
|
|
685
|
+
clipVisible(GUTTER + modeLineText(), width - 1),
|
|
686
|
+
];
|
|
687
|
+
};
|
|
676
688
|
// True only while the MAIN loop's ">> " prompt is waiting for input. The bar belongs to that
|
|
677
689
|
// prompt, so it must never appear under an ask()/askSecret() question or during a turn.
|
|
678
690
|
let footerActive = false;
|
|
@@ -690,18 +702,11 @@ const cursorPos = () =>
|
|
|
690
702
|
// matching up-move still lands on it.
|
|
691
703
|
function drawFooter() {
|
|
692
704
|
if (!footerActive || !process.stdout.isTTY || slashOverlayActive) return;
|
|
693
|
-
const width = Math.max(20, process.stdout.columns || 80);
|
|
694
705
|
const pos = cursorPos();
|
|
695
706
|
const down = Math.max(0, inputRows() - 1 - pos.rows); // caret row → last input row
|
|
696
707
|
process.stdout.write(
|
|
697
708
|
(down ? `\x1b[${down}B` : "") +
|
|
698
|
-
"\r\n\x1b[2K" +
|
|
699
|
-
// CLIPPED: both rows can exceed the width (the mode line is ~44 columns, the token row
|
|
700
|
-
// carries a model name) and would then WRAP onto an extra row — ESC[nA counts PHYSICAL
|
|
701
|
-
// rows, so the caret would come back short and the next keystroke would type over the
|
|
702
|
-
// bar (the #109 failure mode).
|
|
703
|
-
"\r\n\x1b[2K" + clipVisible(GUTTER + tokenLineText(), width - 1) +
|
|
704
|
-
"\r\n\x1b[2K" + clipVisible(GUTTER + modeLineText(), width - 1) +
|
|
709
|
+
footerRows().map((r) => "\r\n\x1b[2K" + r).join("") +
|
|
705
710
|
"\x1b[J" + // nothing stale below the bar
|
|
706
711
|
`\x1b[${FOOTER_ROWS + down}A\r` + // back to the caret's row
|
|
707
712
|
(pos.cols > 0 ? `\x1b[${pos.cols}C` : "")
|
|
@@ -1872,7 +1877,9 @@ async function runAgentTurn(history) {
|
|
|
1872
1877
|
let lastApprovalId = null; // the last command-approval request we've already answered
|
|
1873
1878
|
let carry = ""; // trailing partial content line held until its newline arrives
|
|
1874
1879
|
let statusShown = false; // a transient status line is currently on screen
|
|
1875
|
-
let statusLines = 2; //
|
|
1880
|
+
let statusLines = 2; // TOTAL rows the status block occupies, bottom bar included (clearStatus)
|
|
1881
|
+
// …of which these are the thought/word rows — the only ones hover + click hit-test against.
|
|
1882
|
+
let statusContentLines = 2;
|
|
1876
1883
|
let warnedPending = false;
|
|
1877
1884
|
// Transient poll failures (a network blip or a 5xx like the API's momentary "Worker
|
|
1878
1885
|
// key lookup failed." — a cold Mongo connection, NOT a bad key) must NOT kill a
|
|
@@ -1899,6 +1906,10 @@ async function runAgentTurn(history) {
|
|
|
1899
1906
|
// the lifetime per-workspace + global counters once the turn completes.
|
|
1900
1907
|
let latestOutputTokens = 0;
|
|
1901
1908
|
let outputTokensPosted = false; // guard: increment the lifetime counters once per turn
|
|
1909
|
+
// A new turn starts from zero on the bar's token row too — otherwise the previous turn's
|
|
1910
|
+
// in/out figures would sit there, looking live, until the first poll reports real numbers.
|
|
1911
|
+
lastTurnInputTokens = 0;
|
|
1912
|
+
lastTurnOutputTokens = 0;
|
|
1902
1913
|
// First few KB of the raw stream as consumed — a cheap fingerprint to verify the
|
|
1903
1914
|
// completed resultText really EXTENDS what we streamed (an old worker replaces it
|
|
1904
1915
|
// with the shorter bare answer, which must not be sliced by shownChars).
|
|
@@ -2020,11 +2031,16 @@ async function runAgentTurn(history) {
|
|
|
2020
2031
|
// the playful-word line.
|
|
2021
2032
|
const expLines = thoughtExpanded && rest ? wrapThought(rest, max, 4) : [];
|
|
2022
2033
|
const drawn = 2 + expLines.length;
|
|
2034
|
+
// The bottom bar rides along UNDER the live status while a turn runs, so it stays on screen
|
|
2035
|
+
// the whole time the agent is thinking instead of only at the prompt. It's part of the same
|
|
2036
|
+
// in-place block: the ticker redraws it every 120ms and clearStatus wipes it with the rest,
|
|
2037
|
+
// so content still lands on clean rows.
|
|
2038
|
+
const bar = process.stdout.isTTY ? footerRows() : [];
|
|
2023
2039
|
const rows = process.stdout.rows || 24;
|
|
2024
2040
|
// Hover-highlight only the THOUGHT line (line 1) — its exact viewport row comes from the
|
|
2025
2041
|
// \x1b[6n probe below (`thoughtRow`); fall back to the viewport-bottom assumption only when
|
|
2026
|
-
// the terminal never answered the probe.
|
|
2027
|
-
const tRow = thoughtRow >= 1 ? thoughtRow : rows - drawn + 1;
|
|
2042
|
+
// the terminal never answered the probe (the block's top = bottom minus ALL its rows).
|
|
2043
|
+
const tRow = thoughtRow >= 1 ? thoughtRow : rows - (drawn + bar.length) + 1;
|
|
2028
2044
|
const hovering = clickable && hoverRow === tRow;
|
|
2029
2045
|
clearStatus();
|
|
2030
2046
|
// Right after clearing, the cursor sits at the status block's TOP-LEFT = the thought line's
|
|
@@ -2042,8 +2058,10 @@ async function runAgentTurn(history) {
|
|
|
2042
2058
|
let out = `${markLine(green(BULLET))}${hovering ? hoverThought(label) : green(label)}`;
|
|
2043
2059
|
for (const wl of expLines) out += "\n" + GUTTER + dim(wl);
|
|
2044
2060
|
out += "\n" + `${markLine(color256(wc, glyph))}${color256(wc, `${thinkWord}…`)}${tokLabel}`;
|
|
2061
|
+
for (const br of bar) out += "\n" + br;
|
|
2045
2062
|
process.stdout.write(out);
|
|
2046
|
-
|
|
2063
|
+
statusContentLines = drawn;
|
|
2064
|
+
statusLines = drawn + bar.length;
|
|
2047
2065
|
statusShown = true;
|
|
2048
2066
|
};
|
|
2049
2067
|
// 120ms so the spinner animates smoothly (the frame math above uses the wall clock, so
|
|
@@ -2061,9 +2079,10 @@ async function runAgentTurn(history) {
|
|
|
2061
2079
|
if (!statusShown) return;
|
|
2062
2080
|
const rows = process.stdout.rows || 24;
|
|
2063
2081
|
const top = thoughtRow >= 1 ? thoughtRow : rows - statusLines + 1; // thought line row
|
|
2064
|
-
// Toggle only on the thought line + its grey expansion — NOT the
|
|
2065
|
-
//
|
|
2066
|
-
|
|
2082
|
+
// Toggle only on the thought line + its grey expansion — NOT the playful-word/token row,
|
|
2083
|
+
// and NOT the bottom bar below it (statusContentLines excludes the bar), so a click there
|
|
2084
|
+
// doesn't expand what it isn't part of.
|
|
2085
|
+
if (row < top || row > top + statusContentLines - 2) return;
|
|
2067
2086
|
// Only meaningful when there IS a fuller thought to reveal; otherwise ignore the click
|
|
2068
2087
|
// so we don't flicker an empty expand.
|
|
2069
2088
|
if (!thoughtExpanded && !(fullLiveThought && !runningCmd)) return;
|
|
@@ -2412,6 +2431,12 @@ async function runAgentTurn(history) {
|
|
|
2412
2431
|
if (Number.isFinite(job.agentOutputTokens) && job.agentOutputTokens > latestOutputTokens) {
|
|
2413
2432
|
latestOutputTokens = job.agentOutputTokens;
|
|
2414
2433
|
}
|
|
2434
|
+
// Mirror both into the bottom bar's token row so it counts UP live while the turn runs
|
|
2435
|
+
// (the bar rides under the status block now), instead of sitting on the last turn's
|
|
2436
|
+
// figures until this one finishes. The turn-end assignment writes the same numbers again.
|
|
2437
|
+
lastTurnInputTokens = latestCodeTokens;
|
|
2438
|
+
lastTurnOutputTokens = latestOutputTokens;
|
|
2439
|
+
lastTurnTotalOutput = globalOutputTokens + latestOutputTokens;
|
|
2415
2440
|
const text = typeof job.resultText === "string" ? job.resultText : "";
|
|
2416
2441
|
if (job.jobStatus === "completed") {
|
|
2417
2442
|
// Task #104: fold this turn's output-token total into the lifetime per-workspace +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiens.nguyen/gonext-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.372",
|
|
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",
|