@sunilaleti/hx 0.0.0-stage → 1.0.0
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.
- package/CHANGELOG.md +1138 -0
- package/README.md +1341 -2
- package/dist/hx.js +91620 -0
- package/dist/hx.js.map +7 -0
- package/package.json +55 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,1138 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
What shipped in each released version of HX, newest first.
|
|
4
|
+
|
|
5
|
+
This file is the release record and part of the product: it ships inside the
|
|
6
|
+
package, `hx changelog` prints it, and a session can answer "what's new?" or
|
|
7
|
+
"which version added web search?" from it rather than from a guess. `hx docs`
|
|
8
|
+
prints the manual next to it.
|
|
9
|
+
|
|
10
|
+
The rules, enforced by `tests/e2e/test_cli.py` so a release cannot forget them:
|
|
11
|
+
|
|
12
|
+
- Every released version has a `## [x.y.z] - YYYY-MM-DD` section here, and the
|
|
13
|
+
version in `package.json` always has one. Bumping the version without
|
|
14
|
+
writing the section fails the suite before the tag exists.
|
|
15
|
+
- Work that has landed but is not tagged lives under `## [Unreleased]`. At
|
|
16
|
+
release time that heading is renamed to the new version and dated, and a
|
|
17
|
+
fresh empty `## [Unreleased]` takes its place.
|
|
18
|
+
- Entries say what a user can now do, in one line each, naming the command,
|
|
19
|
+
setting or key that reaches the feature. A changelog nobody can act on is a
|
|
20
|
+
git log with extra steps.
|
|
21
|
+
|
|
22
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
23
|
+
the project follows [semantic versioning](https://semver.org/).
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## [Unreleased]
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## [1.0.0] - 2026-10-31
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- The TypeScript build is the release: install it with `npm install --global @sunilaleti/hx`,
|
|
36
|
+
and run the same command again to update.
|
|
37
|
+
- With nothing signed in, `hx` opens straight into the TUI, under the HX logo, and says to type
|
|
38
|
+
`/login`, instead of asking for an OpenRouter key in the shell before anything is on screen.
|
|
39
|
+
A model whose route is not signed in says to `/login` or pick another with `/model`, and the
|
|
40
|
+
first message after signing in goes through.
|
|
41
|
+
|
|
42
|
+
### Removed
|
|
43
|
+
|
|
44
|
+
- `hx upgrade`. Update by running the install command again.
|
|
45
|
+
- The beta channel (`@sunilaleti/hx@next`). Every version is a stable release on `latest`.
|
|
46
|
+
- The Python build. `hx-cli` on PyPI stops at 0.2.20 and gets no more releases. To move over,
|
|
47
|
+
run `uv tool uninstall hx-cli` and install from npm: `~/.hx` carries over as it is.
|
|
48
|
+
`install.sh` now checks for Node.js 22.15 or newer and installs from npm.
|
|
49
|
+
- The `live` tests, which drove the Python build's internals against real APIs.
|
|
50
|
+
|
|
51
|
+
### Security
|
|
52
|
+
|
|
53
|
+
- A server in a project's `.hx/mcp.json` no longer starts by itself: a cloned repository could
|
|
54
|
+
run any command the moment `hx` opened in it. HX names each waiting server and what it runs;
|
|
55
|
+
`hx mcp approve <server>` starts it, and `hx mcp add` approves what it adds. An approval is for
|
|
56
|
+
that command, so a server whose command changes waits again. Existing project servers need
|
|
57
|
+
approving once.
|
|
58
|
+
- Read, Grep, Glob, Symbols, Write and Edit ask before touching `~/.ssh`, `~/.aws`, `~/.gnupg`,
|
|
59
|
+
`~/.kube`, `~/.config/gh`, `~/.docker/config.json`, `~/.netrc`, `~/.npmrc`, `~/.pypirc` and
|
|
60
|
+
HX's `auth.json`, in every mode and through symlinks. The sandbox kept the shell out of them,
|
|
61
|
+
but these tools run outside it. An allow rule naming the file opens it.
|
|
62
|
+
- A command hidden inside another no longer passes as read-only: `cat <(rm -rf src)`, `>(...)`,
|
|
63
|
+
zsh's `=(...)` and glob qualifiers, an `awk` program that pipes to or reads from a command,
|
|
64
|
+
`@include`, and `find -fprint0` all ask now.
|
|
65
|
+
- A deny rule holds however the command is spelled: behind `NAME=value` assignments, by full
|
|
66
|
+
path, or behind a wrapper such as `nohup`, `timeout`, `sudo` or `xargs`. An allow rule no
|
|
67
|
+
longer covers a command with assignments it does not name.
|
|
68
|
+
- `acceptEdits` covers only the project: a write outside it, through a symlink that leads out
|
|
69
|
+
of it, or into its `.git` or `.hx` folder asks.
|
|
70
|
+
- `$HX_HOME` (`~/.hx`), which holds every transcript, is made private to its owner (`0700`).
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- Reads that come before a write in one turn run concurrently, as reads elsewhere do.
|
|
75
|
+
- A login shell that does not speak POSIX sh - csh, tcsh, fish - no longer makes every command
|
|
76
|
+
wait out its timeout: commands run in bash.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## [0.3.0-beta.1] - 2026-10-31
|
|
81
|
+
|
|
82
|
+
### Changed
|
|
83
|
+
|
|
84
|
+
- HX is rewritten in TypeScript and runs on Node.js 22.15 or newer. Install it with
|
|
85
|
+
`npm install --global @sunilaleti/hx@next`, and `hx upgrade` updates it through npm. The
|
|
86
|
+
commands, keys, screens, settings and `~/.hx` are the same, so a session started on the Python
|
|
87
|
+
build resumes here and the other way round.
|
|
88
|
+
- The Symbols tool takes its grammars from `npm install --global @vscode/tree-sitter-wasm`.
|
|
89
|
+
|
|
90
|
+
### Fixed
|
|
91
|
+
|
|
92
|
+
- An OpenRouter reply that broke off after its first words is no longer retried from the start,
|
|
93
|
+
which showed those words twice.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## [0.2.20] - 2026-10-26
|
|
98
|
+
|
|
99
|
+
### Changed
|
|
100
|
+
|
|
101
|
+
- A tool call is one row with no background: a coloured marker shows how it ended, and what came
|
|
102
|
+
back hangs under it from a `⎿`, for example `● grep TODO` over `⎿ 3 results`. A call that
|
|
103
|
+
gave nothing back takes half the rows it did, one with a summary a row less, and a failed call
|
|
104
|
+
is still the red `✗`. Output too long for the row wraps on that edge rather than being cut at
|
|
105
|
+
the side of the screen.
|
|
106
|
+
- Tool calls are a step quieter than the answer, and what they gave back a step quieter again,
|
|
107
|
+
so the answer is what you read first. A shell command is no longer bold. An approval still
|
|
108
|
+
shows the command at full strength. A theme sets the new levels with `toolTitle`,
|
|
109
|
+
`toolArgs` and `toolOutput`; a theme without `toolArgs` keeps its `accent` for them. The
|
|
110
|
+
`ansi` theme has only the terminal's own two greys, so there a tool header stays at the
|
|
111
|
+
answer's strength and only its arguments and output step down.
|
|
112
|
+
- `toolPendingBg` no longer colours anything, and a theme no longer needs it. A pi theme that
|
|
113
|
+
sets it still loads.
|
|
114
|
+
|
|
115
|
+
### Fixed
|
|
116
|
+
|
|
117
|
+
- `"theme"` in settings and `HX_THEME` choose the palette at startup again. Every session
|
|
118
|
+
opened in `dark` until `/theme` was run.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## [0.2.19] - 2026-10-22
|
|
123
|
+
|
|
124
|
+
### Added
|
|
125
|
+
|
|
126
|
+
- On a ChatGPT or Claude subscription, the status bar has a usage line: how much of each usage
|
|
127
|
+
window is spent and when it renews, for example
|
|
128
|
+
`5h 16% (renews 06:30 am) | 7d 14% (renews Mon Oct 26 04:00 am)`. A window turns yellow at 75%
|
|
129
|
+
and red at 90%, and drops to 0% by itself when it renews. `/cost` lists the same windows.
|
|
130
|
+
- With an older Claude Code, which gives no percentage below its warning level, a window shows
|
|
131
|
+
only when it renews, for example `5h (renews 06:30 am)`.
|
|
132
|
+
|
|
133
|
+
### Fixed
|
|
134
|
+
|
|
135
|
+
- A ChatGPT plan that is out of usage is no longer retried three times. HX says which window
|
|
136
|
+
is spent and when it resets.
|
|
137
|
+
- On a narrow terminal, the status bar drops the token counts and the cache field before the
|
|
138
|
+
context gauge. It no longer cuts a field in the middle of a number.
|
|
139
|
+
- A Claude usage limit message gives its reset time in the same form as the usage line.
|
|
140
|
+
- A resumed session can edit the files it read before. `hx resume` and `/resume` used to
|
|
141
|
+
forget every read, so the model saw a file in its history and still had its edit refused
|
|
142
|
+
with "has not been read in this session". A file changed since that read is still refused
|
|
143
|
+
until the model reads it again.
|
|
144
|
+
- After `/clear`, the new session can no longer edit a file that only the previous session read.
|
|
145
|
+
- `/rewind` keeps the reads from before the prompt it goes back to. An edit to a file that was
|
|
146
|
+
read earlier and then restored is no longer refused.
|
|
147
|
+
- A subagent's reads count for the subagent only. The main session must read a file itself
|
|
148
|
+
before it edits it.
|
|
149
|
+
- The refusal of an edit to an unread file tells the model to use the Read tool, because a
|
|
150
|
+
file shown by a shell command does not count.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## [0.2.18] - 2026-10-18
|
|
155
|
+
|
|
156
|
+
### Security
|
|
157
|
+
|
|
158
|
+
- Edit and Write no longer leave a private file's new content readable by other users while
|
|
159
|
+
they write it. The temporary file now gets the file's own mode before any content goes in.
|
|
160
|
+
|
|
161
|
+
### Fixed
|
|
162
|
+
|
|
163
|
+
- An edit through a symlink says which file it changes. The approval reads "config.yml is a
|
|
164
|
+
link to ~/shared/config.yml", and so does the answered approval and the result the model
|
|
165
|
+
gets. A link into a missing directory is refused in words, not with a raw error.
|
|
166
|
+
- The transcript names a symlink by its own name, not by the name of its target.
|
|
167
|
+
- A failed tool's error wraps in the transcript instead of being cut at the window's edge, so
|
|
168
|
+
a reason that comes after a long path stays readable.
|
|
169
|
+
- An edit to a CRLF file that differs only in line endings is refused as identical. It used to
|
|
170
|
+
be reported as applied, with nothing changed.
|
|
171
|
+
- Counts of one read in the singular when a limit leaves something out: "1 more line",
|
|
172
|
+
"1 more match", "1 more result". So do the other counts that still had "1 lines" forms.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## [0.2.17] - 2026-10-18
|
|
177
|
+
|
|
178
|
+
### Security
|
|
179
|
+
|
|
180
|
+
- The API keys HX itself signs in with (`HX_OPENROUTER_API_KEY`, `OPENROUTER_API_KEY`, and the
|
|
181
|
+
Tavily and Devin keys) are no longer passed to the commands it runs. An auto-approved `env`
|
|
182
|
+
used to put them straight into the model's context.
|
|
183
|
+
- `xxd -r`, `xxd in out`, `uniq in out` and `sort --compress-program` now ask before they run.
|
|
184
|
+
They write files or run a program, but they were auto-approved as read-only commands.
|
|
185
|
+
- A long option cut short now counts as the full option, as getopt_long and git read it.
|
|
186
|
+
`sort --compress=sh`, `sort --out=f`, `git grep --open-files-in=sh` and `git branch --del`
|
|
187
|
+
were auto-approved as read-only. So were `xxd in -p` and macOS `uniq in -c`, which write a
|
|
188
|
+
file named after the flag.
|
|
189
|
+
|
|
190
|
+
### Fixed
|
|
191
|
+
|
|
192
|
+
- A command that reads stdin (`cat`, `read`, `ssh host ls`) no longer breaks the shell. It used
|
|
193
|
+
to swallow every later command, which then reported success without running. Commands now
|
|
194
|
+
get end-of-file on stdin, and a command with an unclosed quote fails on its own.
|
|
195
|
+
- Background jobs no longer read from the terminal, so a dev server cannot take keystrokes
|
|
196
|
+
from the prompt.
|
|
197
|
+
- Background jobs get `HX=1` and `TERM=dumb`, the same as other commands. A tool that colours
|
|
198
|
+
its output for a real terminal used to write escape codes into the job's log.
|
|
199
|
+
- Non-ASCII command output no longer shows `�` where a read split a character.
|
|
200
|
+
- An edit to a CRLF file keeps CRLF on every line. Each edit used to rewrite the whole file
|
|
201
|
+
with LF, and the diff did not show it. `old_string` with plain newlines now matches there.
|
|
202
|
+
- A hashline replacement that ends with a newline no longer adds a blank line after it.
|
|
203
|
+
- Edit and Write keep a file's mode, so a script stays executable, and write through a
|
|
204
|
+
symlink instead of replacing it with a regular file.
|
|
205
|
+
- With `--cwd`, or `hx resume` from another directory, the approval shows the diff of the
|
|
206
|
+
file the edit will change. It used to read a relative path from the shell's directory.
|
|
207
|
+
- An edit's `+N -M` and its diff now include changed lines that start with `--` or `++`,
|
|
208
|
+
such as SQL comments.
|
|
209
|
+
- "Always allow" on a command HX cannot split into parts, such as a heredoc with an
|
|
210
|
+
apostrophe, is now honoured in later sessions instead of asking again.
|
|
211
|
+
- Counts of one read in the singular: "read 1 line", "1 result", "1 match", "1 tool".
|
|
212
|
+
- Write reports the number of lines the way Read numbers them. A file ending in a newline
|
|
213
|
+
was reported one line longer.
|
|
214
|
+
- The status bar shortens a long project path from its start, so the project's name and
|
|
215
|
+
branch stay visible. Before the first request, the cache field reads "no cache".
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## [0.2.16] - 2026-10-13
|
|
220
|
+
|
|
221
|
+
### Fixed
|
|
222
|
+
|
|
223
|
+
- The "Working…" status and the tool spinners now keep moving while a tool runs. They
|
|
224
|
+
used to vanish or freeze from the moment the model asked for a tool until its next reply,
|
|
225
|
+
so a long command looked like an idle session. The clock now counts the whole turn, not
|
|
226
|
+
only the time since the last model reply.
|
|
227
|
+
- A tool call waiting on an approval shows `?` and does not spin, and its "Took" counts
|
|
228
|
+
only the run, not the time you spent deciding. A denied call never ran, so it shows no
|
|
229
|
+
"Took" at all.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## [0.2.15] - 2026-10-04
|
|
234
|
+
|
|
235
|
+
### Changed
|
|
236
|
+
|
|
237
|
+
- `/login` and `hx auth login` list the accounts as OpenAI, Claude, OpenRouter, Devin. At the
|
|
238
|
+
`hx auth login` menu, Enter still picks OpenRouter.
|
|
239
|
+
|
|
240
|
+
### Fixed
|
|
241
|
+
|
|
242
|
+
- `/login claude-code` behind a network whose TLS inspection signs with a certificate not
|
|
243
|
+
valid for servers ("unsupported certificate purpose") now gives Claude Code's reason and
|
|
244
|
+
the fix: have the network fixed or exempted, or set `HX_SSL_NO_VERIFY=1` meanwhile. It
|
|
245
|
+
used to give the whole sign-in URL and no fix. Turns and HX's own requests that fail the
|
|
246
|
+
same way give the same advice, not "add the CA", which is trusted already.
|
|
247
|
+
- A `HX_CA_BUNDLE`, `SSL_CERT_FILE` or `REQUESTS_CA_BUNDLE` that names a file holding no
|
|
248
|
+
certificates no longer crashes HX at startup. HX says so and verifies against the system
|
|
249
|
+
trust store, and does not hand the file to Claude Code.
|
|
250
|
+
- A wrapped error notice no longer shows a bright green background under every line after
|
|
251
|
+
the first.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## [0.2.14] - 2026-10-04
|
|
256
|
+
|
|
257
|
+
### Added
|
|
258
|
+
|
|
259
|
+
- When the installed Claude Code is behind its current release - and so lists older Claude
|
|
260
|
+
models than your subscription can run - `/model` says so under the list, with the command
|
|
261
|
+
that updates it (`brew upgrade --cask claude-code` for a Homebrew install, otherwise
|
|
262
|
+
`claude update`). After updating, the next `/model` lists the new models; no
|
|
263
|
+
refresh needed.
|
|
264
|
+
|
|
265
|
+
### Changed
|
|
266
|
+
|
|
267
|
+
- The model catalogue is refreshed from inside the `/model` picker: press `ctrl+r`. The
|
|
268
|
+
list updates in place and keeps your filter and selection; the outcome, or why the refresh
|
|
269
|
+
failed, shows under the list. With no models at all, `/model` now opens anyway and says
|
|
270
|
+
so, so `ctrl+r` is there to fill it.
|
|
271
|
+
|
|
272
|
+
### Removed
|
|
273
|
+
|
|
274
|
+
- `/models refresh`. It sat one letter away from `/model` and did nothing without its one
|
|
275
|
+
argument; use `ctrl+r` in the `/model` picker.
|
|
276
|
+
|
|
277
|
+
### Fixed
|
|
278
|
+
|
|
279
|
+
- On the `claude-code` route, the context window is the one Claude Code reports, not a
|
|
280
|
+
200k guess: Opus 5.5 runs with 1M. Claude Code lists its models without their sizes, and
|
|
281
|
+
what it serves depends on the plan, so a model starts at the window it was measured
|
|
282
|
+
serving on every plan - 1M for Opus 5.5, Sonnet 5.5, Sonnet 5 and Opus 4.7, 200k
|
|
283
|
+
otherwise - until its first turn reports the real window and maximum output for your
|
|
284
|
+
account. From then on the context gauge, the compaction threshold, the `/model` picker
|
|
285
|
+
and later sessions all use it, and a catalogue refresh keeps it.
|
|
286
|
+
- A model Claude Code lists at its long context - Fable 5.1 as `claude-fable-5-1[1m]` -
|
|
287
|
+
runs at it: `/model` offers `claude-code/claude-fable-5-1[1m]` at 1M, where HX used to
|
|
288
|
+
drop the `[1m]` and run the model at its standard context.
|
|
289
|
+
- A turn refused because the model, or its 1M context, needs usage credits your Claude plan
|
|
290
|
+
has off no longer reads "usage limit is reached. It resets at ...": HX says what needs the
|
|
291
|
+
credits, where to turn them on, and which model runs without them. Once they are on, the
|
|
292
|
+
next turn runs.
|
|
293
|
+
- After a catalogue refresh (`ctrl+r` in `/model`, or signing in), the session in progress
|
|
294
|
+
measures its context against the refreshed window, not the one it started with.
|
|
295
|
+
- `/model anthropic` (or typing `anthropic` in the picker) lists the Claude models your Claude
|
|
296
|
+
Code subscription runs, first, ahead of the per-token OpenRouter ones; `openai` does the same
|
|
297
|
+
for Codex. Several words match in any order: `/model anthropic sonnet`.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## [0.2.13] - 2026-10-04
|
|
302
|
+
|
|
303
|
+
### Added
|
|
304
|
+
|
|
305
|
+
- Claude Pro/Max route: `hx auth login claude-code`, then
|
|
306
|
+
`hx --model claude-code/claude-sonnet-5`. Runs Claude models on your Claude
|
|
307
|
+
subscription through the official Claude Code CLI, with HX's own tools,
|
|
308
|
+
approvals and transcript. `/model` lists the account's models and `/effort`
|
|
309
|
+
their levels.
|
|
310
|
+
- Behind a TLS-inspecting proxy the `claude-code` route takes the same
|
|
311
|
+
settings as every other route: `HX_CA_BUNDLE` reaches Claude Code as
|
|
312
|
+
`NODE_EXTRA_CA_CERTS` and `HX_SSL_NO_VERIFY` as
|
|
313
|
+
`NODE_TLS_REJECT_UNAUTHORIZED=0`, Claude Code's own `CLAUDE_CODE_CERT_STORE`
|
|
314
|
+
and `CLAUDE_CODE_CLIENT_CERT` / `_KEY` / `_KEY_PASSPHRASE` (for a network
|
|
315
|
+
that requires a client certificate) pass through, and a certificate failure
|
|
316
|
+
it reports names the fix.
|
|
317
|
+
- On the `claude-code` route, a turn Anthropic bills as Extra Usage while
|
|
318
|
+
your plan still has room says so, and names HX's part of the system prompt,
|
|
319
|
+
instead of claiming the plan's usage limit is reached. A turn whose prompt
|
|
320
|
+
carries HX's own system prompt text (a copied `system-prompt.md`, an
|
|
321
|
+
AGENTS.md, an agent definition) is refused before it is sent, naming where
|
|
322
|
+
the text is; `hx prompt` shows the sources.
|
|
323
|
+
|
|
324
|
+
### Fixed
|
|
325
|
+
|
|
326
|
+
- A subagent that fails - a provider error, a refused prompt, the turn limit -
|
|
327
|
+
reports why to the parent model, instead of "(the subagent returned no
|
|
328
|
+
text)".
|
|
329
|
+
- `Glob` with a pattern of `.` or `./` returns an error saying to use `*` or
|
|
330
|
+
`**/*`, and an absolute pattern one naming the `path` and relative pattern
|
|
331
|
+
to send instead, on every Python version, instead of a Python exception.
|
|
332
|
+
- With `tui.enterWhileBusy` set to `"steer"`, `alt+enter` queues a message
|
|
333
|
+
behind the running turn, as the prompt says. It used to steer like `enter`,
|
|
334
|
+
leaving no way to queue in that mode.
|
|
335
|
+
- The status bar's `⧗N queued` count stays visible in a project with a long
|
|
336
|
+
path or branch name; the path is shortened instead of the count being cut off.
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## [0.2.12] - 2026-10-01
|
|
341
|
+
|
|
342
|
+
### Fixed
|
|
343
|
+
|
|
344
|
+
- `/exit` and `ctrl+d` return the shell at once. They used to wait up to 10
|
|
345
|
+
seconds on a model call renaming the session; sessions are no longer renamed
|
|
346
|
+
on the way out, in the TUI or with `hx -p`.
|
|
347
|
+
- A name set with `/title` is no longer replaced by a generated one, including
|
|
348
|
+
one that was still being generated when you set it.
|
|
349
|
+
- The session name is generated from the opening and the latest stretch of the
|
|
350
|
+
conversation, not only its first 2000 characters.
|
|
351
|
+
- A session left before its first name came back is named from your first
|
|
352
|
+
message, instead of being listed by `/resume` with no name.
|
|
353
|
+
- A session left with `/resume` into another directory is renamed for what it
|
|
354
|
+
became, by the HX that starts there, in the background.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## [0.2.11] - 2026-10-01
|
|
359
|
+
|
|
360
|
+
### Fixed
|
|
361
|
+
|
|
362
|
+
- `esc` while a tool runs no longer breaks the session: the next question is
|
|
363
|
+
answered instead of every later turn failing. A session that was interrupted
|
|
364
|
+
or killed mid-tool before this release also carries on after `hx resume`.
|
|
365
|
+
- `esc` during a `Bash` command stops the command. The next `Bash` call gets its
|
|
366
|
+
own output and exit code, not what was left of the one stopped.
|
|
367
|
+
- An interrupted call keeps what it printed, on screen and for the model, and
|
|
368
|
+
reads "Interrupted after <time>" instead of looking like a failure.
|
|
369
|
+
- A tool that finished keeps its result when `esc` lands while its
|
|
370
|
+
`PostToolUse` hook runs.
|
|
371
|
+
- `Bash` shows a short line such as "Starting..." as soon as it is printed,
|
|
372
|
+
rather than only when more output follows.
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## [0.2.10] - 2026-09-29
|
|
377
|
+
|
|
378
|
+
### Fixed
|
|
379
|
+
|
|
380
|
+
- `AGENTS.md` at the repository root now applies when HX is started in a
|
|
381
|
+
subdirectory, followed by each `AGENTS.md` down to that directory.
|
|
382
|
+
- Subagents started with `Task` now follow `~/.hx/AGENTS.md` and the project's
|
|
383
|
+
`AGENTS.md`, as the main session does.
|
|
384
|
+
- A saved `/effort` level shows on the status bar from the first screen of every
|
|
385
|
+
new session, not only after `/effort` is run again.
|
|
386
|
+
- `ctrl+l` switches the model and saves it for later sessions, as `/model` does;
|
|
387
|
+
it used to relabel the status bar only.
|
|
388
|
+
- `/model`, `ctrl+l` and `/effort` open with the cursor on the choice in force.
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## [0.2.9] - 2026-09-27
|
|
393
|
+
|
|
394
|
+
### Added
|
|
395
|
+
|
|
396
|
+
- `~/.hx/AGENTS.md` holds your own instructions and is loaded into every
|
|
397
|
+
session in every project, ahead of the project's `AGENTS.md`. The first
|
|
398
|
+
session creates it empty, ready to fill in. `hx prompt` and `/prompt` now
|
|
399
|
+
name each `AGENTS.md` in force, and `/prompt` says when one has changed on
|
|
400
|
+
disk since the session started.
|
|
401
|
+
|
|
402
|
+
### Fixed
|
|
403
|
+
|
|
404
|
+
- An `AGENTS.md` or system-prompt override that is not valid UTF-8 is skipped
|
|
405
|
+
like an unreadable one, instead of stopping HX from starting.
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## [0.2.8] - 2026-09-27
|
|
410
|
+
|
|
411
|
+
### Changed
|
|
412
|
+
|
|
413
|
+
- `ctrl+z` undoes in the prompt and `ctrl+shift+z` redoes; `cmd+z` and
|
|
414
|
+
`cmd+shift+z` do the same in terminals that pass cmd through (kitty,
|
|
415
|
+
WezTerm). Apple's Terminal sends `ctrl+shift+z` as `ctrl+z`, so there it
|
|
416
|
+
undoes. Suspend no longer has a default key: bind `app.suspend` in
|
|
417
|
+
`keybindings.json`, which now also accepts `cmd+` and `option+`.
|
|
418
|
+
|
|
419
|
+
### Fixed
|
|
420
|
+
|
|
421
|
+
- A markdown table wider than the terminal - pasted into the prompt or in a
|
|
422
|
+
reply - wraps its cells inside their columns instead of cutting them off,
|
|
423
|
+
so every word shows; in a terminal too narrow for its columns it is drawn as
|
|
424
|
+
`header: value` records. A bold phrase or link that wraps keeps its style
|
|
425
|
+
on the next line.
|
|
426
|
+
- A long draft in the prompt wraps between words instead of splitting them
|
|
427
|
+
across rows; only a word longer than the row still breaks inside it. The
|
|
428
|
+
cursor after the last character of a full row no longer hides that
|
|
429
|
+
character.
|
|
430
|
+
- The welcome lines wrap on a narrow terminal instead of being cut off
|
|
431
|
+
mid-word.
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## [0.2.7] - 2026-09-27
|
|
436
|
+
|
|
437
|
+
### Added
|
|
438
|
+
|
|
439
|
+
- `HX_OPENROUTER_BASE_URL` points the OpenRouter route at any OpenAI-compatible
|
|
440
|
+
endpoint - a gateway, a recording proxy.
|
|
441
|
+
- Pressing `esc` on a running turn leaves an "Interrupted" line in the transcript,
|
|
442
|
+
so a reply cut off mid-sentence no longer reads as a finished one.
|
|
443
|
+
- A paste over 10 lines or 1000 characters shows in the prompt as
|
|
444
|
+
`[Pasted text #1 +40 lines]` and is sent in full; the token moves and deletes
|
|
445
|
+
as one character, and survives history recall and `ctrl+k`/`ctrl+y`.
|
|
446
|
+
- `\` then `enter` inserts a newline in any terminal, including Apple's Terminal
|
|
447
|
+
and tmux without extended keys, where `shift+enter` cannot be told from `enter`. `\\` then
|
|
448
|
+
`enter` sends a message that ends in a backslash.
|
|
449
|
+
|
|
450
|
+
### Fixed
|
|
451
|
+
|
|
452
|
+
- `shift+enter` inserts a newline instead of sending the message in kitty, Ghostty,
|
|
453
|
+
WezTerm, iTerm2 and tmux (with `extended-keys on`): HX now asks the terminal for
|
|
454
|
+
the kitty keyboard protocol, falling back to xterm's modifyOtherKeys, and hands
|
|
455
|
+
it back on exit, `ctrl+z` and `/fullscreen`. `ctrl+shift+z` (redo) works there too.
|
|
456
|
+
- `shift+enter` also inserts a newline on macOS in terminals that send it as a
|
|
457
|
+
plain `enter` (VS Code's, Apple's Terminal) or as `option+enter` (the Claude
|
|
458
|
+
desktop app's terminal panel): HX asks macOS which modifiers are held when the
|
|
459
|
+
key arrives. A real `option+enter` still steers, an `enter` hard on the heels of
|
|
460
|
+
a shifted character (`?` then `enter`) still sends, and `HX_MODIFIER_PROBE=0`
|
|
461
|
+
turns the check off.
|
|
462
|
+
- A multi-line paste keeps its line breaks. Terminals send them as carriage
|
|
463
|
+
returns, which HX was deleting, so a pasted table arrived as one long line.
|
|
464
|
+
- Lines typed with `shift+enter` stay separate lines in the transcript instead of
|
|
465
|
+
being joined into one paragraph.
|
|
466
|
+
- On macOS the sandbox now refuses *writes* to credential paths (`~/.ssh`,
|
|
467
|
+
`~/.aws`, `~/.hx/auth.json`, ...) as well as reads, so a session opened in your
|
|
468
|
+
home directory cannot plant an `authorized_keys` entry.
|
|
469
|
+
- On Linux the same holds for a credential path that does not exist yet: a
|
|
470
|
+
session opened in your home directory with no `~/.ssh` could create one and
|
|
471
|
+
plant an `authorized_keys` on the host. HX now creates such a path, empty and
|
|
472
|
+
private (`0700` for a directory, `0600` for a file), and mounts a read-only
|
|
473
|
+
shadow over it. Existing credential directories are shadowed read-only too, so
|
|
474
|
+
a write fails with "Read-only file system" instead of vanishing.
|
|
475
|
+
- Shell commands the model runs show their output in the transcript again - the
|
|
476
|
+
last lines, with `ctrl+o` for the rest - instead of only "Took 12ms".
|
|
477
|
+
- `/context` no longer fails with "'UsageLedger' object has no attribute
|
|
478
|
+
'total_tokens'"; it lists what fills the window.
|
|
479
|
+
- `/copy` copies the last reply and says how it was copied, instead of failing.
|
|
480
|
+
- `/theme light` reports "Theme: light" rather than "Theme: None".
|
|
481
|
+
- `/compact` on a short session says only that there is nothing to compact, no
|
|
482
|
+
longer followed by a "Compacting…" that never finishes; a compaction that
|
|
483
|
+
saves nothing still reports that it finished, and the context gauge keeps
|
|
484
|
+
counting the system prompt afterwards.
|
|
485
|
+
- `/help` lines its key descriptions up however long a binding is, and a long
|
|
486
|
+
row in `/agents`, `/cost` or any other command output wraps under its own
|
|
487
|
+
column instead of at the left edge.
|
|
488
|
+
- The "always allow" option on an approval names where the rule is really
|
|
489
|
+
saved (`~/.hx`), and the answered approval shows a file relative to the
|
|
490
|
+
project rather than as an absolute path.
|
|
491
|
+
- `/permissions` no longer says the same thing twice.
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## [0.2.6] - 2026-09-23
|
|
496
|
+
|
|
497
|
+
### Added
|
|
498
|
+
|
|
499
|
+
- Sign in to remote MCP servers over OAuth. A server that asks for it -
|
|
500
|
+
Atlassian's `https://mcp.atlassian.com/v2/mcp`, for one - shows as needing
|
|
501
|
+
sign-in; `/mcp login <server>` or `hx mcp login <server>` opens the browser
|
|
502
|
+
and the tools arrive without a restart. Tokens refresh on their own;
|
|
503
|
+
`/mcp logout <server>` forgets them. `"oauth": {"clientId": ...}` in
|
|
504
|
+
`mcp.json` covers servers without client registration.
|
|
505
|
+
- MCP servers that change their tool list mid-session have the new list
|
|
506
|
+
swapped in, whether they announce it in a reply or on their own stream.
|
|
507
|
+
- `hx mcp add NAME --url URL --header "Name: value"` saves headers, such as an
|
|
508
|
+
API token, with the server.
|
|
509
|
+
- `/mcp reconnect <server>` restarts one server without restarting HX.
|
|
510
|
+
- `/resume` always offers a list to switch to: this directory's sessions
|
|
511
|
+
first, then every other directory's, each row naming where it lives.
|
|
512
|
+
Picking one from elsewhere restarts HX in that directory on it, with the
|
|
513
|
+
same `--mode` and `--model`. The session already open is no longer listed,
|
|
514
|
+
and the list is no longer cut at 20.
|
|
515
|
+
|
|
516
|
+
### Fixed
|
|
517
|
+
|
|
518
|
+
- `/resume` said "No previous sessions in this directory" in a fresh
|
|
519
|
+
directory, or inside a session resumed from another one, instead of
|
|
520
|
+
listing anything.
|
|
521
|
+
- `hx resume <id>` ran the session in whatever directory you typed it in,
|
|
522
|
+
not the one it was recorded in, so its paths and tool calls pointed at the
|
|
523
|
+
wrong project.
|
|
524
|
+
- `hx mcp add NAME --url URL`, `--user`, and server arguments like `npx -y`
|
|
525
|
+
were refused as unknown options, so none of them could be run.
|
|
526
|
+
- `hx mcp add` dropped headers from the entry it wrote.
|
|
527
|
+
- A remote MCP server that expires its session is initialized again instead
|
|
528
|
+
of failing every call until HX restarts.
|
|
529
|
+
- A multi-line shell command no longer breaks the screen. Its approval shows
|
|
530
|
+
one row per line, the tool call shows the first line and `+N lines`
|
|
531
|
+
(`ctrl+o` shows the rest), and the approval options and prompt are no longer
|
|
532
|
+
drawn several times over.
|
|
533
|
+
- `/resume` from inside a resumed session showed the resumed conversation
|
|
534
|
+
pushed off the top of the screen, leaving only the "Resumed" notice.
|
|
535
|
+
- A session title with a line break in it no longer splits its row in the
|
|
536
|
+
`/resume` picker.
|
|
537
|
+
- Started in your home directory, the status bar named it `~/.`; it now reads
|
|
538
|
+
`~`.
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## [0.2.5] - 2026-09-23
|
|
543
|
+
|
|
544
|
+
### Added
|
|
545
|
+
|
|
546
|
+
- Images as input. `ctrl+v` attaches the image on the clipboard, dragging an
|
|
547
|
+
image file into the terminal attaches it, and `hx -p "..." --image PATH`
|
|
548
|
+
attaches one to a headless prompt. Each shows in the prompt as
|
|
549
|
+
`[Image #N]`.
|
|
550
|
+
- `Read` returns image files as images, so the model can look at a screenshot
|
|
551
|
+
or mockup it is pointed at with `@path`.
|
|
552
|
+
- Images returned by MCP tools reach the model instead of being dropped.
|
|
553
|
+
- Every route carries images: OpenRouter, the Codex subscription and the Devin
|
|
554
|
+
subscription. Large images are scaled and re-encoded to fit each route's
|
|
555
|
+
limits.
|
|
556
|
+
- A model that does not accept images is warned about when one is attached,
|
|
557
|
+
and is told the image was left out rather than sent a request that fails.
|
|
558
|
+
The `/model` picker marks the models that can see images.
|
|
559
|
+
|
|
560
|
+
### Fixed
|
|
561
|
+
|
|
562
|
+
- Background jobs are waited for, not only signalled, when a session closes.
|
|
563
|
+
An unwaited subprocess transport was collected after the event loop had
|
|
564
|
+
gone, which raised `Event loop is closed` as an unraisable warning on CI
|
|
565
|
+
runs that otherwise passed.
|
|
566
|
+
|
|
567
|
+
---
|
|
568
|
+
|
|
569
|
+
## [0.2.4] - 2026-09-19
|
|
570
|
+
|
|
571
|
+
### Added
|
|
572
|
+
|
|
573
|
+
- `/trace [path]`: write the whole session to one self-contained HTML page and
|
|
574
|
+
open it. The system prompt, the project context, the skills index, every tool
|
|
575
|
+
schema, every prompt and reply, every thinking block, every tool call beside
|
|
576
|
+
the result it produced, the per-call token ledger and the files HX changed.
|
|
577
|
+
Late-injected reminders and compacted turns are included behind a toggle.
|
|
578
|
+
A session still running traces as it stands, including the turn that just
|
|
579
|
+
landed. The page follows your system's dark or light setting and a button
|
|
580
|
+
overrides it; nothing is fetched and nothing is uploaded.
|
|
581
|
+
- `hx trace [SESSION_ID] [PATH]`: the same page for any transcript on the
|
|
582
|
+
machine, without starting a session. Defaults to the last session in this
|
|
583
|
+
directory and prints only the path, so `open "$(hx trace)"` works. Either
|
|
584
|
+
argument may be given alone - a lone one that looks like a path is one.
|
|
585
|
+
- The system prompt and the tool schemas are now written to the transcript on a
|
|
586
|
+
session's first provider call, so a trace of a finished session can say what
|
|
587
|
+
the model was told and not only what it said.
|
|
588
|
+
|
|
589
|
+
### Security
|
|
590
|
+
|
|
591
|
+
- Permission rules are matched against the path a specifier resolves to, not
|
|
592
|
+
the text it was written as. `deny: Read(~/.ssh/**)` was got past by asking
|
|
593
|
+
for `../../.ssh/id_rsa`, or through a symlink planted in the project. The
|
|
594
|
+
same resolution stops an allow rule leaking: `allow: Edit(src/**)` used to
|
|
595
|
+
cover `src/../../etc/passwd`.
|
|
596
|
+
- `WebFetch` is matched against every URL it was given rather than the first,
|
|
597
|
+
so `deny: WebFetch(https://host/**)` is no longer a question of list order.
|
|
598
|
+
The approval prompt spells the URLs out too; it said `urls=[2 items]`, which
|
|
599
|
+
answered the only question that mattered with a number.
|
|
600
|
+
- The generated Seatbelt profile no longer grants write access to all of
|
|
601
|
+
`/private/var/folders` - every user's and every process's temp and cache.
|
|
602
|
+
`$TMPDIR` was resolved precisely to avoid that, and the profile handed it
|
|
603
|
+
back on the next line. This user's own container stays writable.
|
|
604
|
+
- `~/.hx/auth.json` is created with mode 0600 rather than narrowed to it after
|
|
605
|
+
the tokens are written, closing a window at a predictable path where the
|
|
606
|
+
credential was readable by any local account.
|
|
607
|
+
- The OAuth callback page escapes what the provider sent. An `error` parameter
|
|
608
|
+
carrying markup ran as HTML on a loopback origin that is otherwise the
|
|
609
|
+
user's own.
|
|
610
|
+
|
|
611
|
+
### Fixed
|
|
612
|
+
|
|
613
|
+
- Slash commands read the session that is in force rather than the one HX
|
|
614
|
+
opened with. After `/resume`, `/trace` wrote a page of zeros into the empty
|
|
615
|
+
startup session's directory and `/cost` reported nobody's usage: the command
|
|
616
|
+
context had captured the session object at startup, and `/resume` rebinds it.
|
|
617
|
+
- A session whose title was the trace template's own data placeholder had the
|
|
618
|
+
whole payload spliced into the page's `<title>`.
|
|
619
|
+
- A trace labelled an encrypted reasoning block "thinking (signed)", which read
|
|
620
|
+
as though the page had dropped the rest of it. It now says the text is the
|
|
621
|
+
provider's summary and the reasoning came back encrypted, and a block that
|
|
622
|
+
arrived with no summary at all says that rather than rendering an empty box.
|
|
623
|
+
- MCP servers survive a large response. One JSON-RPC message is one line and
|
|
624
|
+
asyncio's stream limit is 64 KiB, so a tool returning a file did not truncate
|
|
625
|
+
- it raised out of the read loop and killed the connection for the rest of
|
|
626
|
+
the session. The limit is now 16 MiB, and overshooting it says so.
|
|
627
|
+
- A tool call is no longer dropped when a stream ends without a `finish_reason`.
|
|
628
|
+
The arguments were buffered until that field arrived, so a complete call was
|
|
629
|
+
discarded and the turn reported `end_turn`: the model appeared to stop for no
|
|
630
|
+
reason, and the tokens were already paid for.
|
|
631
|
+
- The Linux sandbox starts again for anyone with credential *files*. Denied
|
|
632
|
+
paths were all shadowed with `--tmpfs`, which mounts a directory and aborts
|
|
633
|
+
bwrap when aimed at a file - and six of them are files, `~/.hx/auth.json`
|
|
634
|
+
among them. Files are shadowed with `/dev/null` instead. bwrap also starts in
|
|
635
|
+
the policy's directory rather than whatever `$PWD` happened to hold.
|
|
636
|
+
- `PersistentShell.cwd` follows the shell. It claimed to track it and was in
|
|
637
|
+
fact the launch directory forever, which meant a background job always
|
|
638
|
+
started where the session opened rather than where `cd` had left it.
|
|
639
|
+
- A background job no longer leaks its log file descriptor for the life of the
|
|
640
|
+
session.
|
|
641
|
+
- A record appended while the transcript was being written is no longer
|
|
642
|
+
dropped. The queue is taken in one step, and a failed write puts its records
|
|
643
|
+
back rather than losing them.
|
|
644
|
+
- An automatic compaction with nothing to summarise is no longer announced and
|
|
645
|
+
rerun every turn for the rest of the session. `/compact` still answers either
|
|
646
|
+
way.
|
|
647
|
+
- `/trace` takes `--no-open`, and never launches a *terminal* browser: with no
|
|
648
|
+
display, `webbrowser` falls through to lynx or w3m, which opens in the
|
|
649
|
+
terminal HX is drawing on and takes the session display with it.
|
|
650
|
+
|
|
651
|
+
---
|
|
652
|
+
|
|
653
|
+
## [0.2.3] - 2026-09-18
|
|
654
|
+
|
|
655
|
+
### Added
|
|
656
|
+
|
|
657
|
+
- Hooks: shell commands HX runs at `PreToolUse`, `PostToolUse`,
|
|
658
|
+
`UserPromptSubmit` and `Stop`. A hook can refuse an action (exit 2, or
|
|
659
|
+
`{"decision": "block"}`), rewrite a tool's arguments before the permission
|
|
660
|
+
engine sees them (`updatedInput`), or add a note for the model
|
|
661
|
+
(`additionalContext`). Configure them under `"hooks"` in
|
|
662
|
+
`~/.hx/settings.json`; `/hooks` lists what is loaded. Hooks declared in a
|
|
663
|
+
project's checked-in `.hx/settings.json` are ignored and reported, because
|
|
664
|
+
cloning a repository must not be enough to run commands on your machine.
|
|
665
|
+
- `Symbols`: outline a file, find where a name is defined, or find where it is
|
|
666
|
+
used, over a tree-sitter parse tree rather than a regex - so a match inside a
|
|
667
|
+
comment or a string never counts. Python, TypeScript, JavaScript, Go and
|
|
668
|
+
Rust. Needs the grammars: `uv tool install "hx-cli[symbols]"`. Without them
|
|
669
|
+
the tool is not registered at all.
|
|
670
|
+
- An example hook, `examples/hooks/check.sh`: after every `Edit`/`Write` it
|
|
671
|
+
runs the project's own checker (`ruff`, `tsc`, `go vet`, `cargo check`)
|
|
672
|
+
against just that file and hands the output back to the model, so a typo is
|
|
673
|
+
caught on the next turn instead of at test time.
|
|
674
|
+
- Line anchors. `Read` now labels each line with a short content hash, and
|
|
675
|
+
`Edit` accepts those anchors in a `hashline` argument to replace a span
|
|
676
|
+
without retyping it. An anchor that no longer resolves means the file moved,
|
|
677
|
+
so the patch is rejected instead of landing at the wrong lines. Set
|
|
678
|
+
`"tools": {"hashline": false}` to go back to plain `cat -n` reads.
|
|
679
|
+
|
|
680
|
+
### Removed
|
|
681
|
+
|
|
682
|
+
- The `telemetry` setting, which was parsed and never read by anything.
|
|
683
|
+
|
|
684
|
+
### Fixed
|
|
685
|
+
|
|
686
|
+
- The README's Status section said there were two model routes; Devin made it
|
|
687
|
+
three in 0.2.2.
|
|
688
|
+
- `MultiEdit` and `NotebookEdit` were listed as mutating tools and as edit
|
|
689
|
+
tools in the permission engine, but no such tools exist. A rule written
|
|
690
|
+
against either name silently matched nothing.
|
|
691
|
+
|
|
692
|
+
---
|
|
693
|
+
|
|
694
|
+
## [0.2.2] - 2026-09-17
|
|
695
|
+
|
|
696
|
+
### Added
|
|
697
|
+
|
|
698
|
+
- Devin subscription as a third route: `hx auth login devin` (or `/login`)
|
|
699
|
+
signs in through the browser, and `devin/<model>` ids run against the plan -
|
|
700
|
+
every model the account includes, listed by `/model` and refreshed with
|
|
701
|
+
`/models refresh`. Reasoning depths Devin ships as separate models are folded
|
|
702
|
+
into one, so `/effort` picks the depth, and router models such as
|
|
703
|
+
`devin/adaptive` are assigned per turn. A session token in `DEVIN_API_KEY`
|
|
704
|
+
works where there is no browser. Devin Enterprise accounts sign in through
|
|
705
|
+
the same command by choosing "Log in with Devin for Enterprise".
|
|
706
|
+
|
|
707
|
+
### Fixed
|
|
708
|
+
|
|
709
|
+
- The `/login` dialog no longer sits on "Starting…" for the whole sign-in: it
|
|
710
|
+
now updates as the flow moves on, and keeps the instructions on screen
|
|
711
|
+
alongside the link, progress and paste field instead of overwriting them.
|
|
712
|
+
|
|
713
|
+
- Resizing the terminal no longer crashes the session. A window manager sends
|
|
714
|
+
a burst of SIGWINCH for one drag of a corner, and a signal landing while the
|
|
715
|
+
previous frame was still writing re-entered stdout: `RuntimeError: reentrant
|
|
716
|
+
call inside <_io.BufferedWriter>`, and HX was gone. The redraw is handed to
|
|
717
|
+
the event loop now, and the burst collapses into one repaint.
|
|
718
|
+
|
|
719
|
+
- `/resume` and `hx resume` bring the whole conversation back, not only its
|
|
720
|
+
text: the reasoning, tool calls, their output and the plan are drawn again.
|
|
721
|
+
A resumed session used to show a blank tinted bar for each tool result and
|
|
722
|
+
nothing at all for the calls themselves.
|
|
723
|
+
|
|
724
|
+
---
|
|
725
|
+
|
|
726
|
+
## [0.2.1] - 2026-09-14
|
|
727
|
+
|
|
728
|
+
### Changed
|
|
729
|
+
|
|
730
|
+
- Code blocks in a reply are drawn as a block - the code on a band of its own
|
|
731
|
+
colour, syntax highlighted, with the language named at the top - instead of
|
|
732
|
+
printing the ``` fences back out as text. Themes set the band with the new
|
|
733
|
+
`mdCodeBlockBg` role, and a theme that leaves colour to the terminal, as
|
|
734
|
+
`ansi` does, gets a rule above and below instead.
|
|
735
|
+
|
|
736
|
+
### Fixed
|
|
737
|
+
|
|
738
|
+
- The model's reasoning is shown again. It was collapsed to a bare `Thinking…`
|
|
739
|
+
with no key wired to open it, so the text never reached the screen. `ctrl+o`
|
|
740
|
+
now folds and unfolds it, the same key that expands a tool call.
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
## [0.2.0] - 2026-09-14
|
|
745
|
+
|
|
746
|
+
### Changed
|
|
747
|
+
|
|
748
|
+
- The interface is drawn into your terminal's own scrollback instead of taking
|
|
749
|
+
over the screen. Finished output belongs to the terminal: scroll it, select
|
|
750
|
+
it with the mouse, copy it, pipe it, and it is still there after HX exits.
|
|
751
|
+
`/fullscreen` takes the whole window back for anyone who preferred it that
|
|
752
|
+
way - the prompt pinned to the bottom row, the transcript scrolling above it
|
|
753
|
+
with `pgup`, `pgdn`, `ctrl+home` and `ctrl+end` - and writes the choice to
|
|
754
|
+
`tui.fullscreen` for the next session. On either screen the prompt and the
|
|
755
|
+
status bar sit on the bottom row from the first frame - a conversation that
|
|
756
|
+
does not fill the window is padded above the dock, not below it, and rows a
|
|
757
|
+
closing picker gives back open above it too. The slash-command and `@`-path
|
|
758
|
+
completion lists draw above the prompt for the same reason: under it, every
|
|
759
|
+
keystroke that changed the number of matches moved the prompt and the status
|
|
760
|
+
bar with it. Exiting erases the whole interface - banner, conversation and
|
|
761
|
+
dock - and puts the shell prompt back on the row HX started on, rather than
|
|
762
|
+
leaving a dead prompt box and a status bar on screen above it, from either
|
|
763
|
+
screen: quitting out of `/fullscreen` also takes away the frame that was
|
|
764
|
+
parked underneath it.
|
|
765
|
+
- Approving a tool call is now a framed prompt with one option per line, each
|
|
766
|
+
saying what it does - `always allow` states that it writes a rule to
|
|
767
|
+
`.hx/settings.local.json`. `y`/`s`/`a`/`n` still answer in one keystroke,
|
|
768
|
+
`esc` denies, and the arrow keys work for anyone who wants to read first.
|
|
769
|
+
The command shown is the one that will run, `$` and all. When two subagents
|
|
770
|
+
stop at an approval at once, the older one holds the keyboard and the other
|
|
771
|
+
says it is waiting rather than offering keys that would answer the first.
|
|
772
|
+
- Your theme reaches the whole interface. Links, list bullets, block quotes,
|
|
773
|
+
horizontal rules and inline code in the model's replies now follow the
|
|
774
|
+
palette instead of a hard-coded set of colours, and headings are visibly
|
|
775
|
+
different from each other.
|
|
776
|
+
- Multi-line output from `/help`, `/cost`, `/status` and the rest lines up
|
|
777
|
+
under its own first line instead of at two different indents.
|
|
778
|
+
- The todo list is a block in the conversation rather than a sidebar, and
|
|
779
|
+
`/todos` and `ctrl+t` show it. A scrollback-native interface has no second
|
|
780
|
+
column to put a sidebar in.
|
|
781
|
+
- Mouse selection is the terminal's, across the whole session including what
|
|
782
|
+
has scrolled off, rather than HX's own within the visible screen.
|
|
783
|
+
- Text HX did not write - a file a tool read, a command's output, the model's
|
|
784
|
+
reply, a paste into the prompt - is drawn as plain text. Escape sequences in
|
|
785
|
+
it are removed rather than sent on to your terminal, so a file cannot set
|
|
786
|
+
your clipboard, rename your window, hide a link behind different words, or
|
|
787
|
+
erase what is already on screen by being read.
|
|
788
|
+
- `/clear` now clears the screen and the scrollback above it, instead of
|
|
789
|
+
emptying the transcript and leaving the last session on screen. Terminals
|
|
790
|
+
that do not implement scrollback erasure - macOS Terminal.app - clear the
|
|
791
|
+
screen only.
|
|
792
|
+
- `/model`, the command palette and every other picker are sized to the
|
|
793
|
+
terminal rather than to a fixed eight rows, so a tall window shows a long
|
|
794
|
+
list instead of leaving two thirds of itself empty.
|
|
795
|
+
- `ctrl+z` suspends HX to the background and resuming redraws it; `ctrl+home`
|
|
796
|
+
and `ctrl+end` reach the ends of the conversation; `ctrl+_` undoes in the
|
|
797
|
+
prompt. All three were listed in `/help` and bound to nothing.
|
|
798
|
+
- `ctrl+p` and `ctrl+l` are named in the hints line under the prompt, which is
|
|
799
|
+
where the keys a session offers are supposed to be discoverable.
|
|
800
|
+
- A block drawn against the prompt no longer closes itself with a second rule,
|
|
801
|
+
which left a rule, a blank line and another rule under every picker and under
|
|
802
|
+
every approval. Whichever block ends up against the dock lets the dock's own
|
|
803
|
+
rule close it, and takes its own back as soon as something lands underneath.
|
|
804
|
+
- The installer now opens with a terminal boot animation that draws and lights
|
|
805
|
+
the HX mark, with a clean static version for CI and redirected output.
|
|
806
|
+
|
|
807
|
+
---
|
|
808
|
+
|
|
809
|
+
## [0.1.9] - 2026-09-11
|
|
810
|
+
|
|
811
|
+
### Changed
|
|
812
|
+
|
|
813
|
+
- Project instructions live in `AGENTS.md`, the cross-agent standard, instead
|
|
814
|
+
of `HX.md`. A repo already carrying an `AGENTS.md` for another coding agent
|
|
815
|
+
is understood by HX with no extra file; `/init` writes one. `HX.md` and
|
|
816
|
+
`.hx/HX.md` are no longer read - rename yours.
|
|
817
|
+
- Python 3.13 and 3.14 are supported in fact, not just by the `>=3.11` floor.
|
|
818
|
+
CI runs the suite on every supported minor, so installing on a current
|
|
819
|
+
interpreter is covered by a test run rather than by hope.
|
|
820
|
+
|
|
821
|
+
### Fixed
|
|
822
|
+
|
|
823
|
+
- `/model` lists the Codex models your ChatGPT account can actually run. HX
|
|
824
|
+
shipped a hard-coded pair — `gpt-5.3-codex` and its spark — that the backend
|
|
825
|
+
now refuses outright, so a paid subscription offered two models and both
|
|
826
|
+
failed. Signing in fetches the account's own list instead; `/models refresh`
|
|
827
|
+
asks again, and the last shipped list stands in when the fetch cannot happen.
|
|
828
|
+
- A Codex model that the account is refused now says so in terms you can act
|
|
829
|
+
on. The backend answers with a 400 naming the model, which reads as a bad
|
|
830
|
+
model id. The message now says entitlement is per model and per account, and
|
|
831
|
+
points at `/models refresh` to get the list that is true for your account.
|
|
832
|
+
- `hx auth` names the plan behind a ChatGPT login (`signed in (plus)`), and
|
|
833
|
+
`/login` says when it is a free one, since Codex is documented as needing a
|
|
834
|
+
paid plan. Neither blocks a model: entitlement is per model, and the fetched
|
|
835
|
+
catalogue is what decides it, so HX reports the plan and lets the backend
|
|
836
|
+
answer.
|
|
837
|
+
- `/model` says what pays for each model. Subscription models showed
|
|
838
|
+
`$0.00/$0.00`, indistinguishable from a free OpenRouter one; they now read
|
|
839
|
+
`subscription`. The status bar tags them too — `gpt-5.6-terra (sub)`.
|
|
840
|
+
- `/resume` counts prompts, not wire-format records. A session you sent one
|
|
841
|
+
prompt to listed as "31 msgs"; it now reads `1 prompt · 31 msgs`, and `/cost`
|
|
842
|
+
says "17 API requests across 1 prompt" so the two numbers reconcile.
|
|
843
|
+
- The status bar follows the branch. It read `git HEAD` once at startup, so
|
|
844
|
+
checking out in an IDE or another terminal left it showing a branch you had
|
|
845
|
+
left; it now re-reads on a timer, worktrees included.
|
|
846
|
+
- Selecting assistant prose or a tool block with the mouse copies it. Textual
|
|
847
|
+
can only extract text from widgets that render plain text, so dragging over
|
|
848
|
+
those blocks highlighted them and copied nothing.
|
|
849
|
+
- Copying goes through `pbcopy`/`wl-copy`/`xclip` before OSC 52. Textual's own
|
|
850
|
+
copy is OSC 52 only, which macOS Terminal ignores and iTerm2 ships disabled,
|
|
851
|
+
so `ctrl+c` on a selection silently did nothing there.
|
|
852
|
+
- The prompt names the steer key the way your keyboard does. It read
|
|
853
|
+
`alt+enter` everywhere, while `/help` two keystrokes away read `option+enter`
|
|
854
|
+
on macOS - the same key, spelled two ways in one window. It now comes from
|
|
855
|
+
the keybinding registry, so it follows the platform and follows a rebind.
|
|
856
|
+
- `ctrl+c` reaches the copy at all. The prompt holds focus for the whole session
|
|
857
|
+
and binds that key itself, so a transcript selection was never what it
|
|
858
|
+
copied. It now copies the selection when there is one, and clears the prompt
|
|
859
|
+
when there is not, exactly as before.
|
|
860
|
+
|
|
861
|
+
### Added
|
|
862
|
+
|
|
863
|
+
- `models.codex_models` in settings adds Codex model ids to the `/model`
|
|
864
|
+
picker, on top of the ones fetched for your account — for an id the backend
|
|
865
|
+
serves but does not advertise, or a machine that cannot reach it.
|
|
866
|
+
`{"models": {"codex_models": ["gpt-5.6-terra"]}}` adds your own.
|
|
867
|
+
- `/effort` picks how hard the current model thinks, from the levels that model
|
|
868
|
+
actually offers, and the status bar shows the depth in force next to the
|
|
869
|
+
model — `gpt-5.6-terra (sub) · high`. It applies to the next turn and is
|
|
870
|
+
saved for later sessions. Switching models re-resolves it, so a choice the
|
|
871
|
+
new model cannot reach shows as the depth it will really run at.
|
|
872
|
+
- `models.reasoning_effort` sets how hard a reasoning model thinks —
|
|
873
|
+
`none` through `ultra`. Left unset, each Codex model runs at the default its
|
|
874
|
+
own catalogue entry names (`low` on GPT-6 Astra, `medium` on GPT-5.6 Terra)
|
|
875
|
+
instead of one fixed depth for every model. A value is clamped to what the
|
|
876
|
+
chosen model advertises, so `max` runs at `xhigh` on GPT-5.5 rather than
|
|
877
|
+
being refused.
|
|
878
|
+
- `/mouse [on|off]` hands drag-selection back to your terminal, for when you
|
|
879
|
+
want its own selection rather than HX's. Holding alt/option while dragging
|
|
880
|
+
does the same thing without the toggle.
|
|
881
|
+
|
|
882
|
+
### Changed
|
|
883
|
+
|
|
884
|
+
- "Always allow" grants move out of your repository. They were written to
|
|
885
|
+
`./.hx/settings.local.json`, with a `./.hx/.gitignore` added to hide them —
|
|
886
|
+
two files of HX's in your checkout. They now live in
|
|
887
|
+
`~/.hx/projects/<project>/settings.local.json`, still scoped to the project,
|
|
888
|
+
and an existing local file is migrated on the next run.
|
|
889
|
+
- Grants that much older versions appended to `./.hx/settings.json` — the file
|
|
890
|
+
a project checks in — are lifted into that same local file on the next run,
|
|
891
|
+
and HX says which ones it moved. Only `allow` moves; a project's `deny` and
|
|
892
|
+
`ask` stay shared. It happens once, so a rule you later write there by hand
|
|
893
|
+
is left alone, and an emptied `./.hx` is removed.
|
|
894
|
+
- HX no longer rewrites `./.hx/settings.json` at all. Its one remaining writer
|
|
895
|
+
there, the startup pass that widens stale whole-command rules, now reports
|
|
896
|
+
them and leaves the file to its owner.
|
|
897
|
+
|
|
898
|
+
---
|
|
899
|
+
|
|
900
|
+
## [0.1.8] - 2026-09-10
|
|
901
|
+
|
|
902
|
+
### Fixed
|
|
903
|
+
|
|
904
|
+
- Publishes everything listed under 0.1.7. That tag was cut but never shipped:
|
|
905
|
+
a type annotation on the transcript's pending-approval list failed the lint
|
|
906
|
+
job, so the release build stopped before it reached PyPI. `hx upgrade` moves
|
|
907
|
+
from 0.1.6 straight to here.
|
|
908
|
+
|
|
909
|
+
---
|
|
910
|
+
|
|
911
|
+
## [0.1.7] - 2026-09-10
|
|
912
|
+
|
|
913
|
+
### Added
|
|
914
|
+
|
|
915
|
+
- **Steer a running turn.** `alt+enter` pushes a message into the turn already
|
|
916
|
+
in flight: the model call is cut off mid-stream and the next one starts from
|
|
917
|
+
what you just said. Tools already running are left to finish, so nothing is
|
|
918
|
+
half-written. With an empty prompt it steers the front of the queue, so a
|
|
919
|
+
correction you already queued need not be retyped. `/queue` lists what is
|
|
920
|
+
waiting, `/queue steer <n>` sends one now, `/queue clear` drops them, and the
|
|
921
|
+
status bar shows the depth. Set `tui.enterWhileBusy` to `"steer"` to swap the
|
|
922
|
+
two keys.
|
|
923
|
+
- **Sessions are renamed as they close.** The name written after the first
|
|
924
|
+
exchange described an opening question; `/resume` now lists what the session
|
|
925
|
+
turned into. Also on `/clear` and `/resume`, which leave a session behind.
|
|
926
|
+
|
|
927
|
+
### Changed
|
|
928
|
+
|
|
929
|
+
- **Approvals happen in the transcript, not in a modal.** The permission prompt
|
|
930
|
+
is now a block at the end of the conversation, directly under the sentence
|
|
931
|
+
where the model said what it intended to do. It takes the keyboard while it is
|
|
932
|
+
unanswered (`y` / `s` / `a` / `n`, `v` for the rest of a clipped diff), and
|
|
933
|
+
answering collapses it into a one-line record of what was granted and how
|
|
934
|
+
widely, so a session shows every rule it accumulated.
|
|
935
|
+
- **More commands are recognised as read-only, and fewer are taken on trust.**
|
|
936
|
+
The allowlist that skips the prompt entirely grew from roughly thirty entries
|
|
937
|
+
to a hundred - digests, `ps`, `readlink`, most text filters, and the read-only
|
|
938
|
+
half of `git` (`rev-parse`, `ls-files`, `cat-file`, `stash list`, and the
|
|
939
|
+
rest). Arguments now decide alongside the name: `sed -i`, `find -exec`,
|
|
940
|
+
`sort -o`, `yq -i`, `git branch -D`, an `awk` program that calls `system()`,
|
|
941
|
+
and `git -c core.pager=…` all still ask. Review it if you run HX on anything
|
|
942
|
+
you would not hand a shell.
|
|
943
|
+
- **"Always allow" writes to `.hx/settings.local.json`.** Grants no longer land
|
|
944
|
+
in `.hx/settings.json`, the file a project checks in - one machine's absolute
|
|
945
|
+
paths were being committed to everyone who cloned the repo. The new layer sits
|
|
946
|
+
above project settings, and HX adds a `.hx/.gitignore` covering it. Existing
|
|
947
|
+
rules in either file keep working.
|
|
948
|
+
|
|
949
|
+
### Fixed
|
|
950
|
+
|
|
951
|
+
- **`/login` no longer strands you in a dead modal.** The sign-in screen was
|
|
952
|
+
driven before it had finished mounting, so the flow died on its first call and
|
|
953
|
+
left a prompt that took keystrokes and ignored Enter. Opening the browser and
|
|
954
|
+
tearing down the loopback listener also ran on the event loop, freezing the
|
|
955
|
+
TUI - including the Escape that would have cancelled.
|
|
956
|
+
- Text alongside tool results reaches the model on OpenRouter-shaped routes
|
|
957
|
+
instead of being silently dropped.
|
|
958
|
+
|
|
959
|
+
---
|
|
960
|
+
|
|
961
|
+
## [0.1.6] - 2026-09-10
|
|
962
|
+
|
|
963
|
+
### Added
|
|
964
|
+
|
|
965
|
+
- **Web search, over Tavily.** `WebSearch` returns ranked results with a
|
|
966
|
+
snippet each; `WebFetch` reads up to 5 URLs as markdown. Both are registered
|
|
967
|
+
only when a key resolves, so HX without one is unchanged and pays no
|
|
968
|
+
cached-prefix cost for a tool that would always fail. Set the key with
|
|
969
|
+
`hx auth set tavily`, or `TAVILY_API_KEY` / `HX_TAVILY_API_KEY` in the
|
|
970
|
+
environment. Credits spent are reported per call and per session.
|
|
971
|
+
- **Corporate TLS support.** Certificates verify against the operating
|
|
972
|
+
system's trust store, `HX_CA_BUNDLE` (also `SSL_CERT_FILE`,
|
|
973
|
+
`REQUESTS_CA_BUNDLE`) names a CA file, and `HX_SSL_NO_VERIFY=1` turns
|
|
974
|
+
verification off with a warning every session. A TLS failure is now named in
|
|
975
|
+
one line at startup and in `/model` instead of showing up as an empty model
|
|
976
|
+
catalogue with no cause.
|
|
977
|
+
- **Working-tree notices.** The git branch and files that changed on disk
|
|
978
|
+
outside the session are late-injected onto the newest user message, from one
|
|
979
|
+
`git status --porcelain=v2 --branch -z` per provider call, run in a worker
|
|
980
|
+
thread so it never blocks the UI. Off with
|
|
981
|
+
`"context": {"git_notices": false}` or `HX_GIT_NOTICES=0`.
|
|
982
|
+
- **File checkpoints.** Write and Edit park a file's previous bytes in a
|
|
983
|
+
content-addressed store under the session directory, keyed to the position in
|
|
984
|
+
the transcript, so a rewind can undo edits and not just words. A file whose
|
|
985
|
+
bytes moved since HX wrote them is reported and left alone rather than
|
|
986
|
+
overwritten.
|
|
987
|
+
- **`hx docs` and `hx changelog`.** The manual and this file, printed from the
|
|
988
|
+
installed package. `hx docs` with no argument lists the sections;
|
|
989
|
+
`hx docs credentials` prints one. `hx changelog 0.1.5` prints one release.
|
|
990
|
+
The system prompt points at both, so questions about HX itself are answered
|
|
991
|
+
from the shipped documentation.
|
|
992
|
+
|
|
993
|
+
### Changed
|
|
994
|
+
|
|
995
|
+
- The model catalogue keeps the last good copy when a refresh fails, and
|
|
996
|
+
records why it failed so `/model` can say so. The static routes (the Codex
|
|
997
|
+
models) are registered even when there is no cache file and no network.
|
|
998
|
+
|
|
999
|
+
---
|
|
1000
|
+
|
|
1001
|
+
## [0.1.5] - 2026-09-09
|
|
1002
|
+
|
|
1003
|
+
### Added
|
|
1004
|
+
|
|
1005
|
+
- **A ChatGPT Plus/Pro route.** Sign in with `hx auth login openai-codex` (or
|
|
1006
|
+
`/login` in the TUI) and run `openai-codex/gpt-5.3-codex` against your
|
|
1007
|
+
subscription instead of paying per token. OAuth with PKCE against
|
|
1008
|
+
`auth.openai.com`, a loopback listener on port 1455, a paste-the-URL fallback
|
|
1009
|
+
for SSH and `HX_LOGIN_DEVICE_CODE=1` for the device-code flow; access tokens
|
|
1010
|
+
refresh themselves.
|
|
1011
|
+
- Credentials became one store per route in `~/.hx/auth.json` (mode 0600).
|
|
1012
|
+
`hx auth` reports which routes have a credential and where it came from, and
|
|
1013
|
+
`hx auth logout <provider>` forgets one.
|
|
1014
|
+
- `/model` offers only routes you are actually signed in to, so the picker
|
|
1015
|
+
cannot select a model the session has no way to reach.
|
|
1016
|
+
|
|
1017
|
+
The route serving a turn is decided by the model id alone: `openai-codex/*`
|
|
1018
|
+
goes to the subscription, everything else to OpenRouter.
|
|
1019
|
+
|
|
1020
|
+
---
|
|
1021
|
+
|
|
1022
|
+
## [0.1.4] - 2026-09-09
|
|
1023
|
+
|
|
1024
|
+
### Fixed
|
|
1025
|
+
|
|
1026
|
+
- TUI snapshot tests no longer embed the release version, so bumping the
|
|
1027
|
+
version stopped failing the suite.
|
|
1028
|
+
|
|
1029
|
+
---
|
|
1030
|
+
|
|
1031
|
+
## [0.1.3] - 2026-09-09
|
|
1032
|
+
|
|
1033
|
+
### Fixed
|
|
1034
|
+
|
|
1035
|
+
- An exact slash command runs on the first `enter` instead of only selecting
|
|
1036
|
+
itself in the completion list.
|
|
1037
|
+
|
|
1038
|
+
---
|
|
1039
|
+
|
|
1040
|
+
## [0.1.2] - 2026-09-09
|
|
1041
|
+
|
|
1042
|
+
### Added
|
|
1043
|
+
|
|
1044
|
+
- **Themes as data.** A theme is a JSON file - a `vars` block of colours and a
|
|
1045
|
+
`colors` block mapping semantic roles onto them. Drop one in
|
|
1046
|
+
`~/.hx/themes/mine.json` and `/theme mine` picks it up, no restart. Syntax
|
|
1047
|
+
highlighting is theme-driven, and pi's theme files load unchanged.
|
|
1048
|
+
- **One keybinding registry.** Every key is defined once in `src/hx/keys.py`;
|
|
1049
|
+
`/help`, the hints line and the README table all read from it, and any action
|
|
1050
|
+
id can be rebound in `~/.hx/keybindings.json`. Conflicts and unknown ids are
|
|
1051
|
+
reported at startup rather than silently resolved.
|
|
1052
|
+
- **A completion popup** above the prompt for `/` commands and `@` paths, with
|
|
1053
|
+
fuzzy matching; `tab` cycles, `enter` accepts, `esc` dismisses.
|
|
1054
|
+
- **A readline-style prompt** with a kill ring (`ctrl+w`, `ctrl+u`, `ctrl+k`,
|
|
1055
|
+
`ctrl+y`, `alt+y`), undo and redo.
|
|
1056
|
+
- **System clipboard support.** `ctrl+x` copies the selected message and
|
|
1057
|
+
`/copy` the last reply, through `pbcopy`/`wl-copy`/`xclip`/`xsel` with an
|
|
1058
|
+
OSC 52 fallback that also works over SSH.
|
|
1059
|
+
- The prompt is framed and the working status lives in its border.
|
|
1060
|
+
- `/exit` as an alias for `/quit`.
|
|
1061
|
+
|
|
1062
|
+
### Fixed
|
|
1063
|
+
|
|
1064
|
+
- Permission grants stay inside what the user actually approved.
|
|
1065
|
+
- A `/model` choice persists to user settings.
|
|
1066
|
+
- Slash commands run in a worker, so a slow one no longer blocks the UI.
|
|
1067
|
+
|
|
1068
|
+
---
|
|
1069
|
+
|
|
1070
|
+
## [0.1.0] - 2026-09-08
|
|
1071
|
+
|
|
1072
|
+
First release, published to PyPI as [`hx-cli`](https://pypi.org/project/hx-cli/).
|
|
1073
|
+
|
|
1074
|
+
### Added
|
|
1075
|
+
|
|
1076
|
+
- **The agent loop**, streaming from OpenRouter with prefix caching, accurate
|
|
1077
|
+
per-turn cost accounting and a stable request prefix.
|
|
1078
|
+
- **The tool suite**: Bash (with a background job runner), Read, Write, Edit,
|
|
1079
|
+
Glob, Grep, TodoWrite and Task, with output capping that keeps the head and
|
|
1080
|
+
tail of a large result and spills the rest to the session directory.
|
|
1081
|
+
- **The permission engine and OS sandbox.** `Tool(specifier)` rules where deny
|
|
1082
|
+
beats ask beats allow, shell commands decomposed into their real segments
|
|
1083
|
+
first, and Seatbelt on macOS / bubblewrap on Linux confining writes and
|
|
1084
|
+
network. Modes cycle with `shift+tab`: `plan`, `default`, `acceptEdits`,
|
|
1085
|
+
`bypass`.
|
|
1086
|
+
- **Context engineering**: late injection for per-turn state, compaction at 80%
|
|
1087
|
+
of the window (or `/compact [focus]`), and cache breakpoints that only
|
|
1088
|
+
advance once enough tokens have accumulated.
|
|
1089
|
+
- **Sessions**: persisted transcripts, `hx resume` / `/resume`, and a model-named
|
|
1090
|
+
session title you can change with `/title`.
|
|
1091
|
+
- **Skills, subagents and MCP.** Skills are `SKILL.md` directories under
|
|
1092
|
+
`.hx/skills/` or `~/.hx/skills/`, loaded on demand so a hundred installed
|
|
1093
|
+
skills cost a hundred lines. Subagents (`explore`, `plan`, `general`, plus
|
|
1094
|
+
your own in `.hx/agents/`) run in their own context and return one report.
|
|
1095
|
+
MCP servers go in `.hx/mcp.json` or `hx mcp add`.
|
|
1096
|
+
- **The TUI**: transcript, status bar with token, cache and cost counters, todo
|
|
1097
|
+
sidebar, model picker, command palette and per-tool renderers.
|
|
1098
|
+
- **Print mode** (`hx -p`), where stdout carries only the assistant's text, so
|
|
1099
|
+
it pipes.
|
|
1100
|
+
- **Configuration** merged from defaults, `~/.hx/settings.json`,
|
|
1101
|
+
`./.hx/settings.json`, `HX_*` environment variables and CLI flags, with
|
|
1102
|
+
permission lists and prompt appends unioned rather than replaced.
|
|
1103
|
+
- `/configure` and `hx auth` for the OpenRouter key, `hx upgrade` for
|
|
1104
|
+
self-update, and `install.sh` bootstrapping uv with a pinned Python.
|
|
1105
|
+
|
|
1106
|
+
[Unreleased]: https://github.com/aletisunil/hx/compare/v1.0.0...HEAD
|
|
1107
|
+
[1.0.0]: https://github.com/aletisunil/hx/compare/v0.3.0-beta.1...v1.0.0
|
|
1108
|
+
[0.3.0-beta.1]: https://github.com/aletisunil/hx/compare/v0.2.20...v0.3.0-beta.1
|
|
1109
|
+
[0.2.20]: https://github.com/aletisunil/hx/compare/v0.2.19...v0.2.20
|
|
1110
|
+
[0.2.19]: https://github.com/aletisunil/hx/compare/v0.2.18...v0.2.19
|
|
1111
|
+
[0.2.18]: https://github.com/aletisunil/hx/compare/v0.2.17...v0.2.18
|
|
1112
|
+
[0.2.17]: https://github.com/aletisunil/hx/compare/v0.2.16...v0.2.17
|
|
1113
|
+
[0.2.16]: https://github.com/aletisunil/hx/compare/v0.2.15...v0.2.16
|
|
1114
|
+
[0.2.15]: https://github.com/aletisunil/hx/compare/v0.2.14...v0.2.15
|
|
1115
|
+
[0.2.14]: https://github.com/aletisunil/hx/compare/v0.2.13...v0.2.14
|
|
1116
|
+
[0.2.13]: https://github.com/aletisunil/hx/compare/v0.2.12...v0.2.13
|
|
1117
|
+
[0.2.12]: https://github.com/aletisunil/hx/compare/v0.2.11...v0.2.12
|
|
1118
|
+
[0.2.11]: https://github.com/aletisunil/hx/compare/v0.2.10...v0.2.11
|
|
1119
|
+
[0.2.10]: https://github.com/aletisunil/hx/compare/v0.2.9...v0.2.10
|
|
1120
|
+
[0.2.9]: https://github.com/aletisunil/hx/compare/v0.2.8...v0.2.9
|
|
1121
|
+
[0.2.8]: https://github.com/aletisunil/hx/compare/v0.2.7...v0.2.8
|
|
1122
|
+
[0.2.7]: https://github.com/aletisunil/hx/compare/v0.2.6...v0.2.7
|
|
1123
|
+
[0.2.6]: https://github.com/aletisunil/hx/compare/v0.2.5...v0.2.6
|
|
1124
|
+
[0.2.5]: https://github.com/aletisunil/hx/compare/v0.2.4...v0.2.5
|
|
1125
|
+
[0.2.4]: https://github.com/aletisunil/hx/compare/v0.2.3...v0.2.4
|
|
1126
|
+
[0.2.3]: https://github.com/aletisunil/hx/compare/v0.2.2...v0.2.3
|
|
1127
|
+
[0.2.2]: https://github.com/aletisunil/hx/compare/v0.2.1...v0.2.2
|
|
1128
|
+
[0.2.1]: https://github.com/aletisunil/hx/compare/v0.2.0...v0.2.1
|
|
1129
|
+
[0.2.0]: https://github.com/aletisunil/hx/compare/v0.1.9...v0.2.0
|
|
1130
|
+
[0.1.9]: https://github.com/aletisunil/hx/compare/v0.1.8...v0.1.9
|
|
1131
|
+
[0.1.8]: https://github.com/aletisunil/hx/compare/v0.1.7...v0.1.8
|
|
1132
|
+
[0.1.7]: https://github.com/aletisunil/hx/compare/v0.1.6...v0.1.7
|
|
1133
|
+
[0.1.6]: https://github.com/aletisunil/hx/compare/v0.1.5...v0.1.6
|
|
1134
|
+
[0.1.5]: https://github.com/aletisunil/hx/compare/v0.1.4...v0.1.5
|
|
1135
|
+
[0.1.4]: https://github.com/aletisunil/hx/compare/v0.1.3...v0.1.4
|
|
1136
|
+
[0.1.3]: https://github.com/aletisunil/hx/compare/v0.1.2...v0.1.3
|
|
1137
|
+
[0.1.2]: https://github.com/aletisunil/hx/compare/v0.1.0...v0.1.2
|
|
1138
|
+
[0.1.0]: https://github.com/aletisunil/hx/releases/tag/v0.1.0
|