hexcli 2.8.0__tar.gz → 2.9.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.8.0 → hexcli-2.9.0}/.gitignore +3 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/CHANGELOG.md +136 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/PKG-INFO +110 -146
- {hexcli-2.8.0 → hexcli-2.9.0}/README.md +109 -145
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/__init__.py +1 -1
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/compaction.py +1 -1
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/launcher.py +17 -1
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/loop_v2.py +4 -1
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/protocol_v2.py +333 -19
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/tools.py +13 -2
- {hexcli-2.8.0 → hexcli-2.9.0}/install.ps1 +17 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/Hex CLI.cmd +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/LICENSE +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/agent.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/assets/hexcli.ico +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/assets/hexcli.png +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/cancel.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/chatlog.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/commands.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/config.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/diffview.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/distribution.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/doctor.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/escalate.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/http_client.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/lineedit.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/llm.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/local_escalation.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/lockfile.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/markdown_stream.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/memory.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/network.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/parsing.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/paths.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/prompts.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/repl.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/safety.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/sessions.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/setup_wizard.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/shell_session.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/statusbar.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/stream_render.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/telemetry.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/ui.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/launcher.py +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/pyproject.toml +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/shellai.cmd +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/shellai.example.json +0 -0
- {hexcli-2.8.0 → hexcli-2.9.0}/shellai.py +0 -0
|
@@ -6,6 +6,142 @@ the Hexagon NPU, not single-run anecdotes.
|
|
|
6
6
|
|
|
7
7
|
## Unreleased
|
|
8
8
|
|
|
9
|
+
## 2.9.0 — 2026-09-13
|
|
10
|
+
|
|
11
|
+
A minor release: `edit_file` and `write_file` behave differently for the
|
|
12
|
+
model, and the launcher hands the agent its package path. Gate: extended
|
|
13
|
+
suite at 5 runs PASS after recheck, uc1 at 6 runs, smoke 10/10, CI green.
|
|
14
|
+
|
|
15
|
+
- `edit_file` merges the model's change into the file instead of pasting
|
|
16
|
+
its memory of the file. Multi-turn traces (uc1-t3/t4, 2026-09-13) show
|
|
17
|
+
the 4B rebuilding lines from the traceback in its history rather than
|
|
18
|
+
copying them: `for item in data` where the file says `items`, `"average":
|
|
19
|
+
average` where it says `avg`, a dropped trailing comment. Its
|
|
20
|
+
`old_string` then sits at 40–95 % similarity while the change it wants is
|
|
21
|
+
small and clear (`appned` → `append`; add a key to a dict). The old
|
|
22
|
+
fallback pasted `new_string` over the closest region when similarity was
|
|
23
|
+
≥ 95 %, which installed the misremembered names: five of six live uc1-t4
|
|
24
|
+
runs ended in `NameError: name 'average' is not defined`, put there by the
|
|
25
|
+
harness, and the model then retried the same line four times. The paste
|
|
26
|
+
is gone. `protocol_v2._apply_one` now runs a token-level three-way merge
|
|
27
|
+
between the file's closest region, `old_string` and `new_string`: hunks
|
|
28
|
+
the model changed are applied where the file agrees with what the model
|
|
29
|
+
saw, tokens the model misremembered keep the file's text, and a hunk that
|
|
30
|
+
touches a misremembered token refuses (unless the "change" merely restates
|
|
31
|
+
what the file already says, which is skipped). Inserted lines are
|
|
32
|
+
re-indented by the delta between the model's line and the file's; a
|
|
33
|
+
whitespace run holding a newline never matches one that does not; a
|
|
34
|
+
replacement that would restate the tokens beside its anchor, grow into a
|
|
35
|
+
side the model did not see correctly, or carry JSON escapes into a file
|
|
36
|
+
that has no backslashes, refuses. Every refusal still reports the closest
|
|
37
|
+
region as before. Replaying the saved traces against the fixtures: of 20
|
|
38
|
+
distinct failed uc1-t3 edits 5 now apply correctly, 15 still error, 0
|
|
39
|
+
apply wrongly; of 23 non-exact uc1-t4 attempts 8 apply, 12 refuse, 0
|
|
40
|
+
produce a harmful file (the first draft produced two syntax errors, from
|
|
41
|
+
un-shifted indentation and literal `\"` sequences, both now refused or
|
|
42
|
+
fixed).
|
|
43
|
+
- `edit_file` decodes a double-escaped `old_string`. 58 of 667 saved
|
|
44
|
+
edit_file calls (38 of them errors) carried `\"` or literal `\n` because
|
|
45
|
+
the model escaped its JSON arguments twice (agentic-3: `\"name\":
|
|
46
|
+
\"demo\"` for a file holding `"name": "demo"`). When the raw string is
|
|
47
|
+
absent from the file and the decoded one is present, both strings are
|
|
48
|
+
decoded once and the tiers run again. `write_file` does the same for a
|
|
49
|
+
body that is one line of literal `\n` sequences with no real line break
|
|
50
|
+
(1 of 389 saved calls), and leaves any body with a real newline alone.
|
|
51
|
+
- Audit of the same bug classes elsewhere: protocol v2's `edit` action
|
|
52
|
+
shares the applier and gains all of the above; the JSON action parser
|
|
53
|
+
only tracks escapes to find brace boundaries and never re-escapes; tier 3
|
|
54
|
+
(indent shift) replaces whole lines and has no trailing-segment case;
|
|
55
|
+
`run_command` carried one escaped argument in 303 calls and it was a
|
|
56
|
+
legitimate regex.
|
|
57
|
+
- The edit and write event lines name what landed a non-exact edit
|
|
58
|
+
(`, transfer match`, `, unescaped match`, `, indent match`; `, unescaped`
|
|
59
|
+
on a write) so an arm log shows which calls the fallbacks rescued; the
|
|
60
|
+
model still reads `Edited <path>` / `Wrote <path>`.
|
|
61
|
+
- A checkout's launcher runs the checkout's code. `python -m hexcli.agent`
|
|
62
|
+
resolves the package from the working directory, then site-packages; the
|
|
63
|
+
Terminal profile starts in the home directory, so since the 2.8.0
|
|
64
|
+
packaging the launcher started from `Hex CLI.cmd` was silently running
|
|
65
|
+
the pip-installed copy (2.7.1 on the owner's machine on 2026-09-13,
|
|
66
|
+
found when a new input-line feature did not appear) while the checkout
|
|
67
|
+
sat unused. The launcher now prepends its own package's parent to the
|
|
68
|
+
child's `PYTHONPATH` (`_agent_env`), a no-op for an installed copy.
|
|
69
|
+
- `run_recheck.cmd` documents that the case list must be quoted: cmd splits
|
|
70
|
+
an unquoted comma list into arguments, so the first 2026-09-13 recheck ran
|
|
71
|
+
one case and handed the second case's name to the gate as a baseline path.
|
|
72
|
+
- Live numbers (fresh server per arm, 2026-09-13, a day with under 1 GB
|
|
73
|
+
free on the 16 GB machine). Smoke 10/10. uc1 at 6 runs: t1 6/6, t2 6/6,
|
|
74
|
+
t3 5/6 (3/3 on 09-12, 15/24 pooled over earlier arms; the miss called no
|
|
75
|
+
tool), t4 1/6 (0/3 in every baseline: the first pass ever, on a merged
|
|
76
|
+
insertion beside the file's own `avg`), t5 0/6, t6 0/6 as before; the
|
|
77
|
+
merge landed 13 edits across uc1/uc2 that would have errored. uc2 at 3
|
|
78
|
+
runs: t2 2/3 (the model edited on an explain-only turn; both stray edits
|
|
79
|
+
refused), the rest 3/3. uc3 at 3 runs, twice: every run invalid on both
|
|
80
|
+
attempts, the client's 300 s timeout expiring while the server answered
|
|
81
|
+
429 busy at ~3,200 tokens of context (the 09-12 baseline lost one of
|
|
82
|
+
three the same way; no HTP hang in the server log), so uc3 has no valid
|
|
83
|
+
measurement today. Extended suite at 5 runs, seed 20260913: 31/44 pass^5
|
|
84
|
+
(32/44 on the 2.7.x baseline, 31/41 on 09-05), run-level 162/205 vs
|
|
85
|
+
165/208, Fisher p=1.0; not one fallback tier fired in those 220 runs (the
|
|
86
|
+
event-line markers show none), so the suite is a regression check only.
|
|
87
|
+
Gate RECHECK on agentic-3 (4/5) and self-correct-1 (4/5); recheck at 6
|
|
88
|
+
runs: self-correct-1 5/6, agentic-3 4/6 (two double-escaped runs, one
|
|
89
|
+
rescued by the decode, one landing the model's own comma-less JSON, and
|
|
90
|
+
one old_string hallucinated three times) → gate FAIL; a second agentic-3
|
|
91
|
+
recheck on a fresh server 6/6 → gate PASS. Ceiling panel: +lint-1
|
|
92
|
+
3/5→5/5, +trap-4 0/5→1/5, −regression-anchor-1 5/5→4/5 (the model wrote
|
|
93
|
+
invalid JSON through an exact-match edit). Results:
|
|
94
|
+
`merge_transfer_r5.json`, `multiturn_uc{1,2,3}_merge_*_20260913.json`,
|
|
95
|
+
log `merge_transfer.log`; the earlier anchor-only tier's arm is
|
|
96
|
+
`delta_transfer_r5.json` (gate PASS after recheck, 27/44, superseded).
|
|
97
|
+
|
|
98
|
+
## 2.8.1 — 2026-09-12
|
|
99
|
+
|
|
100
|
+
A patch release: nothing model-facing and nothing the launcher hands the
|
|
101
|
+
server changed. Gate: CI green on main; smoke 10/10 on a fresh
|
|
102
|
+
server.
|
|
103
|
+
|
|
104
|
+
- Installer repairs a Windows Terminal profile whose icon path 2.8.0
|
|
105
|
+
invalidated. The icon moved into the package (`hexcli/assets/`) that
|
|
106
|
+
release, but an existing "Hex CLI" profile kept pointing at the old
|
|
107
|
+
`assets/hexcli.png`; Terminal silently falls back to its own logo on a
|
|
108
|
+
missing file. `install.ps1` now rewrites a stale `hexcli.png` icon path
|
|
109
|
+
in a user's own profile with a targeted text edit, leaving the rest of
|
|
110
|
+
the profile untouched, and the fragment it writes already uses the
|
|
111
|
+
package path.
|
|
112
|
+
|
|
113
|
+
- Releases carry the wheel and the source distribution and go to PyPI
|
|
114
|
+
(`pip install hexcli`): `.github/workflows/publish.yml` runs when a
|
|
115
|
+
release is published, builds both, attaches them and publishes through
|
|
116
|
+
PyPI trusted publishing. 2.8.0's files were attached by hand.
|
|
117
|
+
- README rewritten shorter: the PyPI route sits in Install next to the
|
|
118
|
+
installer, the sample transcript the clips made redundant is gone, the
|
|
119
|
+
configuration table keeps the keys people change, memory and logs are a
|
|
120
|
+
short section under Usage, and the clips use absolute links so they
|
|
121
|
+
render on PyPI, in a two-by-two grid; the edit clip plays faster
|
|
122
|
+
(23 s, was 32 s). Version and CI badges at the top.
|
|
123
|
+
- Paper and CLAUDE.md current through 2.8.0: the timeline row and the
|
|
124
|
+
closing state paragraph record the package and the classifier finding,
|
|
125
|
+
and the milestones table breaks across pages instead of running off
|
|
126
|
+
one (it had been overflowing page 9).
|
|
127
|
+
- Paper numbers re-audited against the code and the latest results: the
|
|
128
|
+
window figure shows the 3,696-token server budget and the ~810-token
|
|
129
|
+
history it leaves instead of the disproven 2,600-token cliff; the
|
|
130
|
+
headline is 32/44 at pass^5 (LATEST.md) and 35/48 multi-turn; the
|
|
131
|
+
"9 s per new conversation" figure is retracted as the backend study
|
|
132
|
+
found; a paragraph carries that study's numbers (15.5 tok/s, 0.8 s warm
|
|
133
|
+
first token, polling-off power). ARCHITECTURE.md gets the same pass
|
|
134
|
+
(template in prompts.py, 29 suites, 44 cases, a dated note on the
|
|
135
|
+
pre-Rewind TTFT section) and the README's suite size reads 44.
|
|
136
|
+
- Paper restructured to six pages: the abstract states the v2.8.0 numbers
|
|
137
|
+
and the thesis; the runtime lever (KV rewind, the context re-read) is
|
|
138
|
+
its own section between results and negative results; the status
|
|
139
|
+
section is one paragraph; the milestones table is one line per row; a
|
|
140
|
+
positioning paragraph names what the report sits next to; three
|
|
141
|
+
figures that duplicated prose (chip diagram, prompt anatomy, NPU/CPU
|
|
142
|
+
bars) are gone and their numbers kept in the text. Every figure in the
|
|
143
|
+
paper is unchanged from the audit above.
|
|
144
|
+
|
|
9
145
|
## 2.8.0 — 2026-09-12
|
|
10
146
|
|
|
11
147
|
A minor release by RELEASING.md's rule (the launcher moved and its
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: hexcli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.9.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
|
|
@@ -27,61 +27,37 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
|
|
28
28
|
# Hex CLI
|
|
29
29
|
|
|
30
|
+
[](https://pypi.org/project/hexcli/)
|
|
31
|
+
[](https://github.com/NathanL15/Hex-CLI/actions/workflows/ci.yml)
|
|
32
|
+
|
|
30
33
|
Hex CLI is a coding agent for the Windows terminal, similar to aider or
|
|
31
34
|
Claude Code, that runs a local model on the Snapdragon Hexagon NPU. It reads
|
|
32
35
|
and edits files, runs commands, and checks its own work. The model is
|
|
33
36
|
Qwen3-4B, served by [npurun](https://github.com/bpbonker/npurun). After
|
|
34
37
|
setup it works offline.
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
run; a delete asks first
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
> the median calc in processor.py is wrong for even-length lists, fix it
|
|
59
|
-
|
|
60
|
-
◆ read_file
|
|
61
|
-
▸ [read] processor.py (lines 1-40 of 40)
|
|
62
|
-
|
|
63
|
-
◆ edit_file
|
|
64
|
-
▸ [edit] processor.py (+2 lines)
|
|
65
|
-
~ processor.py (+3 −1)
|
|
66
|
-
@@ -12,4 +12,6 @@
|
|
67
|
-
- return sorted(data)[len(data) // 2]
|
|
68
|
-
+ mid = len(data) // 2
|
|
69
|
-
+ if len(data) % 2 == 0:
|
|
70
|
-
+ return (sorted(data)[mid - 1] + sorted(data)[mid]) / 2
|
|
71
|
-
|
|
72
|
-
◆ run_code
|
|
73
|
-
|
|
74
|
-
Fixed: even-length lists now average the two middle values. The test file
|
|
75
|
-
prints 3.5 for [1, 2, 5, 6].
|
|
76
|
-
|
|
77
|
-
──────────────────────────────────────────────────────────────────
|
|
78
|
-
> ask, or / for commands
|
|
79
|
-
──────────────────────────────────────────────────────────────────
|
|
80
|
-
context ◔ 21% npu 0% mem 12.5/15.6 GB ~\proj (main)
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Your messages sit on a light band, the input box and status line stay on
|
|
84
|
-
the last rows, and the answer streams in above them.
|
|
39
|
+
<table>
|
|
40
|
+
<tr>
|
|
41
|
+
<td width="50%" valign="top">
|
|
42
|
+
<b>Ask something.</b> The answer streams in, markdown and all, above an input box that stays on the last rows.<br><br>
|
|
43
|
+
<img src="https://raw.githubusercontent.com/NathanL15/Hex-CLI/main/docs/gifs/ask.gif" alt="A question answered in bullet points" width="100%">
|
|
44
|
+
</td>
|
|
45
|
+
<td width="50%" valign="top">
|
|
46
|
+
<b>Ask about the machine.</b> Live state comes from a real command, shown as it runs.<br><br>
|
|
47
|
+
<img src="https://raw.githubusercontent.com/NathanL15/Hex-CLI/main/docs/gifs/machine.gif" alt="The CPU name read with a PowerShell command" width="100%">
|
|
48
|
+
</td>
|
|
49
|
+
</tr>
|
|
50
|
+
<tr>
|
|
51
|
+
<td width="50%" valign="top">
|
|
52
|
+
<b>Change code.</b> Every file change prints a diff; when you ask for the tests, they run; <code>/diff</code> shows the turn's changes and <code>/undo</code> takes them back, files included.<br><br>
|
|
53
|
+
<img src="https://raw.githubusercontent.com/NathanL15/Hex-CLI/main/docs/gifs/edit.gif" alt="A docstring added, the tests run, then undone" width="100%">
|
|
54
|
+
</td>
|
|
55
|
+
<td width="50%" valign="top">
|
|
56
|
+
<b>Nothing destructive runs unasked.</b> Commands are classified before they run; a delete asks first.<br><br>
|
|
57
|
+
<img src="https://raw.githubusercontent.com/NathanL15/Hex-CLI/main/docs/gifs/safety.gif" alt="A delete command waits for a y/N before running" width="100%">
|
|
58
|
+
</td>
|
|
59
|
+
</tr>
|
|
60
|
+
</table>
|
|
85
61
|
|
|
86
62
|
## Requirements
|
|
87
63
|
|
|
@@ -93,33 +69,42 @@ Tested on a Snapdragon X Elite. It will not run on x86 machines or on Macs.
|
|
|
93
69
|
|
|
94
70
|
## Install
|
|
95
71
|
|
|
72
|
+
The installer does everything the machine allows:
|
|
73
|
+
|
|
96
74
|
```powershell
|
|
97
75
|
git clone https://github.com/NathanL15/Hex-CLI
|
|
98
76
|
cd Hex-CLI
|
|
99
77
|
.\install.ps1
|
|
100
78
|
```
|
|
101
79
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
It
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
80
|
+
It checks the machine, installs the package, downloads npurun, the model
|
|
81
|
+
and the small embedding model that memory uses, and runs `--doctor` when it
|
|
82
|
+
finishes. It skips steps that are already done, so run it again after
|
|
83
|
+
fixing anything it reports. Everything it writes for you lives in
|
|
84
|
+
`~\.shellai`.
|
|
85
|
+
|
|
86
|
+
The one thing it cannot download is the QAIRT SDK, because Qualcomm does
|
|
87
|
+
not allow redistribution. It prints the instructions for that step and
|
|
88
|
+
picks the SDK up on the next run.
|
|
89
|
+
|
|
90
|
+
Or install from PyPI. The package is the same one the installer uses; you
|
|
91
|
+
install the SDK yourself and the rest downloads itself:
|
|
92
|
+
|
|
93
|
+
```powershell
|
|
94
|
+
pip install hexcli # the hex and hexcli commands
|
|
95
|
+
hexcli --update # downloads npurun
|
|
96
|
+
hex # the first run downloads the model, about 2.5 GB
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`hexcli --doctor` lists what is still missing and the command that fixes
|
|
100
|
+
each item. Every [release](https://github.com/NathanL15/Hex-CLI/releases)
|
|
101
|
+
also carries the wheel and the source distribution.
|
|
102
|
+
|
|
103
|
+
The Start Menu shortcut opens Hex in Windows Terminal, through a profile
|
|
104
|
+
the installer registers, starting in your home folder; `/cwd <path>` moves
|
|
105
|
+
into a project. Without Windows Terminal it uses the classic console, where
|
|
106
|
+
drag-select is off because Hex disables QuickEdit (a click would otherwise
|
|
107
|
+
freeze output); use the window menu's Edit, Mark to copy there.
|
|
123
108
|
|
|
124
109
|
## Usage
|
|
125
110
|
|
|
@@ -133,14 +118,11 @@ hexcli --doctor # check the install
|
|
|
133
118
|
hexcli --update # refresh npurun (and pull the source in a checkout)
|
|
134
119
|
```
|
|
135
120
|
|
|
136
|
-
In a checkout without the package installed, `python launcher.py` and
|
|
137
|
-
`python -m hexcli.agent` are the same two commands.
|
|
138
|
-
|
|
139
121
|
Piped input is added to the request as context, or used as the request if
|
|
140
|
-
there is no argument.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
122
|
+
there is no argument. When stdout is not a terminal the answer is printed
|
|
123
|
+
alone, so `hexcli "..." > answer.txt` and `hexcli "..." | clip` work. In a
|
|
124
|
+
checkout without the package installed, `python launcher.py` and
|
|
125
|
+
`python -m hexcli.agent` are the same two commands.
|
|
144
126
|
|
|
145
127
|
### Commands
|
|
146
128
|
|
|
@@ -165,10 +147,10 @@ work.
|
|
|
165
147
|
| `/doctor` | check the install |
|
|
166
148
|
| `Esc` | cancel the running step |
|
|
167
149
|
|
|
168
|
-
You can add your own
|
|
169
|
-
|
|
170
|
-
sends its content as the prompt
|
|
171
|
-
|
|
150
|
+
You can add your own. Put a `.md` file in `.shellai/commands/` in the
|
|
151
|
+
project, or `~/.shellai/commands/` for all projects, and `/<filename>`
|
|
152
|
+
sends its content as the prompt, with `$ARGUMENTS` replaced by whatever
|
|
153
|
+
follows the command:
|
|
172
154
|
|
|
173
155
|
```powershell
|
|
174
156
|
# .shellai/commands/review.md contains: Review $ARGUMENTS for bugs and style issues.
|
|
@@ -188,21 +170,15 @@ whatever follows the command.
|
|
|
188
170
|
| `Ctrl+V` | paste a block. Nothing is sent until you press Enter |
|
|
189
171
|
| `Shift+Enter` | new line inside the entry |
|
|
190
172
|
| `\` then `Enter` | continue on a new line |
|
|
191
|
-
| `Ctrl+Plus` `Ctrl+Minus` | text size in the classic console |
|
|
192
173
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
The input box stays on the last rows of the window, and the conversation
|
|
197
|
-
scrolls up above it. The status line under it
|
|
198
|
-
shows how full the context is, the NPU load (the counter behind Task
|
|
199
|
-
Manager's NPU graph), memory in use, and the working directory and branch.
|
|
200
|
-
Set `status_bar` to `false` for the old inline prompt.
|
|
174
|
+
The status line under the input box shows how full the context is, the
|
|
175
|
+
NPU load, memory in use, and the working directory and branch. Set
|
|
176
|
+
`status_bar` to `false` for a plain inline prompt.
|
|
201
177
|
|
|
202
178
|
### Project instructions
|
|
203
179
|
|
|
204
180
|
If a project has an `AGENTS.md`, the agent reads it every turn. Keep it
|
|
205
|
-
under about 1,200 characters
|
|
181
|
+
under about 1,200 characters: the model has a 4K token context and your
|
|
206
182
|
request has to fit in there too.
|
|
207
183
|
|
|
208
184
|
### Tools
|
|
@@ -213,9 +189,19 @@ request has to fit in there too.
|
|
|
213
189
|
for the full signatures.
|
|
214
190
|
|
|
215
191
|
`edit_file` tries an exact match first, then a whitespace-tolerant match,
|
|
216
|
-
then a close match if there is exactly one
|
|
217
|
-
|
|
218
|
-
|
|
192
|
+
then a close match if there is exactly one; an ambiguous match returns an
|
|
193
|
+
error rather than a guess. `read_file` reads large files in pages. Every
|
|
194
|
+
file change prints a diff and can be reverted with `/undo`.
|
|
195
|
+
|
|
196
|
+
### Memory and logs
|
|
197
|
+
|
|
198
|
+
Memory is two local stores, one per project and one global, built on
|
|
199
|
+
MiniLM embeddings. When idle, the agent condenses recent turns into short
|
|
200
|
+
notes that later prompts include; `/memory` shows what is stored.
|
|
201
|
+
|
|
202
|
+
Each session is written to `~/.shellai/chatlog/` as a JSONL file: every
|
|
203
|
+
request, every message sent to the model, every reply with its latency,
|
|
204
|
+
every tool call with its output. Secrets in the config are redacted.
|
|
219
205
|
|
|
220
206
|
## Safety
|
|
221
207
|
|
|
@@ -223,18 +209,16 @@ Commands are classified before they run:
|
|
|
223
209
|
|
|
224
210
|
| Level | Examples | Behaviour |
|
|
225
211
|
|---|---|---|
|
|
226
|
-
| destructive | `Remove-Item`, `git reset --hard`, `
|
|
212
|
+
| destructive | `Remove-Item`, `git reset --hard`, `Format-Volume`, `iex` | asks first |
|
|
227
213
|
| sensitive | ssh/gpg/aws keys, hosts file, registry hives, credential stores, `-EncodedCommand` | asks first, denied when non-interactive |
|
|
228
214
|
| safe | `Get-*`, `ls`, `git status` | runs |
|
|
229
215
|
| caution | anything else | runs |
|
|
230
216
|
|
|
231
217
|
File writes stay inside the working directory unless you widen the scope
|
|
232
218
|
with `workspace_write_allow`. Reads can go anywhere. Key and credential
|
|
233
|
-
paths are blocked for all file tools.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
fetch, and is refused when non-interactive. Set `network_access` to
|
|
237
|
-
`"deny"` to remove the tool or `"allow"` to skip the prompt.
|
|
219
|
+
paths are blocked for all file tools. `fetch_url` is the only tool that
|
|
220
|
+
touches the network; it asks before every fetch and is refused when
|
|
221
|
+
non-interactive.
|
|
238
222
|
|
|
239
223
|
Each classified command is appended to `.shellai/audit.log`. Text inside
|
|
240
224
|
files and tool output is treated as data, not instructions. A 4B model does
|
|
@@ -244,45 +228,20 @@ doing so.
|
|
|
244
228
|
## Configuration
|
|
245
229
|
|
|
246
230
|
Config is optional. `~\.shellai\shellai.json` and `.shellai/config.json`
|
|
247
|
-
in a project are merged over the defaults, so you only
|
|
248
|
-
|
|
249
|
-
and `/setup` writes the file for you.
|
|
231
|
+
in a project are merged over the defaults, so you only write the keys you
|
|
232
|
+
change. The ones people change:
|
|
250
233
|
|
|
251
234
|
| Key | Default | Effect |
|
|
252
235
|
|---|---|---|
|
|
253
236
|
| `max_agent_steps` | `15` | tool calls per turn |
|
|
254
|
-
| `live_streaming` | `true` | show the answer as it arrives |
|
|
255
|
-
| `rich_input` | `true` | history, Tab completion, multi-line paste |
|
|
256
|
-
| `side_padding` | `2` | left margin in columns |
|
|
257
|
-
| `status_bar` | `true` | input box and status line at the bottom |
|
|
258
|
-
| `user_highlight` | `true` | light band behind your messages in the transcript |
|
|
259
237
|
| `show_diffs` | `true` | print a diff after each file change |
|
|
238
|
+
| `status_bar` | `true` | input box and status line at the bottom |
|
|
260
239
|
| `workspace_write_scope` | `true` | keep writes inside the working directory |
|
|
261
|
-
| `
|
|
262
|
-
| `network_access` | `"ask"` | `"deny"` or `"allow"` |
|
|
263
|
-
| `require_verification` | `true` | ask the agent to check its own edits |
|
|
264
|
-
| `prompt_split` | `true` | answer plain questions without the tool loop |
|
|
265
|
-
| `escalation_local_model` | `""` | a larger local model to consult when stuck |
|
|
240
|
+
| `network_access` | `"ask"` | `"deny"` removes `fetch_url`, `"allow"` skips the prompt |
|
|
266
241
|
| `memory_enabled` | `true` | semantic memory |
|
|
267
|
-
| `chat_log_enabled` | `true` | write full session logs |
|
|
268
|
-
| `protocol` | `"v1"` | `"v2"` is experimental |
|
|
269
242
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
Each session is written to `~/.shellai/chatlog/` as a JSONL file: every
|
|
273
|
-
request, every message sent to the model, every reply with its latency,
|
|
274
|
-
every tool call with its output. Secrets in the config are redacted.
|
|
275
|
-
`/stats` prints the path of the current file.
|
|
276
|
-
|
|
277
|
-
```powershell
|
|
278
|
-
python tools/chatlog_report.py # summary across all sessions
|
|
279
|
-
python tools/chatlog_report.py --last # replay the most recent session
|
|
280
|
-
python tools/chatlog_report.py --session 1a2b # replay one session by id prefix
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
Memory is two local stores, one per project and one global, built on MiniLM
|
|
284
|
-
embeddings. When idle, the agent condenses recent turns into short notes
|
|
285
|
-
that are included in later prompts. `/memory` shows what is stored.
|
|
243
|
+
`shellai.example.json` lists every key with its default, and `/setup`
|
|
244
|
+
writes the file for you.
|
|
286
245
|
|
|
287
246
|
## Setup by hand
|
|
288
247
|
|
|
@@ -291,7 +250,7 @@ These are the steps `install.ps1` performs. `--doctor` checks each one.
|
|
|
291
250
|
**1. The package**
|
|
292
251
|
|
|
293
252
|
```powershell
|
|
294
|
-
pip install
|
|
253
|
+
pip install hexcli # or, from the checkout: pip install .
|
|
295
254
|
```
|
|
296
255
|
|
|
297
256
|
`ruff` is optional and enables the `lint_code` tool.
|
|
@@ -313,22 +272,26 @@ setx ADSP_LIBRARY_PATH "C:\Qualcomm\AIStack\QAIRT_2.50.0\lib\hexagon-v73\unsigne
|
|
|
313
272
|
If `ADSP_LIBRARY_PATH` is missing, npurun crashes with
|
|
314
273
|
`STATUS_STACK_BUFFER_OVERRUN`.
|
|
315
274
|
|
|
316
|
-
**3. npurun and
|
|
317
|
-
|
|
318
|
-
The NPU server is a fork of npurun, at
|
|
319
|
-
[NathanL15/npurun](https://github.com/NathanL15/npurun) on the
|
|
320
|
-
`hexcli-fork` branch. Each release there has a prebuilt `npurun-arm64.exe`.
|
|
321
|
-
Hex CLI expects one specific build, set by `REQUIRED_NPURUN` in
|
|
322
|
-
`hexcli/launcher.py`. `--doctor` fails on an older build and `--update`
|
|
323
|
-
replaces it (the download goes to `~\.shellai\bin`).
|
|
275
|
+
**3. npurun and the model**
|
|
324
276
|
|
|
325
|
-
|
|
326
|
-
|
|
277
|
+
The NPU server is a fork of npurun at
|
|
278
|
+
[NathanL15/npurun](https://github.com/NathanL15/npurun), branch
|
|
279
|
+
`hexcli-fork`, with a prebuilt `npurun-arm64.exe` on each release. Hex CLI
|
|
280
|
+
expects one specific build, set by `REQUIRED_NPURUN` in
|
|
281
|
+
`hexcli/launcher.py`; `hexcli --update` downloads it to `~\.shellai\bin`
|
|
282
|
+
and `--doctor` fails on an older one. The fork adds the prompt cache
|
|
283
|
+
rewind, usage reporting, exact `max_tokens` and the request watchdog that
|
|
284
|
+
Hex CLI relies on, so upstream npurun will not do. To build it yourself:
|
|
327
285
|
|
|
328
286
|
```powershell
|
|
329
287
|
git clone -b hexcli-fork https://github.com/NathanL15/npurun
|
|
330
288
|
cd npurun
|
|
331
289
|
cmd /c "scripts\dev-shell-local.bat cargo install --path crates\npurun-cli"
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The model downloads on the first `hex` run, or by hand:
|
|
293
|
+
|
|
294
|
+
```powershell
|
|
332
295
|
npurun pull qwen3-4b-instruct-2507 # about 2.5 GB
|
|
333
296
|
```
|
|
334
297
|
|
|
@@ -357,7 +320,7 @@ curl -L -o ~\.shellai\onnx\tokenizer.json https://huggingface.co/sentence-transf
|
|
|
357
320
|
|
|
358
321
|
## Development
|
|
359
322
|
|
|
360
|
-
CI runs `ruff` and
|
|
323
|
+
CI runs `ruff` and 29 offline test suites against a mock backend, so no NPU
|
|
361
324
|
is needed for those:
|
|
362
325
|
|
|
363
326
|
```powershell
|
|
@@ -369,14 +332,15 @@ python evals/test_lineedit.py
|
|
|
369
332
|
|
|
370
333
|
The live evals need the NPU server. They check what the model actually did,
|
|
371
334
|
by looking at the filesystem and the answer, and run each case several
|
|
372
|
-
times because the model is not deterministic
|
|
335
|
+
times because the model is not deterministic:
|
|
373
336
|
|
|
374
337
|
```powershell
|
|
375
338
|
python evals/cases_smoke.py # quick check
|
|
376
|
-
python evals/cases_extended.py --runs 3 #
|
|
339
|
+
python evals/cases_extended.py --runs 3 # 44 cases
|
|
377
340
|
python evals/cases_multiturn.py --runs 3 --think-time 15
|
|
378
341
|
python evals/compare.py <before.json> <after.json>
|
|
379
342
|
python evals/gate.py --baseline <base.json> <candidate.json>
|
|
343
|
+
python tools/chatlog_report.py --last # replay the most recent session
|
|
380
344
|
```
|
|
381
345
|
|
|
382
346
|
Restart the NPU server before each suite. After an hour or two of steady
|