@sunilaleti/hx 0.3.0-beta.1 → 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 +51 -1
- package/README.md +105 -81
- package/dist/hx.js +959 -597
- package/dist/hx.js.map +4 -4
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -28,6 +28,55 @@ the project follows [semantic versioning](https://semver.org/).
|
|
|
28
28
|
|
|
29
29
|
---
|
|
30
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
|
+
|
|
31
80
|
## [0.3.0-beta.1] - 2026-10-31
|
|
32
81
|
|
|
33
82
|
### Changed
|
|
@@ -1054,7 +1103,8 @@ First release, published to PyPI as [`hx-cli`](https://pypi.org/project/hx-cli/)
|
|
|
1054
1103
|
- `/configure` and `hx auth` for the OpenRouter key, `hx upgrade` for
|
|
1055
1104
|
self-update, and `install.sh` bootstrapping uv with a pinned Python.
|
|
1056
1105
|
|
|
1057
|
-
[Unreleased]: https://github.com/aletisunil/hx/compare/
|
|
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
|
|
1058
1108
|
[0.3.0-beta.1]: https://github.com/aletisunil/hx/compare/v0.2.20...v0.3.0-beta.1
|
|
1059
1109
|
[0.2.20]: https://github.com/aletisunil/hx/compare/v0.2.19...v0.2.20
|
|
1060
1110
|
[0.2.19]: https://github.com/aletisunil/hx/compare/v0.2.18...v0.2.19
|
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# HX
|
|
2
2
|
|
|
3
|
-
An agent harness for the terminal.
|
|
4
|
-
OpenRouter
|
|
3
|
+
An agent harness for the terminal. TypeScript on Node.js, its own terminal
|
|
4
|
+
renderer, and models through OpenRouter or a ChatGPT, Claude or Devin
|
|
5
|
+
subscription.
|
|
5
6
|
|
|
6
7
|
HX runs in your project directory, reads and edits your code, runs commands in
|
|
7
8
|
a sandboxed shell, and shows you what every turn costs.
|
|
@@ -11,12 +12,18 @@ a sandboxed shell, and shows you what every turn costs.
|
|
|
11
12
|
## Install
|
|
12
13
|
|
|
13
14
|
```sh
|
|
14
|
-
npm install --global @sunilaleti/hx
|
|
15
|
+
npm install --global @sunilaleti/hx
|
|
15
16
|
```
|
|
16
17
|
|
|
17
18
|
HX runs on [Node.js](https://nodejs.org/) 22.15 or newer, on macOS and Linux.
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
Run the same command again to update. Then run `hx`: the first time, it opens
|
|
20
|
+
with the HX logo and says to type `/login`.
|
|
21
|
+
|
|
22
|
+
Or let the install script check your Node.js version and run that command:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
curl -fsSL https://raw.githubusercontent.com/aletisunil/hx/main/install.sh | sh
|
|
26
|
+
```
|
|
20
27
|
|
|
21
28
|
### `@sunilaleti/hx` on npm, `hx` in your terminal
|
|
22
29
|
|
|
@@ -37,9 +44,14 @@ to your `PATH`:
|
|
|
37
44
|
export PATH="$(npm prefix --global)/bin:$PATH"
|
|
38
45
|
```
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
sessions, sign-ins and settings
|
|
42
|
-
|
|
47
|
+
Coming from `hx-cli`, the Python build on PyPI? It is no longer released.
|
|
48
|
+
Remove it, then install from npm - sessions, sign-ins and settings in `~/.hx`
|
|
49
|
+
carry over:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
uv tool uninstall hx-cli
|
|
53
|
+
npm install --global @sunilaleti/hx
|
|
54
|
+
```
|
|
43
55
|
|
|
44
56
|
### Credentials
|
|
45
57
|
|
|
@@ -329,6 +341,7 @@ hx --no-sandbox # disable OS sandboxing (rules still apply)
|
|
|
329
341
|
hx --system-prompt @p.md # replace the system prompt for one run
|
|
330
342
|
hx --append-system-prompt "Always run the tests" # add to it; repeatable
|
|
331
343
|
hx mcp list|add|remove # manage MCP servers
|
|
344
|
+
hx mcp approve <server> # let a server from the project's .hx/mcp.json start
|
|
332
345
|
hx mcp login <server> # sign in to a remote MCP server in the browser
|
|
333
346
|
hx mcp logout <server> # forget a remote MCP server's sign-in
|
|
334
347
|
hx auth [set|clear] # manage the OpenRouter key
|
|
@@ -336,7 +349,6 @@ hx auth login [provider] # sign in (openrouter, openai-codex, devin, claude-c
|
|
|
336
349
|
hx auth logout <provider> # forget a stored credential
|
|
337
350
|
hx docs [section] # the manual; no argument lists its sections
|
|
338
351
|
hx changelog [version] # what shipped in each version
|
|
339
|
-
hx upgrade # update to the latest release
|
|
340
352
|
```
|
|
341
353
|
|
|
342
354
|
Print mode is the scriptable one: stdout carries only the assistant's text, so
|
|
@@ -373,7 +385,7 @@ allowlist it if you would rather not be asked:
|
|
|
373
385
|
|
|
374
386
|
### Keys
|
|
375
387
|
|
|
376
|
-
Every key below is defined once, in `src/
|
|
388
|
+
Every key below is defined once, in `src/keys.ts`. `/help`, the hints line
|
|
377
389
|
under the prompt and this table all read from that registry, and any of them can
|
|
378
390
|
be rebound (see [Keybindings](#keybindings)).
|
|
379
391
|
|
|
@@ -474,7 +486,7 @@ says so; copy the file over and drag or paste its path instead.
|
|
|
474
486
|
#### Keybindings
|
|
475
487
|
|
|
476
488
|
Any key can be rebound in `~/.hx/keybindings.json`, keyed by the action ids in
|
|
477
|
-
`src/
|
|
489
|
+
`src/keys.ts`:
|
|
478
490
|
|
|
479
491
|
```json
|
|
480
492
|
{
|
|
@@ -721,7 +733,7 @@ route at another OpenAI-compatible endpoint - a gateway, or a recording proxy.
|
|
|
721
733
|
stderr so stdout stays pipeable. `/prompt` shows the same thing inside a running
|
|
722
734
|
session.
|
|
723
735
|
|
|
724
|
-
The built-in prompt is `SYSTEM_PROMPT` in `src/
|
|
736
|
+
The built-in prompt is `SYSTEM_PROMPT` in `src/core/context.ts`. It is
|
|
725
737
|
replaced by the first of these that exists:
|
|
726
738
|
|
|
727
739
|
| Source | Scope |
|
|
@@ -785,7 +797,7 @@ mapping semantic roles onto them. Drop one in `~/.hx/themes/mine.json` and
|
|
|
785
797
|
```
|
|
786
798
|
|
|
787
799
|
The role names are pi's, so [pi](https://github.com/earendil-works/pi) theme
|
|
788
|
-
files load here unchanged. `src/
|
|
800
|
+
files load here unchanged. `src/tui/themes/dark.json` is the reference.
|
|
789
801
|
A few roles pi has no concept of may be left out, and borrow another role:
|
|
790
802
|
`toolArgs`, the colour of a tool call's paths and patterns, borrows `accent`;
|
|
791
803
|
`panel` and `mdCodeBlockBg` borrow `surface`; `userText` borrows `text`; and
|
|
@@ -842,7 +854,19 @@ Two independent layers guard every tool call, and both must pass.
|
|
|
842
854
|
holds even in bypass mode. Shell commands are decomposed into their real
|
|
843
855
|
segments first, so an allow rule for `git status` does not carry
|
|
844
856
|
`&& rm -rf /` along with it; a command that cannot be decomposed with
|
|
845
|
-
confidence prompts rather than passing.
|
|
857
|
+
confidence prompts rather than passing. What runs inside a command counts as a
|
|
858
|
+
segment of its own - `$(...)`, `<(...)`, `>(...)` - so `cat <(rm -rf src)` is
|
|
859
|
+
not a read-only `cat`. A deny rule matches the command however it is spelled:
|
|
860
|
+
`deny: Bash(rm:*)` also stops `FOO=1 rm`, `/bin/rm` and `nohup rm`. An allow
|
|
861
|
+
rule covers only what it names, so `allow: Bash(ls)` does not cover
|
|
862
|
+
`LD_PRELOAD=x ls`.
|
|
863
|
+
|
|
864
|
+
**Credential files** - the same `~/.ssh`, `~/.aws` and `auth.json` the sandbox
|
|
865
|
+
guards - always ask before Read, Grep, Glob, Symbols, Write or Edit touch them,
|
|
866
|
+
in every mode, symlinks followed. Those tools run inside HX, outside the
|
|
867
|
+
sandbox. An allow rule that names the file, such as `allow: Read(~/.ssh/config)`,
|
|
868
|
+
opens that one file. A search that starts above them, from `$HOME`, leaves
|
|
869
|
+
them out.
|
|
846
870
|
|
|
847
871
|
**An OS sandbox** wraps command execution: Seatbelt on macOS, bubblewrap on
|
|
848
872
|
Linux. The filesystem is readable, writes are confined to the project and the
|
|
@@ -855,7 +879,13 @@ and the Tavily and Devin keys) are removed from the environment of every
|
|
|
855
879
|
command it runs.
|
|
856
880
|
|
|
857
881
|
Modes cycle with shift+tab: `plan` (read-only — mutating tools are not even
|
|
858
|
-
offered to the model), `default`, `acceptEdits`, `bypass`.
|
|
882
|
+
offered to the model), `default`, `acceptEdits`, `bypass`. `acceptEdits` covers
|
|
883
|
+
the project's own files: a Write or Edit outside the project, through a symlink
|
|
884
|
+
that leads out of it, or into its `.git` or `.hx` folder still asks.
|
|
885
|
+
|
|
886
|
+
**Nothing that arrives with a clone runs by itself.** Hooks in a project's
|
|
887
|
+
shared `.hx/settings.json` are ignored, and a server in its `.hx/mcp.json`
|
|
888
|
+
starts only once you approve it - see [Extending it](#extending-it).
|
|
859
889
|
|
|
860
890
|
---
|
|
861
891
|
|
|
@@ -1083,7 +1113,16 @@ construction.
|
|
|
1083
1113
|
```
|
|
1084
1114
|
|
|
1085
1115
|
Or `hx mcp add local python server.py`, or
|
|
1086
|
-
`hx mcp add remote --url https://example.com/mcp`.
|
|
1116
|
+
`hx mcp add remote --url https://example.com/mcp`.
|
|
1117
|
+
|
|
1118
|
+
A server in the project's `.hx/mcp.json` runs a command on your machine, and
|
|
1119
|
+
the file arrives with a clone - so HX does not start it until you approve it.
|
|
1120
|
+
A session names each one waiting, with what it runs. `hx mcp approve local`
|
|
1121
|
+
starts it from the next session on, and `hx mcp add` approves what it adds.
|
|
1122
|
+
The approval is for that command: if a `git pull` changes it, the server waits
|
|
1123
|
+
again. Servers in your own `~/.hx/mcp.json` need no approval.
|
|
1124
|
+
|
|
1125
|
+
Tools arrive namespaced
|
|
1087
1126
|
`mcp__<server>__<tool>` in a deterministic order. Servers connect concurrently
|
|
1088
1127
|
with a per-server timeout; one that is broken or slow logs a warning and is
|
|
1089
1128
|
dropped rather than taking the session with it. A server that changes its tool
|
|
@@ -1186,23 +1225,26 @@ bypassed by delegating through `Task`.
|
|
|
1186
1225
|
|
|
1187
1226
|
```sh
|
|
1188
1227
|
git clone https://github.com/aletisunil/hx && cd hx
|
|
1189
|
-
|
|
1228
|
+
npm ci
|
|
1190
1229
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1230
|
+
npm run check # tsc, biome, and the source-literal check
|
|
1231
|
+
npm run build # dist/hx.js, the bundle npm ships
|
|
1232
|
+
node dist/hx.js # run from the checkout
|
|
1233
|
+
npm run e2e # build, then the whole end-to-end suite
|
|
1234
|
+
uv run ruff check . && uv run ruff format --check . # the suite's own Python
|
|
1195
1235
|
```
|
|
1196
1236
|
|
|
1197
|
-
The suite is end-to-end
|
|
1198
|
-
|
|
1237
|
+
The suite is end-to-end, written in Python with pytest, and run with
|
|
1238
|
+
[uv](https://docs.astral.sh/uv/). Each test in `tests/e2e/` starts the `hx`
|
|
1239
|
+
bundle - on a real pseudo-terminal for the TUI, or as a plain subprocess for
|
|
1199
1240
|
print mode and the other commands - in a private `$HOME` and `$HX_HOME`, and
|
|
1200
1241
|
points it at a local OpenRouter stand-in with `HX_OPENROUTER_BASE_URL`, and
|
|
1201
1242
|
at a ChatGPT stand-in with `HX_CODEX_BASE_URL`.
|
|
1202
1243
|
Everything between the keyboard and the socket is the shipped code; only the
|
|
1203
1244
|
model's replies are scripted. The screen is read back through a VT emulator,
|
|
1204
1245
|
so assertions are on the cells a terminal shows, not on the bytes HX meant to
|
|
1205
|
-
write.
|
|
1246
|
+
write. The suite never imports HX: it tests `dist/hx.js` by default, and any
|
|
1247
|
+
other `hx` - an installed one, say - named by `HX_E2E_BIN`.
|
|
1206
1248
|
|
|
1207
1249
|
Every run writes `e2e-report/`: `index.html` shows each test's outcome and
|
|
1208
1250
|
every screen it checked, rendered in colour, and one text file per screen lets
|
|
@@ -1212,41 +1254,26 @@ two runs be compared with `diff -r`. CI uploads it from every test job as an
|
|
|
1212
1254
|
Tests that need the OS sandbox are skipped where there is no backend
|
|
1213
1255
|
(`sandbox-exec` on macOS, `bwrap` on Linux).
|
|
1214
1256
|
|
|
1215
|
-
Tests marked `live` hit a real API. The OpenRouter ones cost money:
|
|
1216
|
-
|
|
1217
|
-
```sh
|
|
1218
|
-
OPENROUTER_API_KEY=... uv run pytest -m live tests/test_live.py
|
|
1219
|
-
|
|
1220
|
-
# the Codex route draws on your ChatGPT subscription instead
|
|
1221
|
-
hx auth login openai-codex
|
|
1222
|
-
uv run pytest -m live tests/test_live_codex.py
|
|
1223
|
-
|
|
1224
|
-
# and the Devin route on your Devin subscription
|
|
1225
|
-
hx auth login devin
|
|
1226
|
-
uv run pytest -m live tests/test_live_devin.py
|
|
1227
|
-
```
|
|
1228
|
-
|
|
1229
|
-
They are the only place the wire formats, streaming, tool use and a genuine
|
|
1230
|
-
cache hit are proven against a real service; everything else runs against the
|
|
1231
|
-
stand-in.
|
|
1232
|
-
|
|
1233
1257
|
### Layout
|
|
1234
1258
|
|
|
1235
1259
|
```
|
|
1236
|
-
src/
|
|
1237
|
-
cli.
|
|
1260
|
+
src/
|
|
1261
|
+
cli.ts runtime.ts config.ts paths.ts frontmatter.ts git.ts trace.ts
|
|
1238
1262
|
core/ loop, context assembly, compaction, late injection, sessions, usage
|
|
1239
1263
|
auth/ credential store, OAuth flows, per-route resolution
|
|
1240
|
-
providers/ OpenRouter, Codex, Devin, the model catalogue
|
|
1264
|
+
providers/ OpenRouter, Codex, Claude Code, Devin, the model catalogue
|
|
1241
1265
|
tools/ Bash, Read, Write, Edit, Glob, Grep, Symbols, TodoWrite, Task,
|
|
1242
1266
|
WebSearch, WebFetch, line anchors, output capping
|
|
1243
1267
|
permissions/ rule engine, shell decomposition, Seatbelt/bubblewrap
|
|
1244
1268
|
hooks/ the four events, the trust boundary, subprocess execution
|
|
1245
|
-
skills/ agents/
|
|
1246
|
-
|
|
1269
|
+
skills/ agents/
|
|
1270
|
+
mcp/ client, OAuth, and approval for a project's servers
|
|
1271
|
+
keys.ts keybinding registry: ids, defaults, descriptions, user overrides
|
|
1247
1272
|
term/ the renderer: raw mode, input decoding, the differ, the screen
|
|
1248
1273
|
tui/ views, commands, per-tool renderers, themes
|
|
1249
1274
|
themes/ the shipped palettes, as JSON
|
|
1275
|
+
py.ts pathlib.ts json.ts Python's string, path and JSON behaviour, which
|
|
1276
|
+
HX's output and files were written against
|
|
1250
1277
|
```
|
|
1251
1278
|
|
|
1252
1279
|
The core is headless and emits events; the TUI and print mode are both just
|
|
@@ -1257,44 +1284,43 @@ imports `tui/`.
|
|
|
1257
1284
|
|
|
1258
1285
|
## Releasing
|
|
1259
1286
|
|
|
1260
|
-
For maintainers. HX
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
1. Push the repo to `github.com/<owner>/hx` and update the URLs in
|
|
1265
|
-
`pyproject.toml`.
|
|
1266
|
-
2. Create a GitHub environment named `pypi`.
|
|
1267
|
-
3. On PyPI, add a [trusted publisher](https://docs.pypi.org/trusted-publishers/)
|
|
1268
|
-
for the project: owner, repo `hx`, workflow `ci.yml`, environment `pypi`.
|
|
1269
|
-
No API token is stored anywhere.
|
|
1287
|
+
For maintainers. HX is published to npm as **`@sunilaleti/hx`**, on the
|
|
1288
|
+
`latest` tag, by CI. Every version is a stable release; there is no beta
|
|
1289
|
+
channel.
|
|
1270
1290
|
|
|
1271
|
-
|
|
1291
|
+
A push to `main` whose `package.json` version is not on npm yet is a release:
|
|
1292
|
+
once lint, the test suite and the install script pass, CI's `release` job
|
|
1293
|
+
publishes it to npm, tags it `vX.Y.Z` and makes the GitHub release, with the
|
|
1294
|
+
version's `CHANGELOG.md` section as its notes. A push that leaves the version
|
|
1295
|
+
alone publishes nothing.
|
|
1272
1296
|
|
|
1273
1297
|
```sh
|
|
1274
1298
|
# 1. move CHANGELOG.md's [Unreleased] entries under the new version, dated,
|
|
1275
1299
|
# and leave a fresh empty [Unreleased] behind
|
|
1276
|
-
# 2. bump
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1300
|
+
# 2. bump "version" in package.json
|
|
1301
|
+
npm version X.Y.Z --no-git-tag-version
|
|
1302
|
+
# 3. the whole suite; the version must have a CHANGELOG section
|
|
1303
|
+
npm run e2e
|
|
1304
|
+
git commit -am "chore(release): X.Y.Z"
|
|
1305
|
+
git push origin main
|
|
1280
1306
|
```
|
|
1281
1307
|
|
|
1282
1308
|
Step 1 is not optional and is not a convention: `tests/e2e/test_cli.py` fails the
|
|
1283
|
-
whole suite when
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1309
|
+
whole suite when the version has no `CHANGELOG.md` section, and the `release`
|
|
1310
|
+
job checks again before it publishes. The file ships inside the bundle and
|
|
1311
|
+
`hx changelog` prints it, so a version with no section there would reach users
|
|
1312
|
+
as a blank release.
|
|
1287
1313
|
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1314
|
+
One-time setup: CI publishes with npm's
|
|
1315
|
+
[trusted publishing](https://docs.npmjs.com/trusted-publishers), so no npm
|
|
1316
|
+
token is stored anywhere. On npmjs.com, open `@sunilaleti/hx` → Settings →
|
|
1317
|
+
Trusted Publisher, choose GitHub Actions, and enter owner `aletisunil`,
|
|
1318
|
+
repository `hx` and workflow `ci.yml`, with no environment.
|
|
1293
1319
|
|
|
1294
|
-
To
|
|
1320
|
+
To see what npm would ship before releasing:
|
|
1295
1321
|
|
|
1296
1322
|
```sh
|
|
1297
|
-
|
|
1323
|
+
npm pack --dry-run
|
|
1298
1324
|
```
|
|
1299
1325
|
|
|
1300
1326
|
---
|
|
@@ -1306,13 +1332,11 @@ streaming with prefix caching and accurate cost accounting, session persistence
|
|
|
1306
1332
|
and resume, the tool suite, the permission engine and OS sandbox, late
|
|
1307
1333
|
injection, compaction, output capping, skills, subagents, MCP, and the TUI.
|
|
1308
1334
|
|
|
1309
|
-
|
|
1310
|
-
subscription over OAuth,
|
|
1311
|
-
decides which.
|
|
1335
|
+
Four routes to a model: an OpenRouter API key, a ChatGPT Plus/Pro
|
|
1336
|
+
subscription over OAuth, a Claude Pro/Max subscription through Claude Code, or
|
|
1337
|
+
a Devin subscription over OAuth. The model id decides which.
|
|
1312
1338
|
|
|
1313
|
-
Published to
|
|
1314
|
-
|
|
1339
|
+
Published to npm as [`@sunilaleti/hx`](https://www.npmjs.com/package/@sunilaleti/hx).
|
|
1340
|
+
The Python build that came before it, `hx-cli` on PyPI, stopped at 0.2.20.
|
|
1315
1341
|
|
|
1316
|
-
|
|
1317
|
-
tests exist and cover all three wire formats, tool use, reasoning replay and a
|
|
1318
|
-
genuine cache hit, but they need a credential and are deselected by default.
|
|
1342
|
+
No test reaches a real model: every route is proven against a local stand-in.
|