@tiens.nguyen/gu-cli 1.0.754 → 1.0.756

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/cli-token.mjs CHANGED
@@ -289,15 +289,18 @@ export function barNotice({ token, publicKeyB64, mode, now = new Date(), attempt
289
289
  if (check.ok) {
290
290
  const p = check.payload;
291
291
  const ended = p.paidUntil && Date.parse(p.paidUntil) <= now.getTime();
292
- if (ended) return { kind: "plan-ended", text: `Your gu Pro plan ended on ${shortDate(p.paidUntil)} — renew at ${pricingUrl}` };
292
+ // SHORT AND CALM (#292: "make it simpler … seems so serious"): the fact, then the one thing to
293
+ // do. `urgent` is what the bar colours by — only these two need the user to act.
294
+ if (ended) return { kind: "plan-ended", urgent: true, text: `Pro ended ${shortDate(p.paidUntil)} · renew at ${pricingUrl}` };
293
295
  if (Number.isInteger(p.activeDevices) && p.activeDevices > p.deviceLimit) {
294
296
  return {
295
297
  kind: "over-limit",
298
+ urgent: true,
296
299
  // PLAIN WORDS (asked: "make it simple to understand"). It read "gu is active on 4 devices —
297
300
  // Free covers 1", which leaves the reader to work out that "active" means "used gu in the
298
301
  // last 30 days" and "covers" means "the plan allows". Kept short: the bar drops it entirely
299
302
  // when it does not fit the terminal's width.
300
- text: `${planName(p.plan)} allows ${computers(p.deviceLimit)} — you've used gu on ${p.activeDevices} this month`,
303
+ text: `${planName(p.plan)}: ${computers(p.deviceLimit)} · used on ${p.activeDevices} this month`,
301
304
  };
302
305
  }
303
306
  const age = now.getTime() - Date.parse(p.issuedAt);
@@ -306,9 +309,10 @@ export function barNotice({ token, publicKeyB64, mode, now = new Date(), attempt
306
309
  return null; // Client mode fetches one by itself; do not complain before it has tried.
307
310
  }
308
311
 
312
+ // ROUTINE, so not an alarm (#292): a token getting old only means updates will stop one day.
309
313
  return mode === "expert"
310
- ? { kind: "stale", text: `Get a fresh gu token at ${accountUrl} to keep getting updates` }
311
- : { kind: "stale", text: "Sign in to keep getting gu updates — run gu login" };
314
+ ? { kind: "stale", urgent: false, text: `gu token is old · get a new one at ${accountUrl}` }
315
+ : { kind: "stale", urgent: false, text: "gu token is old · run gu login" };
312
316
  }
313
317
 
314
318
  /** "Free plan" / "Pro plan" — the name a person uses. */
@@ -12950,8 +12950,6 @@ def run_agent_chat(cfg):
12950
12950
  task_with_hint = _label_failed_images(task_with_hint, _img_failed)
12951
12951
  _log(f"attached {len(_turn_images)} image(s) to the question"
12952
12952
  + (f", {len(_img_failed)} could not be opened" if _img_failed else ""))
12953
- if _turn_images:
12954
- _emit({"type": "step", "text": f"Sending {len(_turn_images)} image(s) with the question"})
12955
12953
  for _lbl, _why in _img_failed:
12956
12954
  _emit({"type": "step", "text": f"⚠ {_lbl} not sent — {_why}"})
12957
12955
  else:
package/gu-repl.mjs CHANGED
@@ -128,7 +128,7 @@ import { unapprovedProjectServers } from "./mcp-command.mjs";
128
128
  import { readHooks } from "./hooks-config.mjs";
129
129
  import { codingBannerLabel, isOllamaCloudUrl, SECRET_KEY_HINT } from "./coding-key-prompt.mjs";
130
130
  import { codingChoiceTags, coderDisplayName, coderWhere, whereOfActiveCoder } from "./coding-choice-tags.mjs";
131
- import { IMAGE_NEEDS_CLAUDE, clipboardImageToFile, fitImageNotice, createImageStore, imagePasteKey, imagePathFromPaste, isClaudeCoderUrl, isImagePasteKey, storeImageFile } from "./image-paste.mjs";
131
+ import { IMAGE_NEEDS_CLAUDE, clipboardImageToFile, fitImageNotice, imageMarkersForDisplay, createImageStore, imagePasteKey, imagePathFromPaste, isClaudeCoderUrl, isImagePasteKey, storeImageFile } from "./image-paste.mjs";
132
132
  import {
133
133
  isDoubleClick,
134
134
  clickRun,
@@ -2474,7 +2474,10 @@ function frozenZone() {
2474
2474
  // it whole → the condition above lets the agents count (or nothing) have the row instead.
2475
2475
  const len = visibleLen(tokenNotice.text);
2476
2476
  const from = width - 1 - len;
2477
- rows[sep] = clipVisible(dim("\u2500".repeat(Math.max(0, from - 1)) + " ") + yellow(tokenNotice.text), width - 1);
2477
+ // Yellow only when the user must act (plan ended, over the computer limit); an old token is
2478
+ // routine and reads in the rule's own dim grey (#292 — the yellow line "seems so serious").
2479
+ const paint = tokenNotice.urgent ? yellow : dim;
2480
+ rows[sep] = clipVisible(dim("\u2500".repeat(Math.max(0, from - 1)) + " ") + paint(tokenNotice.text), width - 1);
2478
2481
  } else if (agentsIndicator(otherAgents)) {
2479
2482
  // …and when there is no update to offer, the same row carries the agents count (task #195).
2480
2483
  // THREE THINGS CANNOT SHARE ONE ROW, so they are ranked by how time-critical they are:
@@ -11423,7 +11426,8 @@ async function main() {
11423
11426
  // against the ">>" row. Unlike the live block's separator, this one belongs to the
11424
11427
  // TRANSCRIPT: the echo is written exactly once per submitted line and never repainted,
11425
11428
  // so there is nothing here for a blank to accumulate against.
11426
- process.stdout.write(PROMPT_PAST + line + "\n\n");
11429
+ // As typed, not as sent: "[Image #1]", never the file path the agent needs (#51).
11430
+ process.stdout.write(PROMPT_PAST + imageMarkersForDisplay(line) + "\n\n");
11427
11431
  }
11428
11432
  // ESC AT ANY QUESTION LANDS HERE (see PromptCancelled). Every command below is wrapped as
11429
11433
  // one block rather than each flow catching for itself, because the request was to be able to
package/image-paste.mjs CHANGED
@@ -214,6 +214,15 @@ export function clipboardImageToFile({ home = homedir(), platform = process.plat
214
214
  return { path: dest };
215
215
  }
216
216
 
217
+ /**
218
+ * A sent line as the USER should see it again: "[Image #1: /Users/…/x.png]" → "[Image #1]".
219
+ * The file path is for the agent, which reads the image from it; on screen it is noise (the echo
220
+ * of the question showed it). Claude Code shows the placeholder the same way.
221
+ */
222
+ export function imageMarkersForDisplay(text) {
223
+ return String(text ?? "").replace(/\[Image #(\d+): [^\]\n]+\]/g, "[Image #$1]");
224
+ }
225
+
217
226
  /** Placeholders shown in the input, and the files behind them. */
218
227
  export function createImageStore() {
219
228
  const store = new Map(); // token -> absolute path
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gu-cli",
3
- "version": "1.0.754",
3
+ "version": "1.0.756",
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",