@tiens.nguyen/gu-cli 1.0.837 → 1.0.838

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.
@@ -12,7 +12,7 @@
12
12
  // host.close(tabId)
13
13
  // host.runsJs — true only where running gu's own JavaScript text is allowed (gu's own window).
14
14
  // The Chrome Web Store extension leaves it unset: text sent at run time and executed is
15
- // "remote code", which a Manifest V3 store extension may not run (#377, GitHub #158).
15
+ // "remote code", which a Manifest V3 store extension may not run (#377, GitHub #164).
16
16
  // No chrome.* and no Node API in here: it runs in an extension service worker and in Node.
17
17
 
18
18
  const consoleBuf = new Map(); // tabId → [{level, text}] since the last "console" read
@@ -2226,6 +2226,41 @@ def _announces_next_step(stripped: str) -> bool:
2226
2226
  return bool(_NEXT_STEP_RE.search(last)) and not _HANDED_TO_USER_RE.search(last)
2227
2227
 
2228
2228
 
2229
+ # A SENTENCE OF INTENT WITH NO CODE (#379, GitHub #163), measured: after a failed `cd` the model
2230
+ # replied "The shell was already in `frontend`, so `cd frontend` failed. Run without the cd." and
2231
+ # then "I forgot the code block. Run the tests with the correct workdir." — no code either time,
2232
+ # two failed steps (~94k input tokens). Neither says "let me" / "I'll", so _announces_next_step
2233
+ # missed them. A SHORT reply whose last sentence tells ITSELF to act (an imperative, not addressed
2234
+ # to "you"), or that admits leaving the code out, is a plan. A short report of finished work
2235
+ # ("Done. Run `npm start` to see it.") is not.
2236
+ _SELF_ACTION_RE = re.compile(
2237
+ r"^(?:(?:now|so|then|next|ok(?:ay)?),?\s+)?(?:run|re-?run|retry|try|check|read|open|call|use|"
2238
+ r"fix|edit|update|test|execute|search|grep|list|look|install|build|verify|inspect|add|remove|"
2239
+ r"stage|commit|push|create|write|redo|repeat|drop|skip|pass|set)\b", re.I)
2240
+ _SELF_FIX_RE = re.compile(
2241
+ r"\bI\s+(?:forgot|missed|left\s+out|omitted|didn't\s+include|did\s+not\s+include)\b[^.]*\b(?:code|block)\b"
2242
+ r"|\bno\s+code\s+block\b", re.I)
2243
+ _FINISHED_RE = re.compile(
2244
+ r"\b(?:done|committed|pushed|fixed|created|deployed|merged|finished|completed|all set)\b|✅", re.I)
2245
+
2246
+
2247
+ def _short_self_directive(stripped: str) -> bool:
2248
+ """A short, code-less reply that ends by telling itself what to do, or admits it left the
2249
+ code out. PURE, so the measured replies can be replayed."""
2250
+ t = (stripped or "").strip()
2251
+ if not t or len(t) > 300 or "\n\n" in t:
2252
+ return False
2253
+ if _SELF_FIX_RE.search(t):
2254
+ return True
2255
+ sentences = [x.strip() for x in re.split(r"(?<=[.!?])\s+", t) if x.strip()]
2256
+ if not sentences or len(sentences) > 3:
2257
+ return False
2258
+ last = sentences[-1]
2259
+ if re.search(r"\byou(?:r)?\b", last, re.I) or _FINISHED_RE.search(t):
2260
+ return False
2261
+ return bool(_SELF_ACTION_RE.match(last))
2262
+
2263
+
2229
2264
  def _looks_like_scratchpad(stripped: str) -> bool:
2230
2265
  """True if `stripped` reads like unresolved internal deliberation rather than a
2231
2266
  finished, user-facing answer — see module comment above for the signals used."""
@@ -2233,6 +2268,8 @@ def _looks_like_scratchpad(stripped: str) -> bool:
2233
2268
  return True
2234
2269
  if _announces_next_step(stripped):
2235
2270
  return True
2271
+ if _short_self_directive(stripped):
2272
+ return True
2236
2273
  return len(_DELIBERATION_RE.findall(stripped)) >= 3
2237
2274
 
2238
2275
 
@@ -13812,11 +13849,12 @@ def run_agent_chat(cfg):
13812
13849
 
13813
13850
  def _sanitize_parse_errors(self, msgs):
13814
13851
  """Swap smolagents' code-parsing error text for a delimiter-free directive
13815
- (see _PARSE_ERROR_REPLACEMENT). No-op unless the backend is kind=openai, and
13852
+ (see _PARSE_ERROR_REPLACEMENT), on every backend; a
13816
13853
  no-op on every other kind of message — genuine SyntaxError feedback from a
13817
13854
  block that DID run is left intact, since that is real information."""
13818
- if not self._openai_backend:
13819
- return
13855
+ # Every backend now (#379, GitHub #163): ollama.com is an "ollama" backend, kept the
13856
+ # stock text, and its model answered it twice more without code. gu runs strong
13857
+ # models only (2026-09-25), the reason the CODE-NOW retry went ungated in #139.
13820
13858
  n = 0
13821
13859
  for m in msgs or []:
13822
13860
  if not isinstance(m, dict):
@@ -13833,8 +13871,7 @@ def run_agent_chat(cfg):
13833
13871
  part["text"] = self._PARSE_ERROR_REPLACEMENT
13834
13872
  n += 1
13835
13873
  if n:
13836
- _log(f"#113 replaced {n} code-parsing error message(s) with a "
13837
- "delimiter-free directive (openai backend)")
13874
+ _log(f"#113 replaced {n} code-parsing error message(s) with a direct directive")
13838
13875
 
13839
13876
  def _prepare_completion_kwargs(self, *args, **kwargs):
13840
13877
  ck = super()._prepare_completion_kwargs(*args, **kwargs)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gu-cli",
3
- "version": "1.0.837",
3
+ "version": "1.0.838",
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",