hexcli 2.16.0__tar.gz → 2.17.0__tar.gz
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.
- {hexcli-2.16.0 → hexcli-2.17.0}/CHANGELOG.md +35 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/PKG-INFO +1 -1
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/__init__.py +1 -1
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/parsing.py +49 -4
- {hexcli-2.16.0 → hexcli-2.17.0}/.gitignore +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/Hex CLI.cmd +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/LICENSE +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/README.md +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/agent.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/assets/hexcli.ico +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/assets/hexcli.png +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/cancel.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/chatlog.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/commands.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/compaction.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/config.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/diffview.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/distribution.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/doctor.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/editing.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/http_client.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/launcher.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/lineedit.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/llm.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/lockfile.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/markdown_stream.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/memory.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/network.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/paths.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/prompts.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/psparse.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/repl.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/safety.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/sessions.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/setup_wizard.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/statusbar.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/stream_render.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/telemetry.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/tools.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/hexcli/ui.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/install.ps1 +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/launcher.py +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/pyproject.toml +0 -0
- {hexcli-2.16.0 → hexcli-2.17.0}/shellai.example.json +0 -0
|
@@ -4,6 +4,41 @@ Full evidence for every claim below — including the experiments that failed
|
|
|
4
4
|
lives in `docs/V2_PLAN.md` §14. Numbers are pass^k over repeated live runs on
|
|
5
5
|
the Hexagon NPU, not single-run anecdotes.
|
|
6
6
|
|
|
7
|
+
## 2.17.0 — 2026-09-18
|
|
8
|
+
|
|
9
|
+
A minor release: the parser accepts a reply it used to send back for a
|
|
10
|
+
retry, so what the model sees after such a reply changes.
|
|
11
|
+
|
|
12
|
+
> **Gate: PASS** on the pinned 24-case set, all 24 held at 5 runs with no
|
|
13
|
+
> recheck. Own arm, fresh server, seed 20260918, 6 invalid of 245 runs.
|
|
14
|
+
> Against the v2.11.1 12-run baseline: run-level 402/514 vs 174/224, −0.5 %,
|
|
15
|
+
> Fisher p = 0.92; pass^k 26 → 31 of 46 shared cases, McNemar p = 0.13. The
|
|
16
|
+
> close fired on five replies in the arm (`claims-1`, `claims-2`, two in
|
|
17
|
+
> `tests-claim-1`, one in `trap-3`); `claims-1` 3/5 against 3/11, `trap-3`
|
|
18
|
+
> 0/5 as before. Smoke 9/10 on a fresh server; the miss was `factual-1`,
|
|
19
|
+
> re-measured 7/10 against its recorded 79 % (p = 0.67).
|
|
20
|
+
|
|
21
|
+
- A reply that stops before its last closing brace is closed and decoded.
|
|
22
|
+
In the owner's 2026-09-18 session two `write_file` replies, 2,239 and 782
|
|
23
|
+
characters, ended at `"}` with the outer object never closed. The parser's
|
|
24
|
+
stray-quote repair took the decoder's "Expecting ',' delimiter" at the end
|
|
25
|
+
of the text as a quote that had ended a string early, escaped the
|
|
26
|
+
content's own closing quote, and the decoder then reported an unterminated
|
|
27
|
+
string; the retry feedback told the model to fix its quoting, the model
|
|
28
|
+
escaped everything twice, and the file it wrote had `\n` and `\"` in it as
|
|
29
|
+
text. Every step after that in the session was the model failing to repair
|
|
30
|
+
that file. When the decoder's error sits at the very end of the reply,
|
|
31
|
+
every string is closed and the brace depth is still positive, the missing
|
|
32
|
+
braces are appended and the object decodes. Measured over the 10,772
|
|
33
|
+
replies on record: 185 fail to decode, and 27 of them now decode (all one
|
|
34
|
+
brace short; 23 in `claims-1`, `claims-2` and `tests-claim-1`, four in the
|
|
35
|
+
owner's sessions). The close is only tried on the reply as sent: three
|
|
36
|
+
more replies are a brace short *after* a stray quote, and a reply whose
|
|
37
|
+
content string was cut off and then followed by a second object would
|
|
38
|
+
otherwise be glued into one, so those stay retries. A reply cut off inside
|
|
39
|
+
a string is still reported as cut off, and a stray quote in the middle is
|
|
40
|
+
still repaired as before; the other 158 undecodable replies are unchanged.
|
|
41
|
+
|
|
7
42
|
## 2.16.0 — 2026-09-18
|
|
8
43
|
|
|
9
44
|
A minor release: a tool result the model reads changes (`find_files` returns
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: hexcli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.17.0
|
|
4
4
|
Summary: Local Hexagon NPU terminal agent for Snapdragon X Elite Windows ARM64
|
|
5
5
|
Project-URL: Homepage, https://github.com/NathanL15/Hex-CLI
|
|
6
6
|
Project-URL: Repository, https://github.com/NathanL15/Hex-CLI
|
|
@@ -126,6 +126,49 @@ def _repair_stray_quote(text: str, err: json.JSONDecodeError) -> str | None:
|
|
|
126
126
|
return text[:q] + "\\" + text[q:]
|
|
127
127
|
|
|
128
128
|
|
|
129
|
+
def _close_unfinished_object(text: str, start: int, err: json.JSONDecodeError) -> str | None:
|
|
130
|
+
"""The reply ended before its last closing brace(s): `{"action":"write_file",
|
|
131
|
+
"args":{...,"content":"..."}` and then nothing. The decoder reports
|
|
132
|
+
"Expecting ',' delimiter" AT THE END of the text; every string is closed
|
|
133
|
+
and the brace depth is still positive. Appending the missing braces is
|
|
134
|
+
the whole repair. 30 of the 185 undecodable replies on record are this
|
|
135
|
+
shape, all one brace short, four of them in the owner's sessions
|
|
136
|
+
(2026-09-13 and 2026-09-18), and the stray-quote repair below used to
|
|
137
|
+
make them worse: it escaped the content's own closing quote, the decoder
|
|
138
|
+
then said "Unterminated string", and the retry feedback told the model
|
|
139
|
+
to fix its quoting -- which it did by escaping everything twice, and the
|
|
140
|
+
file it then wrote had backslashes in it. None when the error is not at
|
|
141
|
+
the end, a string is still open, or the braces already balance."""
|
|
142
|
+
if err.pos < len(text.rstrip()):
|
|
143
|
+
return None
|
|
144
|
+
depth, in_string, escaped = 0, False, False
|
|
145
|
+
for ch in text[start:]:
|
|
146
|
+
if in_string:
|
|
147
|
+
if escaped:
|
|
148
|
+
escaped = False
|
|
149
|
+
elif ch == "\\":
|
|
150
|
+
escaped = True
|
|
151
|
+
elif ch == '"':
|
|
152
|
+
in_string = False
|
|
153
|
+
elif ch == '"':
|
|
154
|
+
in_string = True
|
|
155
|
+
elif ch == "{":
|
|
156
|
+
depth += 1
|
|
157
|
+
elif ch == "}":
|
|
158
|
+
depth -= 1
|
|
159
|
+
if in_string or depth <= 0:
|
|
160
|
+
return None
|
|
161
|
+
return text.rstrip() + "}" * depth
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# Only the reply AS SENT is closed this way. Once a stray-quote repair has
|
|
165
|
+
# changed the text, a positive depth at the end can be the repair's own
|
|
166
|
+
# doing (a write whose content string was cut off, followed by a second
|
|
167
|
+
# object: the repairs walk the quotes back until the text "balances" and
|
|
168
|
+
# the close would then glue the two objects into one), and that reply must
|
|
169
|
+
# stay a retry -- evals/test_backports.py pins it.
|
|
170
|
+
|
|
171
|
+
|
|
129
172
|
def _loads_object(text: str) -> dict[str, Any] | None:
|
|
130
173
|
"""The first JSON object in `text`, decoded from its first brace and
|
|
131
174
|
ignoring whatever follows it (a second batched action, prose). Stray
|
|
@@ -133,12 +176,13 @@ def _loads_object(text: str) -> dict[str, Any] | None:
|
|
|
133
176
|
start = text.find("{")
|
|
134
177
|
if start < 0:
|
|
135
178
|
return None
|
|
136
|
-
for
|
|
179
|
+
for attempt in range(_MAX_QUOTE_REPAIRS + 1):
|
|
137
180
|
try:
|
|
138
181
|
parsed, _end = _DECODER.raw_decode(text, start)
|
|
139
182
|
return parsed if isinstance(parsed, dict) else None
|
|
140
183
|
except json.JSONDecodeError as err:
|
|
141
|
-
fixed =
|
|
184
|
+
fixed = (_close_unfinished_object(text, start, err) if attempt == 0 else None) \
|
|
185
|
+
or _repair_stray_quote(text, err)
|
|
142
186
|
if fixed is None:
|
|
143
187
|
return None
|
|
144
188
|
text = fixed
|
|
@@ -155,13 +199,14 @@ def _decode_failure(text: str) -> tuple[json.JSONDecodeError | None, str]:
|
|
|
155
199
|
if start < 0:
|
|
156
200
|
return None, text
|
|
157
201
|
last: json.JSONDecodeError | None = None
|
|
158
|
-
for
|
|
202
|
+
for attempt in range(_MAX_QUOTE_REPAIRS + 1):
|
|
159
203
|
try:
|
|
160
204
|
_DECODER.raw_decode(text, start)
|
|
161
205
|
return None, text
|
|
162
206
|
except json.JSONDecodeError as err:
|
|
163
207
|
last = err
|
|
164
|
-
fixed =
|
|
208
|
+
fixed = (_close_unfinished_object(text, start, err) if attempt == 0 else None) \
|
|
209
|
+
or _repair_stray_quote(text, err)
|
|
165
210
|
if fixed is None:
|
|
166
211
|
return err, text
|
|
167
212
|
text = fixed
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|