@tiens.nguyen/gonext-cli 1.0.369 → 1.0.370

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 +38 -22
  2. package/package.json +1 -1
package/gonext-repl.mjs CHANGED
@@ -651,7 +651,28 @@ let editMode = "accept"; // "accept" | "manual"
651
651
  const modeLineText = () =>
652
652
  white("✎ " + (editMode === "accept" ? "accept edits on" : "manually edit")) +
653
653
  dim(" (shift+tab to switch)");
654
- const FOOTER_ROWS = 2; // the rule row + the mode row
654
+ // Token usage for the bar's middle row (task #104): the LAST turn's code-model input +
655
+ // output, then the coder's lifetime output total. Unlike the old per-turn print, this row is
656
+ // ALWAYS on screen — a turn that did no code-model work honestly reads "0", and the lifetime
657
+ // total is loaded at startup so the row is populated before the first turn.
658
+ let lastTurnInputTokens = 0;
659
+ let lastTurnOutputTokens = 0;
660
+ // This turn's lifetime total. `globalOutputTokens` is updated by an async POST that can lag a
661
+ // turn behind, so the bar takes whichever of the two is fresher (both are zeroed by a reset).
662
+ let lastTurnTotalOutput = 0;
663
+ const tokenLineText = () => {
664
+ const coder = sessionCodingLabel || defaultCodingModel;
665
+ return (
666
+ dim("tokens · ↑ in ") +
667
+ fmtTokens(lastTurnInputTokens) +
668
+ dim(" · ") +
669
+ cyan(`↓ out ${fmtTokens(lastTurnOutputTokens)}`) +
670
+ dim(" · ") +
671
+ dim(`${coder ? coder + " " : ""}total `) +
672
+ cyan(`↓ ${fmtTokens(Math.max(globalOutputTokens, lastTurnTotalOutput))}`)
673
+ );
674
+ };
675
+ const FOOTER_ROWS = 3; // the rule row + the token row + the mode row
655
676
  // True only while the MAIN loop's ">> " prompt is waiting for input. The bar belongs to that
656
677
  // prompt, so it must never appear under an ask()/askSecret() question or during a turn.
657
678
  let footerActive = false;
@@ -675,9 +696,11 @@ function drawFooter() {
675
696
  process.stdout.write(
676
697
  (down ? `\x1b[${down}B` : "") +
677
698
  "\r\n\x1b[2K" + dim("─".repeat(width)) +
678
- // CLIPPED: the mode line is ~44 columns, so on a narrow terminal it would WRAP onto a
679
- // third row and ESC[nA counts PHYSICAL rows, so the caret would come back one row
680
- // short and the next keystroke would type over the rule (the #109 failure mode).
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) +
681
704
  "\r\n\x1b[2K" + clipVisible(GUTTER + modeLineText(), width - 1) +
682
705
  "\x1b[J" + // nothing stale below the bar
683
706
  `\x1b[${FOOTER_ROWS + down}A\r` + // back to the caret's row
@@ -2842,6 +2865,9 @@ async function main() {
2842
2865
  const done = await resetGlobalOutputTokens();
2843
2866
  if (done) {
2844
2867
  globalOutputTokens = 0;
2868
+ // The bar takes the FRESHER of the two totals, so the last turn's copy has to be
2869
+ // zeroed too or the reset wouldn't show up in it.
2870
+ lastTurnTotalOutput = 0;
2845
2871
  console.log(GUTTER + green("✓ global + workspace output-token totals reset to 0.\n"));
2846
2872
  } else {
2847
2873
  console.log(GUTTER + dim("Could not reset the output-token totals (API error).\n"));
@@ -2887,6 +2913,14 @@ async function main() {
2887
2913
  try {
2888
2914
  const { text: answer, shown, cancelled, inputTokens, outputTokens, turnOutputTokens } =
2889
2915
  await runAgentTurn(history);
2916
+ // Feed the bottom bar's token row (task #104). Assigned for EVERY turn — including one
2917
+ // that ended without an answer — so the row never shows a stale count from an older
2918
+ // turn. It replaces the print-once-per-turn footer that used to live here: the bar is
2919
+ // always on screen, so printing the same three figures into the scrollback right above
2920
+ // it just read as a duplicate.
2921
+ lastTurnInputTokens = inputTokens ?? 0;
2922
+ lastTurnOutputTokens = turnOutputTokens ?? 0;
2923
+ lastTurnTotalOutput = outputTokens ?? 0;
2890
2924
  if (answer) {
2891
2925
  history.push({ role: "assistant", content: answer });
2892
2926
  // Persist after every successful turn (not just on clean exit) — an ungraceful
@@ -2902,24 +2936,6 @@ async function main() {
2902
2936
  // renderAnswer also WRAPS long lines and re-indents each continuation row, so a wrapped
2903
2937
  // paragraph keeps the same left margin instead of soft-wrapping back to column 0.
2904
2938
  console.log(shown ? "" : `${LINE_SPACING}${renderAnswer(answer)}\n`);
2905
- // Task #104: persistent token footer at the bottom of the screen — this turn's
2906
- // code-model INPUT + this turn's OUTPUT (↓ out), then the model's GLOBAL lifetime
2907
- // output total (all turns for this user + coding server), labelled with the model
2908
- // name so it's clear which coder that running total belongs to. Guard on THIS turn's
2909
- // activity so a plain-reply greeting that did no code-model work shows nothing (#112).
2910
- if ((inputTokens ?? 0) > 0 || (turnOutputTokens ?? 0) > 0) {
2911
- const coder = sessionCodingLabel || defaultCodingModel;
2912
- console.log(
2913
- GUTTER +
2914
- dim("tokens · ↑ in ") +
2915
- fmtTokens(inputTokens ?? 0) +
2916
- dim(" · ") +
2917
- cyan(`↓ out ${fmtTokens(turnOutputTokens ?? 0)}`) +
2918
- dim(" · ") +
2919
- dim(`${coder ? coder + " " : ""}total `) +
2920
- cyan(`↓ ${fmtTokens(outputTokens ?? 0)}`)
2921
- );
2922
- }
2923
2939
  } else {
2924
2940
  history.pop(); // aborted/failed/cancelled turn — don't poison the next request
2925
2941
  // A turn that ended WITHOUT an answer (Ctrl+C cancel, or an abort/failure) must not
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-cli",
3
- "version": "1.0.369",
3
+ "version": "1.0.370",
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",