@tiens.nguyen/gu-cli 1.0.808 → 1.0.810

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/gu-repl.mjs CHANGED
@@ -142,7 +142,7 @@ import { unapprovedProjectServers } from "./mcp-command.mjs";
142
142
  import { readHooks } from "./hooks-config.mjs";
143
143
  import { readSkillSource, sourceLabel, SKILL_SOURCE_KEY } from "./skill-source.mjs";
144
144
  import { codingBannerLabel, isOllamaCloudUrl, SECRET_KEY_HINT } from "./coding-key-prompt.mjs";
145
- import { questionEchoText, thoughtPrintSeq } from "./terminal-layout.mjs";
145
+ import { questionEchoText, thoughtPrintSeq, wrapDiffText } from "./terminal-layout.mjs";
146
146
  import { globalPrefixOf } from "./install-path.mjs";
147
147
  import { firstGuOnPath, pathGuMismatch, guOnPathIsCurrent, updateAttemptRecord, repeatUpdateNotice } from "./update-verify.mjs";
148
148
  import { embedNotice, embedState } from "./embed-model-command.mjs";
@@ -917,16 +917,14 @@ const COMMANDS = [
917
917
  // outside, it is confused". The command still works and still does the same thing.
918
918
  { name: "/agent-model", desc: "switch the AGENT model (the one that talks) — saved on this machine" },
919
919
  { name: "/embed-model", desc: "switch the RAG EMBEDDING model (nomic-embed-text / qwen3-embedding) and its server — re-indexes what is already indexed; supports a remote URL + API key" },
920
- { name: "/test-auto", desc: "toggle auto-test (verify & fix until it passes) for this folder" },
921
- // ONE command, three answers. /rag-local and /rag-cloud still work — they are in people's
922
- // fingers — but they are HIDDEN_COMMANDS now, because offering three doors into one setting
923
- // is what made this confusing in the first place.
924
- { name: "/rag", desc: "where this folder's RAG knowledge base lives: local, cloud, or both" },
920
+ // /test-auto is HIDDEN_COMMANDS now — the handler still works when typed, it is just not offered.
921
+ // /rag, /rag-local and /rag-cloud all still work — they are in people's fingers — but they are
922
+ // HIDDEN_COMMANDS now, so /help and Tab-completion do not offer them.
925
923
  // The two synonyms were dispatched but declared nowhere (found by slash-command-wiring.test.mjs):
926
924
  // they worked, Tab-completed to nothing, and appeared in no help. Aliases is what the registry
927
925
  // already has for exactly this — /quit, /rag-local.
928
926
  { name: "/host-mode", desc: "undo client mode — run agent turns on THIS machine again" },
929
- { name: "/undo", usage: "/undo [runId]", desc: "put back the files the last stopped turn changed (from its backups) — asks first" },
927
+ // /undo is HIDDEN_COMMANDS now — still works when typed.
930
928
  { name: "/server", desc: "pick a deployment server (host/user) for this session" },
931
929
  { name: "/proxy", usage: "/proxy [show|set|on|off|clear|test]", desc: "route MODEL calls through an HTTP proxy (local calls always go direct)" },
932
930
  { name: "/s3", usage: "/s3 [test]", desc: "set up YOUR S3 bucket (RAG index, and the PDFs/downloads gu makes) and check the AWS keys work — without it, files are saved on this computer" },
@@ -12053,7 +12051,14 @@ async function runAgentTurn(history, { attachJobId = "", ownJob = false } = {})
12053
12051
  if (m) {
12054
12052
  // " 19 - old text" → dim line number, red/cyan signed content.
12055
12053
  const paintDiff = m[3] === "+" ? accent : red;
12056
- process.stdout.write(`${GUTTER}${m[1]}${dim(m[2])} ${paintDiff(`${m[3]} ${m[4]}`)}\n`);
12054
+ // Wrap to the terminal width: continuation rows hang under the text column
12055
+ // (no line number, blank sign) instead of soft-wrapping back to column 0.
12056
+ const room = Math.max(8, Math.max(20, process.stdout.columns || 80) - 1 - visibleLen(GUTTER) - m[1].length - m[2].length - 1 - 2);
12057
+ const pieces = wrapDiffText(m[4], room);
12058
+ process.stdout.write(`${GUTTER}${m[1]}${dim(m[2])} ${paintDiff(`${m[3]} ${pieces[0]}`)}\n`);
12059
+ for (const p of pieces.slice(1)) {
12060
+ process.stdout.write(`${GUTTER}${m[1]}${" ".repeat(m[2].length)} ${paintDiff(` ${p}`)}\n`);
12061
+ }
12057
12062
  } else {
12058
12063
  process.stdout.write(GUTTER + dim(line) + "\n"); // "└ summary" / "… (+N more)"
12059
12064
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gu-cli",
3
- "version": "1.0.808",
3
+ "version": "1.0.810",
4
4
  "description": "Gu CLI — the gu 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",
@@ -66,6 +66,45 @@ export function clipVisible(s, max) {
66
66
  return out;
67
67
  }
68
68
 
69
+ /**
70
+ * Wrap the TEXT of one edit-card diff row so no piece is wider than `room` visible columns
71
+ * (task #24). A raw over-wide row is soft-wrapped by the terminal back to column 0, which
72
+ * destroys the card's indent. Prefers breaking after a space; a token longer than a whole
73
+ * row is hard-split. The caller paints the first piece after the line number and every
74
+ * further piece under the text column (hanging indent). Leading code indentation is kept.
75
+ */
76
+ export function wrapDiffText(text, room) {
77
+ const max = Math.max(8, Math.floor(room) || 8);
78
+ const chars = [...String(text ?? "").replace(/\t/g, " ")];
79
+ const rows = [];
80
+ let cur = [];
81
+ let w = 0;
82
+ let lastSpace = -1;
83
+ for (const ch of chars) {
84
+ const cw = charWidth(ch);
85
+ if (w + cw > max) {
86
+ const canBreak = lastSpace > 0 && ch !== " " && cur.slice(0, lastSpace).some((c) => c !== " ");
87
+ if (canBreak) {
88
+ const tail = cur.slice(lastSpace + 1);
89
+ rows.push(cur.slice(0, lastSpace).join("").replace(/\s+$/, ""));
90
+ cur = tail;
91
+ w = tail.reduce((n, c) => n + charWidth(c), 0);
92
+ } else {
93
+ rows.push(cur.join(""));
94
+ cur = [];
95
+ w = 0;
96
+ }
97
+ lastSpace = -1;
98
+ if (ch === " ") continue; // a space that lands on the break is not carried to the next row
99
+ }
100
+ cur.push(ch);
101
+ w += cw;
102
+ if (ch === " ") lastSpace = cur.length - 1;
103
+ }
104
+ rows.push(cur.join(""));
105
+ return rows;
106
+ }
107
+
69
108
  /**
70
109
  * The prompt breakdown as rows to print (task #142) — WHERE the coding model's context went.
71
110
  *
@@ -2296,6 +2335,9 @@ export const HIDDEN_COMMANDS = [
2296
2335
  // too offered two doors into one setting (reported as confusing)
2297
2336
  "/rag-local", // superseded by /rag's picker; still work, still mean what
2298
2337
  "/rag-cloud", // they say — unlisted so /help offers one way into the setting
2338
+ "/rag", // hidden too (user request, #25): handler unchanged, not offered
2339
+ "/test-auto", // hidden, still toggles auto-test when typed
2340
+ "/undo", // hidden, still restores the last stopped turn when typed
2299
2341
  "/thought", // the reasoning display stays ON; only the toggle is unlisted
2300
2342
  "/hover", // in-turn mouse capture — behaviour unchanged, just hidden
2301
2343
  "/reset", // the old name of /new (#278) — still works, one name is offered