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.
Files changed (49) hide show
  1. {hexcli-2.8.0 → hexcli-2.9.0}/.gitignore +3 -0
  2. {hexcli-2.8.0 → hexcli-2.9.0}/CHANGELOG.md +136 -0
  3. {hexcli-2.8.0 → hexcli-2.9.0}/PKG-INFO +110 -146
  4. {hexcli-2.8.0 → hexcli-2.9.0}/README.md +109 -145
  5. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/__init__.py +1 -1
  6. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/compaction.py +1 -1
  7. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/launcher.py +17 -1
  8. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/loop_v2.py +4 -1
  9. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/protocol_v2.py +333 -19
  10. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/tools.py +13 -2
  11. {hexcli-2.8.0 → hexcli-2.9.0}/install.ps1 +17 -0
  12. {hexcli-2.8.0 → hexcli-2.9.0}/Hex CLI.cmd +0 -0
  13. {hexcli-2.8.0 → hexcli-2.9.0}/LICENSE +0 -0
  14. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/agent.py +0 -0
  15. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/assets/hexcli.ico +0 -0
  16. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/assets/hexcli.png +0 -0
  17. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/cancel.py +0 -0
  18. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/chatlog.py +0 -0
  19. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/commands.py +0 -0
  20. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/config.py +0 -0
  21. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/diffview.py +0 -0
  22. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/distribution.py +0 -0
  23. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/doctor.py +0 -0
  24. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/escalate.py +0 -0
  25. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/http_client.py +0 -0
  26. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/lineedit.py +0 -0
  27. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/llm.py +0 -0
  28. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/local_escalation.py +0 -0
  29. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/lockfile.py +0 -0
  30. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/markdown_stream.py +0 -0
  31. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/memory.py +0 -0
  32. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/network.py +0 -0
  33. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/parsing.py +0 -0
  34. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/paths.py +0 -0
  35. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/prompts.py +0 -0
  36. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/repl.py +0 -0
  37. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/safety.py +0 -0
  38. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/sessions.py +0 -0
  39. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/setup_wizard.py +0 -0
  40. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/shell_session.py +0 -0
  41. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/statusbar.py +0 -0
  42. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/stream_render.py +0 -0
  43. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/telemetry.py +0 -0
  44. {hexcli-2.8.0 → hexcli-2.9.0}/hexcli/ui.py +0 -0
  45. {hexcli-2.8.0 → hexcli-2.9.0}/launcher.py +0 -0
  46. {hexcli-2.8.0 → hexcli-2.9.0}/pyproject.toml +0 -0
  47. {hexcli-2.8.0 → hexcli-2.9.0}/shellai.cmd +0 -0
  48. {hexcli-2.8.0 → hexcli-2.9.0}/shellai.example.json +0 -0
  49. {hexcli-2.8.0 → hexcli-2.9.0}/shellai.py +0 -0
@@ -53,3 +53,6 @@ docs/backend_study/data_npu_ab/
53
53
  docs/backend_study/_pre_restart/
54
54
  tools/backend_bench/.tok_*.txt
55
55
  tools/backend_bench/.battery_probe.ps1
56
+
57
+ # python -m build output
58
+ dist/
@@ -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.8.0
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
+ [![PyPI](https://img.shields.io/pypi/v/hexcli)](https://pypi.org/project/hexcli/)
31
+ [![CI](https://github.com/NathanL15/Hex-CLI/actions/workflows/ci.yml/badge.svg)](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
- **Ask something.** The answer streams in, markdown and all, above an input
37
- box that stays on the last rows.
38
-
39
- ![A question answered in bullet points](docs/gifs/ask.gif)
40
-
41
- **Ask about the machine.** Live state comes from a real command, shown as
42
- it runs.
43
-
44
- ![The CPU name read with a PowerShell command](docs/gifs/machine.gif)
45
-
46
- **Change code.** Every file change prints a diff; when you ask for the
47
- tests, they run; `/diff` shows the turn's changes and `/undo` takes them
48
- back, files included.
49
-
50
- ![A docstring added, the tests run, then undone](docs/gifs/edit.gif)
51
-
52
- **Nothing destructive runs unasked.** Commands are classified before they
53
- run; a delete asks first.
54
-
55
- ![A delete command waits for a y/N before running](docs/gifs/safety.gif)
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
- The installer checks the machine, installs the package (`pip install .`,
103
- which gives you the `hex` and `hexcli` commands), downloads npurun, the
104
- model and the small embedding model that semantic memory uses, and runs
105
- `--doctor` when it finishes. It skips steps that are already done, so run
106
- it again after fixing anything it reports. Everything it writes for you
107
- lives in `~\.shellai`.
108
-
109
- It cannot download the QAIRT SDK for you, because Qualcomm does not allow
110
- redistribution. It prints instructions for that step and picks the SDK up on
111
- the next run. See [Setup by hand](#setup-by-hand) if you want to do the
112
- steps yourself.
113
-
114
- The Start Menu shortcut opens Hex in Windows Terminal when it is installed,
115
- through a "Hex CLI" profile the installer registers. It starts in your home
116
- folder; `/cwd <path>` moves into a project, whose `AGENTS.md` then applies. Text selection, copy
117
- and paste work there as in any other tab. Without Windows Terminal the
118
- shortcut uses the classic console, where drag-select is off because Hex
119
- disables QuickEdit (a click would otherwise freeze output); use the window
120
- menu's Edit, Mark to copy there. To have `hex` itself open in Windows
121
- Terminal from any shell, set it as the default terminal in its Settings,
122
- Startup.
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. Long input is trimmed to fit the context window.
141
- When stdout is not a terminal the answer is printed alone, with no
142
- progress text, so `hexcli "..." > answer.txt` and `hexcli "..." | clip`
143
- work.
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 commands. Put a `.md` file in `.shellai/commands/` in
169
- the project, or `~/.shellai/commands/` for all projects, and `/<filename>`
170
- sends its content as the prompt. `$ARGUMENTS` in the file is replaced with
171
- whatever follows the command.
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
- History is saved in `~/.shellai/input_history`. When stdin is not a
194
- terminal the agent falls back to plain `input()`, so pipes and CI work.
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. The model has a 4K token context and your
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. If the match is ambiguous it
217
- returns an error rather than guessing. `read_file` reads large files in
218
- pages. Every file change prints a diff and can be reverted with `/undo`.
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`, `format-*`, `iex` | asks first |
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
- `fetch_url` is the only tool that touches the network. It asks before every
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 need to write the
248
- keys you change. `shellai.example.json` lists every key with its default,
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
- | `autopilot_confirm_sensitive` | `true` | ask before touching keys and credentials |
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
- ## Logs and memory
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 . # from the checkout: the hex and hexcli commands, numpy, onnxruntime
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 a model**
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
- To build from source, use the fork. It has the prompt cache rewind, usage
326
- reporting, exact `max_tokens`, and request watchdog that Hex CLI relies on.
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 25 offline test suites against a mock backend, so no NPU
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 # 41 cases
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