@tiens.nguyen/gonext-cli 1.0.367 → 1.0.368

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 +39 -8
  2. package/package.json +1 -1
package/gonext-repl.mjs CHANGED
@@ -21,7 +21,7 @@
21
21
  * interrupted investigation actually resumes it). /reset clears it.
22
22
  *
23
23
  * Requires the worker daemon to be running (it claims the job).
24
- * Commands: /exit /model /reset /revert [runId] /help Ctrl-C stops following the current run.
24
+ * Commands: /exit /model /reset /manual-edit [runId] /help Ctrl-C stops following the current run.
25
25
  */
26
26
  import { readFile, mkdir, writeFile, unlink } from "node:fs/promises";
27
27
  import { existsSync, statSync, readFileSync } from "node:fs";
@@ -286,7 +286,7 @@ if (argv.includes("--help") || argv.includes("-h")) {
286
286
  " -v, --verbose show the agent's diagnostic logs ([gonext-agent] …) and python stderr\n" +
287
287
  " -h, --help this help\n\n" +
288
288
  "Auth comes from ~/.gonext/worker.env; models/budgets/RAG come from your web Settings.\n" +
289
- "Inside the REPL: /exit · /model · /reset · /revert [runId] · /help — Ctrl-C stops following a turn.\n" +
289
+ "Inside the REPL: /exit · /model · /reset · /manual-edit [runId] · /help — Ctrl-C stops following a turn.\n" +
290
290
  "Questions are enqueued like web questions and executed by the RUNNING\n" +
291
291
  "gonext-cli daemon — its terminal shows the full [gonext-agent] logs.\n" +
292
292
  "Conversation history is saved per-folder in ~/.gonext/sessions and resumed next time\n" +
@@ -360,7 +360,7 @@ const COMMANDS = [
360
360
  { name: "/test-auto", desc: "toggle auto-test (verify & fix until it passes) for this folder" },
361
361
  { name: "/rag-local", desc: "toggle storing this folder's RAG knowledge base locally (vs cloud/S3)" },
362
362
  { name: "/server", desc: "pick a deployment server (host/user) for this session" },
363
- { name: "/revert", usage: "/revert [runId]", desc: "undo the agent's file edits (latest run)" },
363
+ { name: "/manual-edit", aliases: ["/revert"], usage: "/manual-edit [runId]", desc: "undo the agent's file edits so you can edit them yourself (latest run)" },
364
364
  { name: "/reset", aliases: ["/new"], desc: "forget this folder's saved conversation and start fresh" },
365
365
  { name: "/output-token", desc: "show the agent code-model OUTPUT-token total for this workspace" },
366
366
  { name: "/reset-output-token-global", desc: "reset the GLOBAL (you + coding server) output-token total to 0" },
@@ -641,6 +641,26 @@ if (process.stdout.isTTY) {
641
641
  }
642
642
  });
643
643
  }
644
+ // ---------- edit-mode bottom bar (display-only + Shift+Tab to cycle) ----------
645
+ // A split line + a mode indicator shown just above the prompt each turn: "accept edits on"
646
+ // (the agent applies file edits — today's behaviour) vs "manually edit". DISPLAY-ONLY for now
647
+ // — it tracks the mode so the bar reflects it and Shift+Tab cycles it; wiring it to actually
648
+ // gate the agent's edits is a deliberate follow-up.
649
+ let editMode = "accept"; // "accept" | "manual"
650
+ const modeLineText = () =>
651
+ white("✎ " + (editMode === "accept" ? "accept edits on" : "manually edit")) +
652
+ dim(" (shift+tab to switch)");
653
+ // The bar: a full-width dim rule (turn separator), then the gutter-inset mode line. Printed
654
+ // above the prompt in the main loop.
655
+ const modeBar = () =>
656
+ dim("─".repeat(process.stdout.columns || 80)) + "\n" + GUTTER + modeLineText();
657
+ // Shift+Tab redraw: the mode line sits ONE physical row above the ">> " input line. Move up,
658
+ // rewrite it, drop back, and let readline restore the input row + caret (cursor-relative, like
659
+ // the status ticker / pickers, so it survives scrolling).
660
+ const redrawModeLine = () => {
661
+ process.stdout.write("\x1b[1A\r\x1b[2K" + GUTTER + modeLineText() + "\x1b[1B\r");
662
+ rl._refreshLine();
663
+ };
644
664
  if (process.stdin.isTTY) {
645
665
  process.stdin.on("keypress", (_ch, key) => {
646
666
  // An arrow-key list picker (/server) is up → ↑/↓ move the highlight, Enter chooses.
@@ -665,6 +685,15 @@ if (process.stdin.isTTY) {
665
685
  rl.historyIndex = -1;
666
686
  return;
667
687
  }
688
+ // Shift+Tab cycles the edit mode shown in the bottom bar (display-only). Skip while the
689
+ // slash overlay owns the screen so we don't fight its redraw.
690
+ if (key && key.name === "tab" && key.shift) {
691
+ if (!slashOverlayActive) {
692
+ editMode = editMode === "accept" ? "manual" : "accept";
693
+ redrawModeLine();
694
+ }
695
+ return;
696
+ }
668
697
  // Enter is handled by rl.on("line") — don't redraw the hint on the way out.
669
698
  if (key && (key.name === "return" || key.name === "enter")) return;
670
699
  // ↑/↓ while the menu is up = move the highlight IN PLACE. readline drawing is
@@ -2525,14 +2554,13 @@ async function main() {
2525
2554
  bold(cyan("GONEXT AGENT")) + dim(` v${REPL_VERSION}`),
2526
2555
  lbl("model") + (p.agentModelId || probe?.modelKey || "?"),
2527
2556
  lbl("coder") + (p.codingModelId || dim("—")),
2528
- lbl("RAG") +
2529
- (p.ragEnabled ? (sessionRagMode === "cloud" ? "cloud" : "local") : dim("off")) +
2530
- dim(" auto-test ") + (sessionTestAuto ? "on" : dim("off")),
2557
+ lbl("RAG") + (p.ragEnabled ? (sessionRagMode === "cloud" ? "cloud" : "local") : dim("off")),
2558
+ sessionTestAuto ? green("auto-test on") : dim("auto-test off"),
2531
2559
  lbl("folder") + ws + dim(wsSync ? ` (${wsSync.run ? "run" : "read-only"}${wsSync.enabled ? " · sync" : ""})` : ""),
2532
2560
  ];
2533
2561
  if (selectedServer)
2534
2562
  statusRows.push(lbl("deploy") + `${selectedServer.name} ${dim(`(${selectedServer.user}@${selectedServer.host})`)}`);
2535
- statusRows.push(dim(`api ${apiBase.replace(/^https?:\/\//, "")} · key ${workerKey.slice(0, 8)}…`));
2563
+ statusRows.push(dim(`key ${workerKey.slice(0, 8)}…`));
2536
2564
  console.log(renderBanner(statusRows));
2537
2565
  }
2538
2566
  // Task #127, Phase 3: if the base chat model server isn't answering, offer to set it up
@@ -2655,6 +2683,9 @@ async function main() {
2655
2683
  let lastCancelledTask = "";
2656
2684
 
2657
2685
  for (;;) {
2686
+ // The bottom bar (split line + edit-mode indicator) sits just above the prompt each turn;
2687
+ // the mode line is exactly ONE row above ">> " so Shift+Tab can redraw it in place.
2688
+ if (process.stdout.isTTY) process.stdout.write(modeBar() + "\n");
2658
2689
  rl.prompt();
2659
2690
  // Absorb blank Enter-presses SILENTLY under the same prompt — without this, lines
2660
2691
  // typed while the agent was busy on a long turn (queued, not yet read) all drain at
@@ -2751,7 +2782,7 @@ async function main() {
2751
2782
  }
2752
2783
  continue;
2753
2784
  }
2754
- if (line.startsWith("/revert")) {
2785
+ if (line.startsWith("/manual-edit") || line.startsWith("/revert")) {
2755
2786
  const runId = line.split(/\s+/)[1] ?? "";
2756
2787
  await new Promise((res) => {
2757
2788
  const p = spawn(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gonext-cli",
3
- "version": "1.0.367",
3
+ "version": "1.0.368",
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",