abeam 0.0.1__py3-none-win_amd64.whl

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.
Binary file
@@ -0,0 +1,355 @@
1
+ Metadata-Version: 2.4
2
+ Name: abeam
3
+ Version: 0.0.1
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Environment :: Console
6
+ Classifier: Intended Audience :: Developers
7
+ Classifier: Operating System :: Microsoft :: Windows
8
+ Classifier: Programming Language :: Rust
9
+ Classifier: Topic :: Software Development
10
+ Classifier: Topic :: Terminals :: Terminal Emulators/X Terminals
11
+ License-File: LICENSE
12
+ Summary: One window for a Claude Code session: Claude hosted in a pty pane, with git, a file viewer and a shell beside it.
13
+ Keywords: claude,claude-code,terminal,tui,multiplexer,conpty
14
+ License-Expression: MIT
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
17
+ Project-URL: Homepage, https://github.com/OpenAfterHours/abeam
18
+ Project-URL: Issues, https://github.com/OpenAfterHours/abeam/issues
19
+ Project-URL: Repository, https://github.com/OpenAfterHours/abeam
20
+
21
+ # abeam
22
+
23
+ One window for a Claude Code session.
24
+
25
+ Claude runs in the left pane — hosted in a pty, parsed, and drawn by abeam, not
26
+ passed through to your terminal. The right pane shows the state of the git
27
+ worktree, the document Claude just wrote, or a shell to run things in. A file
28
+ watcher drives the first two, so neither has to be asked.
29
+
30
+ It replaces a three-window setup: Claude in one terminal, git in another, and an
31
+ editor open purely to read the markdown Claude produced.
32
+
33
+ ```
34
+ ┌ claude ──────────────────────────────┐┌ git · main ↑2 · 12 changed ──────┐
35
+ │ ││ Staged (1) +40 -6 │
36
+ │ > implement the parser ││ M crates/abeam/src/app.rs +40-6│
37
+ │ ││ Changed (1) +7 -0 │
38
+ │ ● Writing crates/abeam/src/parse.rs ││ M docs/design.md +7-0 │
39
+ │ ││ Untracked (1) │
40
+ │ ││ ? notes/ │
41
+ │ ││ Recent │
42
+ │ ││ a1b2c3d 2m parser skeleton │
43
+ └──────────────────────────────────────┘└──────────────────────────────────┘
44
+ ```
45
+
46
+ ## Why this rather than wezterm + lazygit + glow
47
+
48
+ Because the right pane knows what the agent just did. A watcher on the
49
+ repository root feeds both views: markdown Claude writes becomes the document on
50
+ screen, and any file it touches refreshes git within one debounce interval. That
51
+ is the only thing here you cannot assemble out of existing tools, and it is the
52
+ reason abeam exists.
53
+
54
+ Everything else is a consequence of that. The panes are read-only, they never
55
+ take focus from Claude, and they never switch themselves — a pane that yanks
56
+ itself into view while you are reading is delightful twice and infuriating
57
+ thereafter.
58
+
59
+ ## Installing
60
+
61
+ ```
62
+ uvx abeam # run it without installing anything
63
+ uv tool install abeam # or keep it on PATH
64
+ ```
65
+
66
+ There is no Python in abeam. PyPI is the delivery van: the wheel's whole payload
67
+ is a compiled Windows binary, so `uvx` fetches a few hundred kilobytes and runs
68
+ it — no Rust toolchain, no build step, and nothing to compile on your machine.
69
+
70
+ ## Requirements
71
+
72
+ - **Windows, x86-64.** Everything works because of specific ConPTY behaviour,
73
+ and the pty test suites are `#![cfg(windows)]`. The code is not portable
74
+ today, and on another platform the tests pass by saying nothing. Only x86-64
75
+ wheels are published: ARM Windows would cross-compile in one line and would be
76
+ a binary nobody has ever run, so `uvx` reporting no matching wheel is the more
77
+ honest answer until someone can test it.
78
+ - `git` on `PATH`, for the git pane. Its absence is reported in the pane, not
79
+ fatal.
80
+ - Rust 1.95+ and the MSVC toolchain — **only to build from source**. Plain
81
+ `cargo build`, no vcvars wrapper.
82
+
83
+ ## Running it
84
+
85
+ ```
86
+ abeam # hosts `claude` in the current directory
87
+ abeam pwsh # hosts something else
88
+ ```
89
+
90
+ The first argument is the program; everything after it is passed through. The
91
+ current directory is both the child's working directory and the root that the
92
+ git pane and the watcher use.
93
+
94
+ From a checkout, `cargo run -p abeam` and `cargo run -p abeam -- pwsh` do the
95
+ same.
96
+
97
+ ## Keys
98
+
99
+ Everything abeam binds lives under `Alt` and the F-keys. **Nothing abeam claims
100
+ is a key Claude can act on** — the audit behind that is `docs/keymap.md`, and
101
+ `crates/abeam/src/keys.rs` has tests pinning it. Everything else you type goes
102
+ to Claude untouched.
103
+
104
+ | Key | |
105
+ | --- | --- |
106
+ | `Alt+G` | right pane → git |
107
+ | `Alt+E` | right pane → files / markdown (again for the file list) |
108
+ | `Alt+S` | right pane → a shell, **and focus it** (again to hand focus back) |
109
+ | `F2` | right pane → pty diagnostics, and back to what it displaced |
110
+ | `Alt+Left` / `Alt+Right` | move focus |
111
+ | `Alt+J` / `Alt+K` | scroll the right pane a line — **without focusing it** |
112
+ | `Alt+PgDn` / `Alt+PgUp` | scroll the right pane a page — without focusing it |
113
+ | `Alt+Z` | zoom: hide / show the right pane |
114
+ | `Alt+Q` | quit (twice while a child is live) |
115
+ | `F1` | key help overlay |
116
+ | `Ctrl+\` or `F12` | send the *next* key to Claude verbatim |
117
+
118
+ Reading the right pane costs nothing: switching views and scrolling both work
119
+ while Claude still has focus. You only need focus to drive a selection — or to
120
+ type, which is what `Alt+S` is for and the only reason a view key moves focus.
121
+
122
+ Once the right pane *is* focused, plain keys work, deliberately the same
123
+ vocabulary as Claude's own transcript view: `j`/`k` and arrows for a line,
124
+ `space`/`b` and PgDn/PgUp for a page, `Ctrl+D`/`Ctrl+U` for a half page, `g`/`G`
125
+ and Home/End for the ends, `Tab`/`Shift+Tab` for the selection, `Enter` to open,
126
+ `r` to refresh. `t` swaps rendered markdown for its source; in the file list,
127
+ `/` finds a file anywhere under the root and `Backspace` goes up a directory.
128
+ `Esc` or `q` hands focus back to Claude.
129
+
130
+ The shell view is the exception, and it has to be: `Esc` and `q` belong to
131
+ whatever is running in it. `Alt+S` or `Alt+←` is the way out, and its border
132
+ says so rather than leaving you to find out.
133
+
134
+ `Ctrl+\` exists so abeam can never permanently shadow a Claude binding. If a
135
+ future Claude release binds `Alt+G`, `Ctrl+\` then `Alt+G` still reaches it.
136
+
137
+ ## The panes
138
+
139
+ **git** — read-only. Branch, ahead/behind, staged / unstaged / untracked files
140
+ with per-file line counts, and recent commits. Every `git` call is a read: it
141
+ stages nothing and commits nothing, which matters because it refreshes itself
142
+ unasked. Refreshes when the watcher sees a write, and on a two-second safety
143
+ timer for changes the watcher cannot see (`.git` is deliberately not watched, so
144
+ a commit made in another terminal arrives on the timer). `Enter` opens the
145
+ selected file in the viewer, when the row names one to open: git collapses an
146
+ untracked tree to a single `dir/` entry and there is no directory view to open
147
+ one in, and a deletion is a path git has just finished saying is gone. Enter
148
+ does nothing on those, rather than trading the view you are reading for a
149
+ viewer saying "no such file".
150
+
151
+ **files** — read-only markdown and source, and a way to reach any of it.
152
+ Markdown is rendered, not shown as source: headings, lists, tables, quotes, GFM
153
+ alerts, footnotes, and syntax-highlighted fenced code. `t` swaps that for the
154
+ source it was rendered from, highlighted and numbered like any other file, and
155
+ back. Source files get highlighting and a line-number gutter. Everything is
156
+ pre-wrapped to the pane's exact width and scrolled by physical row, so
157
+ jump-to-end lands where you asked. On startup it opens the newest markdown under
158
+ the root; after that it follows what gets written. If something arrives while
159
+ another view is showing it waits, and the border says `◆ Alt+E` rather than
160
+ switching under you.
161
+
162
+ A second `Alt+E` opens the **file list**: a gitignore-aware directory browser
163
+ starting wherever the open file lives. `Enter` descends or opens, `Backspace`
164
+ climbs — and lands on the directory you just left, so walking back up is a place
165
+ rather than a reset. `/` finds a file by name anywhere under the root, matched
166
+ as a subsequence over its path and ranked so a hit in the file name beats one in
167
+ a directory name; `Esc` cancels the find and stays put, because being thrown out
168
+ of the pane by the key that means "never mind" is the worst thing a filter box
169
+ can do. The list is the second half of the never-switch-under-you rule: a
170
+ document arriving from the watcher waits while you are walking a tree, exactly
171
+ as it waits behind the git view.
172
+
173
+ **shell** — `Alt+S`, and the reason it is here rather than in another window:
174
+ `git branch`, `uv run ruff format`, `cargo test`, run in the directory abeam was
175
+ pointed at, next to the session that is about to be told what they printed. It
176
+ is a real pty — `pwsh`, falling back to `powershell` then `cmd`, or whatever
177
+ `ABEAM_SHELL` names — started the first time the view is drawn and never before,
178
+ so a session that never asks for one never pays for it. `Alt+J`/`Alt+K` scroll
179
+ its history without focusing it, which is why they are not the arrow keys the
180
+ shell would read as history. A child that exits leaves its last screen up with
181
+ `Enter` to start another, and while it is dead `Esc` means what it means
182
+ everywhere else.
183
+
184
+ Nor will abeam close out from under it: Claude exiting holds the door rather
185
+ than killing whatever is running, and says `shell open · Alt+Q to quit` in the
186
+ left title. That is "open", not "busy" — ConPTY cannot be asked whether a
187
+ command is running, so a shell sitting at a prompt holds the door exactly as a
188
+ build does. Type `exit` in it, or `Alt+Q` twice.
189
+
190
+ **pty diagnostics** (`F2`) — what the emulation layer is doing: alt-screen,
191
+ application cursor, bracketed paste, mouse mode, byte counts, resize count, and
192
+ the pty and parser sizes side by side, to be compared with the pane you can see
193
+ rather than with each other. **DSR answered** is the one that matters:
194
+ ConPTY blocks until its opening cursor query is answered, so a red zero there
195
+ means the session is hung rather than slow. `docs/conpty-findings.md` explains
196
+ each field and why it is on screen.
197
+
198
+ ## Layout
199
+
200
+ 60/40 split, and below 60 columns the right pane collapses entirely rather than
201
+ squeezing Claude into 36. The pty is sized from exactly the rect that was drawn,
202
+ once per frame, which is also what coalesces a window drag into a single ConPTY
203
+ resize.
204
+
205
+ ## Repository
206
+
207
+ ```
208
+ crates/abeam-pty/ the pty host layer: ConPTY session, input encoding, DSR
209
+ crates/abeam/ the binary: shell, layout, focus, panes
210
+ crates/abeam/tests/end_to_end.rs abeam itself, hosted in a pty and typed at
211
+ docs/conpty-findings.md what the spike learned. Read before touching the pty.
212
+ docs/keymap.md the keybinding collision audit
213
+ pyproject.toml maturin's instructions for wrapping the binary
214
+ .github/workflows/ ci on every push; release on a v* tag
215
+ ```
216
+
217
+ ## Releasing
218
+
219
+ Tag it and push the tag:
220
+
221
+ ```
222
+ git tag v0.0.1 && git push origin v0.0.1
223
+ ```
224
+
225
+ `release.yml` builds the wheel on Windows and uploads it to PyPI through
226
+ **trusted publishing** — an OIDC exchange between GitHub and PyPI, so there is
227
+ no API token in this repository to leak or rotate. PyPI is configured to trust
228
+ one repository, one workflow file and one environment (`pypi`); renaming any of
229
+ the three breaks the publish, deliberately.
230
+
231
+ The version lives in `[workspace.package]` in `Cargo.toml` and nowhere else. The
232
+ workflow refuses to run if the tag disagrees with it, because a release where
233
+ those two differ is discovered by whoever installs the wrong thing.
234
+
235
+ `cargo run -p abeam-pty --example host` is a complete pty host in one file, kept
236
+ as the manual regression harness and as proof that `PtySession` is sufficient
237
+ without abeam. The one thing that reproduces the ConPTY stall itself is the
238
+ ignore-DSR arm of `conpty_stalls_until_the_dsr_query_is_answered` in
239
+ `crates/abeam-pty/tests/conpty.rs`, which pins it in both directions.
240
+
241
+ Build and test with `--all-targets`, or the examples bit-rot:
242
+
243
+ ```
244
+ cargo build --workspace --all-targets
245
+ cargo test --workspace
246
+ cargo clippy --workspace --all-targets
247
+ ```
248
+
249
+ ## Status
250
+
251
+ Working, and used. Not finished.
252
+
253
+ **Done.** The pty host layer, proven by a spike that ran a real Claude session
254
+ against it on 2026-08-01. All three right-hand views, the file list and the find
255
+ under it, the rendered/source toggle, and the watcher driving what it should.
256
+ Focus, zoom, help, the diagnostics view, and the literal-next escape hatch.
257
+ 248 tests, `clippy --all-targets` clean.
258
+
259
+ **Verified how.** The whole path — pty → parser → pane, git worker → real `git`
260
+ → rendered rows, watcher → both panes, Enter in git → open in files — is
261
+ covered by tests that spawn a real ConPTY child and draw real frames.
262
+
263
+ On top of that, `crates/abeam/tests/end_to_end.rs` does to abeam what abeam does
264
+ to Claude: it spawns **the built binary** in a ConPTY, types at it as bytes, and
265
+ reads the screen that comes back. That is what proves the parts no in-process
266
+ test can reach — that abeam starts at all, that raw mode and the alternate
267
+ screen survive being someone else's child, that `Alt+S` written as `ESC s`
268
+ becomes the binding it should, and that a command typed into the shell view runs
269
+ in the right directory and puts its answer on screen. Three paths are pinned that
270
+ way today: type a command in the shell and read its output; reach a file nothing
271
+ pointed the pane at, by `Alt+E` `Alt+E` `/`; and a copy of a real shell planted
272
+ in the repository under the name abeam is about to look for, which abeam must
273
+ refuse to run. That last one is a test about an attack rather than a feature —
274
+ Windows resolves a bare program name against the *calling* process's directory
275
+ before it consults `PATH`, so a `pwsh.exe` committed to a cloned repo was one
276
+ keystroke from executing. For the whole session abeam now stands in
277
+ `%SystemRoot%` and hands its pty absolute paths only.
278
+
279
+ What none of it covers is a human at a terminal. The six pass criteria in
280
+ `docs/conpty-findings.md` (rendering, one character per keystroke, editing keys,
281
+ live resize, paste, `/exit`) were confirmed against the spike's host and have
282
+ not been re-run against `abeam` itself since the panes landed. Do that before
283
+ trusting it with real work.
284
+
285
+ **Not done, and known.**
286
+
287
+ - **The shell view has never been driven by a human.** A test types `set /a
288
+ 123*456` into the real binary and reads `56088` back off the screen, which is
289
+ more than a smoke test and still less than use: the six pass criteria above
290
+ were confirmed against Claude in the *left* pane, and a shell in a
291
+ 46-column right pane is not the same thing. Expect the first real `cargo test`
292
+ run in there to find something about width, wrapping or the scrollback that no
293
+ test thought to ask about.
294
+ - **`Alt+J` is on borrowed time.** Claude has a live `app:toggleTerminal` action
295
+ with no default key, and its footer already prints `meta + j` as a fallback —
296
+ so Claude's own UI advertises a key abeam has claimed. Nothing is bound today
297
+ and the invariant holds; the day it is bound, abeam has to move.
298
+ `docs/keymap.md` carries the details.
299
+ - **An npm-installed `claude` cannot be hosted.** `npm i -g` puts three files in
300
+ `%APPDATA%\npm`: `claude` (a POSIX shell script, no extension), `claude.cmd`
301
+ and `claude.ps1`. Windows starts only `.exe` and `.com` directly, so plain
302
+ `abeam` fails at startup for those users — and the two obvious fixes are both
303
+ wrong: preferring the `.cmd` does not help, because `CreateProcessW` cannot
304
+ run that either, and going through `cmd.exe /c` hands abeam's own argv to a
305
+ command-line re-parser that treats `&`, `|` and `^` as syntax. The native
306
+ installer (`claude.exe`) is unaffected. Fixing it properly is a decision about
307
+ how abeam launches its main program, not a patch.
308
+ - **Windows only.** Not a portability bug so much as an absence of work: nothing
309
+ outside `abeam-pty` is platform-specific, but nothing has been tried.
310
+ - **No configuration.** Keybindings, the split ratio and the refresh interval are
311
+ all constants. Claude's own bindings are user-configurable, so abeam's should
312
+ be too before anyone else uses it.
313
+ - **No diff view.** The git pane shows *which* files changed and by how many
314
+ lines, not what changed in them.
315
+ - **The first source file with code in it costs a visible hitch** of 100–200 ms,
316
+ while syntect deserialises its syntax and theme dumps. A session that only
317
+ reads prose never pays it.
318
+ - **Laying a document out happens on the frame path**, and stalling a frame
319
+ stalls Claude's keystrokes. Reading is the cheap half; parsing, highlighting
320
+ and wrapping is the expensive one, and it runs again at every new width, so
321
+ dragging the window border with a large document open pays it per frame. The
322
+ only bound on it is the 512 KiB read cap — about 210 ms of layout in a release
323
+ build, measured — and going over that is visible: the pane says where it
324
+ stopped. Highlighting gives up above 64 KiB and shows plain text. A slow
325
+ network share can still stall the frame that opens a file.
326
+ - **Source highlighting assumes a dark terminal** (base16-ocean.dark). A TUI
327
+ cannot ask the terminal for its palette. Backgrounds are discarded so that a
328
+ light theme is washed out rather than unreadable.
329
+ - **UTF-16 files are reported as binary.** The sniff is a NUL byte in the first
330
+ 8 KiB, which is what git does.
331
+ - **Two Claude features are unreachable inside abeam**, and both will be reported
332
+ as abeam bugs: `Ctrl+Shift+B` and `Ctrl+Shift+C` are indistinguishable from
333
+ `Ctrl+B` / `Ctrl+C` in legacy terminal encoding, and hold-to-talk voice needs
334
+ key *release* events, which abeam drops for a load-bearing reason
335
+ (`docs/conpty-findings.md`, constraint 3).
336
+ - **`EnableMouseCapture` disables your terminal's native text selection.**
337
+ Copying out of abeam needs Shift+drag, and which terminals honour that varies.
338
+ - **The `Alt` namespace is free against one audited Claude build** (2026-07-25).
339
+ It is not guaranteed free forever; `Ctrl+\` is the mitigation.
340
+
341
+ ## A warning for anyone changing the pty layer
342
+
343
+ `docs/conpty-findings.md` lists five constraints that look like things to tidy
344
+ up and are not. The shortest version: ConPTY hangs forever if you do not answer
345
+ its opening `ESC [ 6 n`, `wait()` never returns so only `try_wait()` is safe,
346
+ Windows sends a Release event for every key and forwarding it double-types
347
+ everything, mouse reports must be gated on what the hosted app enabled, and
348
+ `Screen::contents()` tells you nothing about layout.
349
+
350
+ There are tests pinning all five. Read the document first.
351
+
352
+ ## License
353
+
354
+ MIT. See [LICENSE](LICENSE).
355
+
@@ -0,0 +1,6 @@
1
+ abeam-0.0.1.data/scripts/abeam.exe,sha256=MegVq8AWOs31xbJIdnOCa2h-7R_EsnLXFszqfH6dMYE,4658688
2
+ abeam-0.0.1.dist-info/METADATA,sha256=n9lAlt3T7qSPYdBLbpcTpD9ZIUfwkvGbLcud3DK9qnM,19293
3
+ abeam-0.0.1.dist-info/WHEEL,sha256=2zDlIYIdD4m4N3p5DVEG3iJhGLdhsBQgdH-FqVkAur8,94
4
+ abeam-0.0.1.dist-info/licenses/LICENSE,sha256=WjN_Zhc7AK0DglJTwdUcgqkGSnV7EoTqdttW5GcWHAE,1091
5
+ abeam-0.0.1.dist-info/sboms/abeam.cyclonedx.json,sha256=Ly_Et7ZxkwpsSDUahZgLw3hV_xUluNt3oGgN22tlbzY,237127
6
+ abeam-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.14.1)
3
+ Root-Is-Purelib: false
4
+ Tag: py3-none-win_amd64
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Phil Harrison
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.