hexcli 2.8.0__tar.gz → 2.8.1__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.8.1}/.gitignore +3 -0
  2. {hexcli-2.8.0 → hexcli-2.8.1}/CHANGELOG.md +47 -0
  3. {hexcli-2.8.0 → hexcli-2.8.1}/PKG-INFO +110 -146
  4. {hexcli-2.8.0 → hexcli-2.8.1}/README.md +109 -145
  5. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/__init__.py +1 -1
  6. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/compaction.py +1 -1
  7. {hexcli-2.8.0 → hexcli-2.8.1}/install.ps1 +17 -0
  8. {hexcli-2.8.0 → hexcli-2.8.1}/Hex CLI.cmd +0 -0
  9. {hexcli-2.8.0 → hexcli-2.8.1}/LICENSE +0 -0
  10. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/agent.py +0 -0
  11. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/assets/hexcli.ico +0 -0
  12. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/assets/hexcli.png +0 -0
  13. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/cancel.py +0 -0
  14. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/chatlog.py +0 -0
  15. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/commands.py +0 -0
  16. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/config.py +0 -0
  17. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/diffview.py +0 -0
  18. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/distribution.py +0 -0
  19. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/doctor.py +0 -0
  20. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/escalate.py +0 -0
  21. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/http_client.py +0 -0
  22. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/launcher.py +0 -0
  23. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/lineedit.py +0 -0
  24. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/llm.py +0 -0
  25. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/local_escalation.py +0 -0
  26. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/lockfile.py +0 -0
  27. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/loop_v2.py +0 -0
  28. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/markdown_stream.py +0 -0
  29. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/memory.py +0 -0
  30. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/network.py +0 -0
  31. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/parsing.py +0 -0
  32. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/paths.py +0 -0
  33. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/prompts.py +0 -0
  34. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/protocol_v2.py +0 -0
  35. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/repl.py +0 -0
  36. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/safety.py +0 -0
  37. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/sessions.py +0 -0
  38. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/setup_wizard.py +0 -0
  39. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/shell_session.py +0 -0
  40. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/statusbar.py +0 -0
  41. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/stream_render.py +0 -0
  42. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/telemetry.py +0 -0
  43. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/tools.py +0 -0
  44. {hexcli-2.8.0 → hexcli-2.8.1}/hexcli/ui.py +0 -0
  45. {hexcli-2.8.0 → hexcli-2.8.1}/launcher.py +0 -0
  46. {hexcli-2.8.0 → hexcli-2.8.1}/pyproject.toml +0 -0
  47. {hexcli-2.8.0 → hexcli-2.8.1}/shellai.cmd +0 -0
  48. {hexcli-2.8.0 → hexcli-2.8.1}/shellai.example.json +0 -0
  49. {hexcli-2.8.0 → hexcli-2.8.1}/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,53 @@ the Hexagon NPU, not single-run anecdotes.
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 2.8.1 — 2026-09-12
10
+
11
+ A patch release: nothing model-facing and nothing the launcher hands the
12
+ server changed. Gate: CI green on main; smoke 10/10 on a fresh
13
+ server.
14
+
15
+ - Installer repairs a Windows Terminal profile whose icon path 2.8.0
16
+ invalidated. The icon moved into the package (`hexcli/assets/`) that
17
+ release, but an existing "Hex CLI" profile kept pointing at the old
18
+ `assets/hexcli.png`; Terminal silently falls back to its own logo on a
19
+ missing file. `install.ps1` now rewrites a stale `hexcli.png` icon path
20
+ in a user's own profile with a targeted text edit, leaving the rest of
21
+ the profile untouched, and the fragment it writes already uses the
22
+ package path.
23
+
24
+ - Releases carry the wheel and the source distribution and go to PyPI
25
+ (`pip install hexcli`): `.github/workflows/publish.yml` runs when a
26
+ release is published, builds both, attaches them and publishes through
27
+ PyPI trusted publishing. 2.8.0's files were attached by hand.
28
+ - README rewritten shorter: the PyPI route sits in Install next to the
29
+ installer, the sample transcript the clips made redundant is gone, the
30
+ configuration table keeps the keys people change, memory and logs are a
31
+ short section under Usage, and the clips use absolute links so they
32
+ render on PyPI, in a two-by-two grid; the edit clip plays faster
33
+ (23 s, was 32 s). Version and CI badges at the top.
34
+ - Paper and CLAUDE.md current through 2.8.0: the timeline row and the
35
+ closing state paragraph record the package and the classifier finding,
36
+ and the milestones table breaks across pages instead of running off
37
+ one (it had been overflowing page 9).
38
+ - Paper numbers re-audited against the code and the latest results: the
39
+ window figure shows the 3,696-token server budget and the ~810-token
40
+ history it leaves instead of the disproven 2,600-token cliff; the
41
+ headline is 32/44 at pass^5 (LATEST.md) and 35/48 multi-turn; the
42
+ "9 s per new conversation" figure is retracted as the backend study
43
+ found; a paragraph carries that study's numbers (15.5 tok/s, 0.8 s warm
44
+ first token, polling-off power). ARCHITECTURE.md gets the same pass
45
+ (template in prompts.py, 29 suites, 44 cases, a dated note on the
46
+ pre-Rewind TTFT section) and the README's suite size reads 44.
47
+ - Paper restructured to six pages: the abstract states the v2.8.0 numbers
48
+ and the thesis; the runtime lever (KV rewind, the context re-read) is
49
+ its own section between results and negative results; the status
50
+ section is one paragraph; the milestones table is one line per row; a
51
+ positioning paragraph names what the report sits next to; three
52
+ figures that duplicated prose (chip diagram, prompt anatomy, NPU/CPU
53
+ bars) are gone and their numbers kept in the text. Every figure in the
54
+ paper is unchanged from the audit above.
55
+
9
56
  ## 2.8.0 — 2026-09-12
10
57
 
11
58
  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.8.1
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
@@ -1,60 +1,36 @@
1
1
  # Hex CLI
2
2
 
3
+ [![PyPI](https://img.shields.io/pypi/v/hexcli)](https://pypi.org/project/hexcli/)
4
+ [![CI](https://github.com/NathanL15/Hex-CLI/actions/workflows/ci.yml/badge.svg)](https://github.com/NathanL15/Hex-CLI/actions/workflows/ci.yml)
5
+
3
6
  Hex CLI is a coding agent for the Windows terminal, similar to aider or
4
7
  Claude Code, that runs a local model on the Snapdragon Hexagon NPU. It reads
5
8
  and edits files, runs commands, and checks its own work. The model is
6
9
  Qwen3-4B, served by [npurun](https://github.com/bpbonker/npurun). After
7
10
  setup it works offline.
8
11
 
9
- **Ask something.** The answer streams in, markdown and all, above an input
10
- box that stays on the last rows.
11
-
12
- ![A question answered in bullet points](docs/gifs/ask.gif)
13
-
14
- **Ask about the machine.** Live state comes from a real command, shown as
15
- it runs.
16
-
17
- ![The CPU name read with a PowerShell command](docs/gifs/machine.gif)
18
-
19
- **Change code.** Every file change prints a diff; when you ask for the
20
- tests, they run; `/diff` shows the turn's changes and `/undo` takes them
21
- back, files included.
22
-
23
- ![A docstring added, the tests run, then undone](docs/gifs/edit.gif)
24
-
25
- **Nothing destructive runs unasked.** Commands are classified before they
26
- run; a delete asks first.
27
-
28
- ![A delete command waits for a y/N before running](docs/gifs/safety.gif)
29
-
30
- ```
31
- > the median calc in processor.py is wrong for even-length lists, fix it
32
-
33
- ◆ read_file
34
- ▸ [read] processor.py (lines 1-40 of 40)
35
-
36
- ◆ edit_file
37
- ▸ [edit] processor.py (+2 lines)
38
- ~ processor.py (+3 −1)
39
- @@ -12,4 +12,6 @@
40
- - return sorted(data)[len(data) // 2]
41
- + mid = len(data) // 2
42
- + if len(data) % 2 == 0:
43
- + return (sorted(data)[mid - 1] + sorted(data)[mid]) / 2
44
-
45
- ◆ run_code
46
-
47
- Fixed: even-length lists now average the two middle values. The test file
48
- prints 3.5 for [1, 2, 5, 6].
49
-
50
- ──────────────────────────────────────────────────────────────────
51
- > ask, or / for commands
52
- ──────────────────────────────────────────────────────────────────
53
- context ◔ 21% npu 0% mem 12.5/15.6 GB ~\proj (main)
54
- ```
55
-
56
- Your messages sit on a light band, the input box and status line stay on
57
- the last rows, and the answer streams in above them.
12
+ <table>
13
+ <tr>
14
+ <td width="50%" valign="top">
15
+ <b>Ask something.</b> The answer streams in, markdown and all, above an input box that stays on the last rows.<br><br>
16
+ <img src="https://raw.githubusercontent.com/NathanL15/Hex-CLI/main/docs/gifs/ask.gif" alt="A question answered in bullet points" width="100%">
17
+ </td>
18
+ <td width="50%" valign="top">
19
+ <b>Ask about the machine.</b> Live state comes from a real command, shown as it runs.<br><br>
20
+ <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%">
21
+ </td>
22
+ </tr>
23
+ <tr>
24
+ <td width="50%" valign="top">
25
+ <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>
26
+ <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%">
27
+ </td>
28
+ <td width="50%" valign="top">
29
+ <b>Nothing destructive runs unasked.</b> Commands are classified before they run; a delete asks first.<br><br>
30
+ <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%">
31
+ </td>
32
+ </tr>
33
+ </table>
58
34
 
59
35
  ## Requirements
60
36
 
@@ -66,33 +42,42 @@ Tested on a Snapdragon X Elite. It will not run on x86 machines or on Macs.
66
42
 
67
43
  ## Install
68
44
 
45
+ The installer does everything the machine allows:
46
+
69
47
  ```powershell
70
48
  git clone https://github.com/NathanL15/Hex-CLI
71
49
  cd Hex-CLI
72
50
  .\install.ps1
73
51
  ```
74
52
 
75
- The installer checks the machine, installs the package (`pip install .`,
76
- which gives you the `hex` and `hexcli` commands), downloads npurun, the
77
- model and the small embedding model that semantic memory uses, and runs
78
- `--doctor` when it finishes. It skips steps that are already done, so run
79
- it again after fixing anything it reports. Everything it writes for you
80
- lives in `~\.shellai`.
81
-
82
- It cannot download the QAIRT SDK for you, because Qualcomm does not allow
83
- redistribution. It prints instructions for that step and picks the SDK up on
84
- the next run. See [Setup by hand](#setup-by-hand) if you want to do the
85
- steps yourself.
86
-
87
- The Start Menu shortcut opens Hex in Windows Terminal when it is installed,
88
- through a "Hex CLI" profile the installer registers. It starts in your home
89
- folder; `/cwd <path>` moves into a project, whose `AGENTS.md` then applies. Text selection, copy
90
- and paste work there as in any other tab. Without Windows Terminal the
91
- shortcut uses the classic console, where drag-select is off because Hex
92
- disables QuickEdit (a click would otherwise freeze output); use the window
93
- menu's Edit, Mark to copy there. To have `hex` itself open in Windows
94
- Terminal from any shell, set it as the default terminal in its Settings,
95
- Startup.
53
+ It checks the machine, installs the package, downloads npurun, the model
54
+ and the small embedding model that memory uses, and runs `--doctor` when it
55
+ finishes. It skips steps that are already done, so run it again after
56
+ fixing anything it reports. Everything it writes for you lives in
57
+ `~\.shellai`.
58
+
59
+ The one thing it cannot download is the QAIRT SDK, because Qualcomm does
60
+ not allow redistribution. It prints the instructions for that step and
61
+ picks the SDK up on the next run.
62
+
63
+ Or install from PyPI. The package is the same one the installer uses; you
64
+ install the SDK yourself and the rest downloads itself:
65
+
66
+ ```powershell
67
+ pip install hexcli # the hex and hexcli commands
68
+ hexcli --update # downloads npurun
69
+ hex # the first run downloads the model, about 2.5 GB
70
+ ```
71
+
72
+ `hexcli --doctor` lists what is still missing and the command that fixes
73
+ each item. Every [release](https://github.com/NathanL15/Hex-CLI/releases)
74
+ also carries the wheel and the source distribution.
75
+
76
+ The Start Menu shortcut opens Hex in Windows Terminal, through a profile
77
+ the installer registers, starting in your home folder; `/cwd <path>` moves
78
+ into a project. Without Windows Terminal it uses the classic console, where
79
+ drag-select is off because Hex disables QuickEdit (a click would otherwise
80
+ freeze output); use the window menu's Edit, Mark to copy there.
96
81
 
97
82
  ## Usage
98
83
 
@@ -106,14 +91,11 @@ hexcli --doctor # check the install
106
91
  hexcli --update # refresh npurun (and pull the source in a checkout)
107
92
  ```
108
93
 
109
- In a checkout without the package installed, `python launcher.py` and
110
- `python -m hexcli.agent` are the same two commands.
111
-
112
94
  Piped input is added to the request as context, or used as the request if
113
- there is no argument. Long input is trimmed to fit the context window.
114
- When stdout is not a terminal the answer is printed alone, with no
115
- progress text, so `hexcli "..." > answer.txt` and `hexcli "..." | clip`
116
- work.
95
+ there is no argument. When stdout is not a terminal the answer is printed
96
+ alone, so `hexcli "..." > answer.txt` and `hexcli "..." | clip` work. In a
97
+ checkout without the package installed, `python launcher.py` and
98
+ `python -m hexcli.agent` are the same two commands.
117
99
 
118
100
  ### Commands
119
101
 
@@ -138,10 +120,10 @@ work.
138
120
  | `/doctor` | check the install |
139
121
  | `Esc` | cancel the running step |
140
122
 
141
- You can add your own commands. Put a `.md` file in `.shellai/commands/` in
142
- the project, or `~/.shellai/commands/` for all projects, and `/<filename>`
143
- sends its content as the prompt. `$ARGUMENTS` in the file is replaced with
144
- whatever follows the command.
123
+ You can add your own. Put a `.md` file in `.shellai/commands/` in the
124
+ project, or `~/.shellai/commands/` for all projects, and `/<filename>`
125
+ sends its content as the prompt, with `$ARGUMENTS` replaced by whatever
126
+ follows the command:
145
127
 
146
128
  ```powershell
147
129
  # .shellai/commands/review.md contains: Review $ARGUMENTS for bugs and style issues.
@@ -161,21 +143,15 @@ whatever follows the command.
161
143
  | `Ctrl+V` | paste a block. Nothing is sent until you press Enter |
162
144
  | `Shift+Enter` | new line inside the entry |
163
145
  | `\` then `Enter` | continue on a new line |
164
- | `Ctrl+Plus` `Ctrl+Minus` | text size in the classic console |
165
146
 
166
- History is saved in `~/.shellai/input_history`. When stdin is not a
167
- terminal the agent falls back to plain `input()`, so pipes and CI work.
168
-
169
- The input box stays on the last rows of the window, and the conversation
170
- scrolls up above it. The status line under it
171
- shows how full the context is, the NPU load (the counter behind Task
172
- Manager's NPU graph), memory in use, and the working directory and branch.
173
- Set `status_bar` to `false` for the old inline prompt.
147
+ The status line under the input box shows how full the context is, the
148
+ NPU load, memory in use, and the working directory and branch. Set
149
+ `status_bar` to `false` for a plain inline prompt.
174
150
 
175
151
  ### Project instructions
176
152
 
177
153
  If a project has an `AGENTS.md`, the agent reads it every turn. Keep it
178
- under about 1,200 characters. The model has a 4K token context and your
154
+ under about 1,200 characters: the model has a 4K token context and your
179
155
  request has to fit in there too.
180
156
 
181
157
  ### Tools
@@ -186,9 +162,19 @@ request has to fit in there too.
186
162
  for the full signatures.
187
163
 
188
164
  `edit_file` tries an exact match first, then a whitespace-tolerant match,
189
- then a close match if there is exactly one. If the match is ambiguous it
190
- returns an error rather than guessing. `read_file` reads large files in
191
- pages. Every file change prints a diff and can be reverted with `/undo`.
165
+ then a close match if there is exactly one; an ambiguous match returns an
166
+ error rather than a guess. `read_file` reads large files in pages. Every
167
+ file change prints a diff and can be reverted with `/undo`.
168
+
169
+ ### Memory and logs
170
+
171
+ Memory is two local stores, one per project and one global, built on
172
+ MiniLM embeddings. When idle, the agent condenses recent turns into short
173
+ notes that later prompts include; `/memory` shows what is stored.
174
+
175
+ Each session is written to `~/.shellai/chatlog/` as a JSONL file: every
176
+ request, every message sent to the model, every reply with its latency,
177
+ every tool call with its output. Secrets in the config are redacted.
192
178
 
193
179
  ## Safety
194
180
 
@@ -196,18 +182,16 @@ Commands are classified before they run:
196
182
 
197
183
  | Level | Examples | Behaviour |
198
184
  |---|---|---|
199
- | destructive | `Remove-Item`, `git reset --hard`, `format-*`, `iex` | asks first |
185
+ | destructive | `Remove-Item`, `git reset --hard`, `Format-Volume`, `iex` | asks first |
200
186
  | sensitive | ssh/gpg/aws keys, hosts file, registry hives, credential stores, `-EncodedCommand` | asks first, denied when non-interactive |
201
187
  | safe | `Get-*`, `ls`, `git status` | runs |
202
188
  | caution | anything else | runs |
203
189
 
204
190
  File writes stay inside the working directory unless you widen the scope
205
191
  with `workspace_write_allow`. Reads can go anywhere. Key and credential
206
- paths are blocked for all file tools.
207
-
208
- `fetch_url` is the only tool that touches the network. It asks before every
209
- fetch, and is refused when non-interactive. Set `network_access` to
210
- `"deny"` to remove the tool or `"allow"` to skip the prompt.
192
+ paths are blocked for all file tools. `fetch_url` is the only tool that
193
+ touches the network; it asks before every fetch and is refused when
194
+ non-interactive.
211
195
 
212
196
  Each classified command is appended to `.shellai/audit.log`. Text inside
213
197
  files and tool output is treated as data, not instructions. A 4B model does
@@ -217,45 +201,20 @@ doing so.
217
201
  ## Configuration
218
202
 
219
203
  Config is optional. `~\.shellai\shellai.json` and `.shellai/config.json`
220
- in a project are merged over the defaults, so you only need to write the
221
- keys you change. `shellai.example.json` lists every key with its default,
222
- and `/setup` writes the file for you.
204
+ in a project are merged over the defaults, so you only write the keys you
205
+ change. The ones people change:
223
206
 
224
207
  | Key | Default | Effect |
225
208
  |---|---|---|
226
209
  | `max_agent_steps` | `15` | tool calls per turn |
227
- | `live_streaming` | `true` | show the answer as it arrives |
228
- | `rich_input` | `true` | history, Tab completion, multi-line paste |
229
- | `side_padding` | `2` | left margin in columns |
230
- | `status_bar` | `true` | input box and status line at the bottom |
231
- | `user_highlight` | `true` | light band behind your messages in the transcript |
232
210
  | `show_diffs` | `true` | print a diff after each file change |
211
+ | `status_bar` | `true` | input box and status line at the bottom |
233
212
  | `workspace_write_scope` | `true` | keep writes inside the working directory |
234
- | `autopilot_confirm_sensitive` | `true` | ask before touching keys and credentials |
235
- | `network_access` | `"ask"` | `"deny"` or `"allow"` |
236
- | `require_verification` | `true` | ask the agent to check its own edits |
237
- | `prompt_split` | `true` | answer plain questions without the tool loop |
238
- | `escalation_local_model` | `""` | a larger local model to consult when stuck |
213
+ | `network_access` | `"ask"` | `"deny"` removes `fetch_url`, `"allow"` skips the prompt |
239
214
  | `memory_enabled` | `true` | semantic memory |
240
- | `chat_log_enabled` | `true` | write full session logs |
241
- | `protocol` | `"v1"` | `"v2"` is experimental |
242
215
 
243
- ## Logs and memory
244
-
245
- Each session is written to `~/.shellai/chatlog/` as a JSONL file: every
246
- request, every message sent to the model, every reply with its latency,
247
- every tool call with its output. Secrets in the config are redacted.
248
- `/stats` prints the path of the current file.
249
-
250
- ```powershell
251
- python tools/chatlog_report.py # summary across all sessions
252
- python tools/chatlog_report.py --last # replay the most recent session
253
- python tools/chatlog_report.py --session 1a2b # replay one session by id prefix
254
- ```
255
-
256
- Memory is two local stores, one per project and one global, built on MiniLM
257
- embeddings. When idle, the agent condenses recent turns into short notes
258
- that are included in later prompts. `/memory` shows what is stored.
216
+ `shellai.example.json` lists every key with its default, and `/setup`
217
+ writes the file for you.
259
218
 
260
219
  ## Setup by hand
261
220
 
@@ -264,7 +223,7 @@ These are the steps `install.ps1` performs. `--doctor` checks each one.
264
223
  **1. The package**
265
224
 
266
225
  ```powershell
267
- pip install . # from the checkout: the hex and hexcli commands, numpy, onnxruntime
226
+ pip install hexcli # or, from the checkout: pip install .
268
227
  ```
269
228
 
270
229
  `ruff` is optional and enables the `lint_code` tool.
@@ -286,22 +245,26 @@ setx ADSP_LIBRARY_PATH "C:\Qualcomm\AIStack\QAIRT_2.50.0\lib\hexagon-v73\unsigne
286
245
  If `ADSP_LIBRARY_PATH` is missing, npurun crashes with
287
246
  `STATUS_STACK_BUFFER_OVERRUN`.
288
247
 
289
- **3. npurun and a model**
290
-
291
- The NPU server is a fork of npurun, at
292
- [NathanL15/npurun](https://github.com/NathanL15/npurun) on the
293
- `hexcli-fork` branch. Each release there has a prebuilt `npurun-arm64.exe`.
294
- Hex CLI expects one specific build, set by `REQUIRED_NPURUN` in
295
- `hexcli/launcher.py`. `--doctor` fails on an older build and `--update`
296
- replaces it (the download goes to `~\.shellai\bin`).
248
+ **3. npurun and the model**
297
249
 
298
- To build from source, use the fork. It has the prompt cache rewind, usage
299
- reporting, exact `max_tokens`, and request watchdog that Hex CLI relies on.
250
+ The NPU server is a fork of npurun at
251
+ [NathanL15/npurun](https://github.com/NathanL15/npurun), branch
252
+ `hexcli-fork`, with a prebuilt `npurun-arm64.exe` on each release. Hex CLI
253
+ expects one specific build, set by `REQUIRED_NPURUN` in
254
+ `hexcli/launcher.py`; `hexcli --update` downloads it to `~\.shellai\bin`
255
+ and `--doctor` fails on an older one. The fork adds the prompt cache
256
+ rewind, usage reporting, exact `max_tokens` and the request watchdog that
257
+ Hex CLI relies on, so upstream npurun will not do. To build it yourself:
300
258
 
301
259
  ```powershell
302
260
  git clone -b hexcli-fork https://github.com/NathanL15/npurun
303
261
  cd npurun
304
262
  cmd /c "scripts\dev-shell-local.bat cargo install --path crates\npurun-cli"
263
+ ```
264
+
265
+ The model downloads on the first `hex` run, or by hand:
266
+
267
+ ```powershell
305
268
  npurun pull qwen3-4b-instruct-2507 # about 2.5 GB
306
269
  ```
307
270
 
@@ -330,7 +293,7 @@ curl -L -o ~\.shellai\onnx\tokenizer.json https://huggingface.co/sentence-transf
330
293
 
331
294
  ## Development
332
295
 
333
- CI runs `ruff` and 25 offline test suites against a mock backend, so no NPU
296
+ CI runs `ruff` and 29 offline test suites against a mock backend, so no NPU
334
297
  is needed for those:
335
298
 
336
299
  ```powershell
@@ -342,14 +305,15 @@ python evals/test_lineedit.py
342
305
 
343
306
  The live evals need the NPU server. They check what the model actually did,
344
307
  by looking at the filesystem and the answer, and run each case several
345
- times because the model is not deterministic.
308
+ times because the model is not deterministic:
346
309
 
347
310
  ```powershell
348
311
  python evals/cases_smoke.py # quick check
349
- python evals/cases_extended.py --runs 3 # 41 cases
312
+ python evals/cases_extended.py --runs 3 # 44 cases
350
313
  python evals/cases_multiturn.py --runs 3 --think-time 15
351
314
  python evals/compare.py <before.json> <after.json>
352
315
  python evals/gate.py --baseline <base.json> <candidate.json>
316
+ python tools/chatlog_report.py --last # replay the most recent session
353
317
  ```
354
318
 
355
319
  Restart the NPU server before each suite. After an hour or two of steady
@@ -3,4 +3,4 @@
3
3
  # The one place the version is written. pyproject.toml reads it (hatch
4
4
  # dynamic version), agent.VERSION re-exports it, and CI refuses a release
5
5
  # tag that does not match it.
6
- __version__ = "2.8.0"
6
+ __version__ = "2.8.1"
@@ -270,7 +270,7 @@ def _maybe_auto_compact(
270
270
  The full summary is suppressed (quiet=True); only a one-line notice prints.
271
271
 
272
272
  Thrash guard: before 2.5.0's window-derived budget, a 2,200-token prompt
273
- clamped the history budget to the 250-token floor (now ~850 against a
273
+ clamped the history budget to the 250-token floor (now ~800 against a
274
274
  3,696-token server budget), and the compacted tail usually exceeded it —
275
275
  so v2.2 re-fired every single turn, shredding the condensed block a little
276
276
  further each time while freeing almost nothing (the user-reported "by the
@@ -400,6 +400,23 @@ if (-not $NoStartMenu) {
400
400
  if ($useTerminal) {
401
401
  $wtSettings = Join-Path $env:LOCALAPPDATA "Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json"
402
402
  $hasOwnProfile = (Test-Path $wtSettings) -and ((Get-Content $wtSettings -Raw) -match '"name"\s*:\s*"Hex CLI"')
403
+ if ($hasOwnProfile -and (Test-Path $iconPng)) {
404
+ # The user's own copy of the profile is left alone, except
405
+ # for an icon path that no longer exists (2.8.0 moved the
406
+ # icon into the package): Terminal falls back to its own
407
+ # logo on a missing file. A targeted text edit, so the
408
+ # user's comments and formatting survive.
409
+ $raw = Get-Content $wtSettings -Raw
410
+ $m = [regex]::Match($raw, '"icon"\s*:\s*"((?:[^"\\]|\\.)*hexcli\.png)"')
411
+ if ($m.Success) {
412
+ $current = $m.Groups[1].Value.Replace('\\', '\')
413
+ if (-not (Test-Path $current)) {
414
+ $raw = $raw.Replace($m.Groups[1].Value, $iconPng.Replace('\', '\\'))
415
+ [IO.File]::WriteAllText($wtSettings, $raw, (New-Object Text.UTF8Encoding $false))
416
+ Write-Ok "Windows Terminal profile icon repaired: $iconPng"
417
+ }
418
+ }
419
+ }
403
420
  if (-not $hasOwnProfile) {
404
421
  $fragmentDir = Join-Path $env:LOCALAPPDATA "Microsoft\Windows Terminal\Fragments\Hex CLI"
405
422
  New-Item -ItemType Directory -Force -Path $fragmentDir | Out-Null
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
File without changes