| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
| |
Every project now maps to a stable Claude Code session color: an override alist wins, else a character-sum hash of the project basename picks one of the eight names. When a fresh tmux session is created (never on reattach), a poller waits for the TUI to boot and types /color <name> itself, with the Enter deferred a beat so the slash-command menu can't swallow it. Two refusals keep the injection safe: the bypass banner must be on screen (a bare shell never gets typed into) and the prompt line must still be empty (typed-ahead input is never corrupted).
|
| |
|
|
| |
Claude Code's /color picks a session accent from eight names, each emitted as a fixed xterm-256 index (probed against v2.1.198 by cycling /color in a scratch tmux session and reading the SGR codes). Agent terminals now pin all eight indices plus the bypass banner to dupre faces, so any /color choice renders in the theme's palette instead of stock xterm hues. dupre has no orange or pink, so those borrow red+1 and magenta+1. If a Claude Code update moves an index, the stock hue comes back (the alist docstring carries the re-probe note).
|
| |
|
|
| |
Claude Code draws its accent (the bypass-permissions banner, borders, spinner) with xterm-256 palette codes, and the stock rose red is palette index 211. eat resolves those codes through a per-terminal face vector, so agent terminals now point index 211 at the new cj/ai-term-accent face (dupre blue #67809c) at creation. Every other eat terminal keeps the true palette. Per-project colors can later ride the same per-terminal mechanism.
|
| |
|
|
| |
The message duplicated the modeline directly above it: the buffer name and the eat state icons already announce which agent is focused, so the echo just lingered as clutter. The no-other-agents echo stays, since it reports something the modeline can't.
|
| |
|
|
|
|
|
|
| |
ai-term.el had grown to ~1,215 lines mixing project/tmux session discovery, window display policy, the EAT terminal backend, and the public commands, so a change to any one risked coupling to the others.
I extracted three layers, following the calendar-sync split shape: ai-term-sessions.el (discovery, tmux naming and parsing, launch command, picker ordering), ai-term-display.el (display-buffer actions and rule, toggle state, server-window routing), and ai-term-backend-eat.el (terminal create/reattach, pty send, EAT keymap). The backend file is named for its backend so a future one lands as a sibling. ai-term.el stays the public face (options, commands, keymap, shutdown), every name unchanged, so existing (require 'ai-term) callers and all 30 test files work as before.
The extracted layers forward-declare the face's defcustoms rather than requiring it, keeping the graph acyclic. I dropped the unused cl-lib and host-environment requires and added the three modules to the header-contract list.
|
| |
|
|
| |
With a single agent open and focused, the rotation wrapped back to the same agent and echoed a misleading "Agent: <name>" as if it had swapped. Now it says there are no other ai-terms to switch to. A sole agent that is displayed but not selected still gets selected, and the no-agents picker fallback is unchanged.
|
| |
|
|
|
|
| |
F1 (cj/dashboard-only) kills every other buffer, burying only those on the undead list. Agent buffers were never registered, so the sweep killed live agents and detached their sessions.
Agent buffers are a dynamic family ("agent [<project>]") that an exact-name list can't pre-enumerate, so undead-buffers gains a regexp list (cj/undead-buffer-regexps) and a centralized cj/--buffer-undead-p predicate. Both kill paths route through it. ai-term registers the "agent [" pattern, so every agent -- current or future, however created -- is buried rather than killed.
|
| |
|
|
|
|
| |
Summoning the agent (M-SPC) into a single-window frame docked it at the default fraction even when it was last fullscreen, because the size-memory model only captured split geometry at toggle-off and a sole window has no dock size to record. A window-configuration-change-hook tracker now records whether the displayed agent fills its frame (cj/--ai-term-last-fullscreen), and display-saved restores it in place when that flag and a single-window frame both hold.
The tracker records only that flag, not dock geometry: re-capturing the dock size on every window change fed a capture/replay loop that drifted the dock height a couple rows per cycle. The restore guard uses (one-window-p t) so an active minibuffer (a picker prompt mid-summon) isn't counted as a second window, which otherwise misfired the restore into a dock and cascaded.
|
| |
|
|
| |
22 module headers carried long user-manual commentaries (quick-starts, keybinding matrices, setup walkthroughs) that belong in user docs, not source. Each now states the purpose, load contract, and entry points tersely. ai-term also drops its stale F9 keybinding references (the scheme is C-; a plus M-SPC now) and a header line claiming a vertical-split that's really host-aware.
|
| |
|
|
| |
Add cj/completion-table (and an annotated variant) to system-lib: a wrapper that tags any collection with a completion category so marginalia, embark, consult, and sorting can recognize the candidates. None of the config's completing-reads declared a category, so the rich-candidate pickers showed bare. This applies it to the four whose candidates match a standard category and so need no custom annotator: benchmark-method (function), ERC buffer switch (buffer), ai-term close (buffer), and theme switch (theme). Each now annotates for free.
|
| |
|
|
| |
Port ai-term from ghostel to EAT. Agents spawn in an EAT terminal running the same tmux session (tmux new-session -A -s aiv-<project>), so the persistence and detach/reattach model is unchanged. A spike confirmed EAT + tmux detach and reattach exactly like ghostel + tmux. The swaps: (ghostel) becomes (eat) with eat-buffer-name carrying the agent name, ghostel-send-string becomes a process-send-string helper, and the M-SPC swap chord is bound directly in eat-semi-char-mode-map (no exception-list plus rebuild dance). Buffer detection was already name-based, so the dispatch, next, and cycle logic is unchanged. Dropped the now-unused suppress-tmux variable. Tests updated to mock eat.
|
| |
|
|
| |
The next-agent step (C-; a n / M-SPC) cycled only live agent buffers, so a detached session (alive in tmux, no Emacs buffer) was reachable only through the picker. Now the queue is every active agent, live buffer or live session, keyed on the project dir and ordered by buffer name. Stepping onto a detached one attaches it: show-or-create recreates the terminal, which reattaches the tmux session. The live-buffer swap path is unchanged. I replaced the buffer-rotation helper with a dir-based one and added an active-agent enumerator, with 10 tests.
|
| |
|
|
|
|
| |
Add the three headless functions the rulesets wrap-it-up workflow calls via emacsclient -e, since this module owns the aiv- session naming, the agent buffer, and the geometry restore. cj/ai-term-quit kills a project's tmux session and agent buffer and restores the layout, idempotent and safe when already gone. cj/ai-term-live-count returns the integer count of live aiv- sessions for the shutdown safety gate. cj/ai-term-shutdown-countdown re-checks that gate, then runs an abort-able run-at-time countdown in the echo area and, uncancelled, runs the shutdown command (a defcustom so tests stub it). Reuses the existing kill/close helpers. 13 ERT tests cover the live-count parsing, the quit kill-and-idempotency, and the gate-abort/cancel/tick logic; the tmux and shutdown side effects are manual.
Claude-Session: https://claude.ai/code/session_01BqrdWUo9GcznYX2pZr76gZ
|
| |
|
|
|
|
|
|
| |
I moved the ai-term family off the F9 keys onto the C-; a prefix, vacated when gptel was archived: a toggles the agent, s opens the project picker, n swaps to the next agent, k closes one. The frequent swap also gets M-SPC as a fast chord, bound in ghostel-mode-map and added to the semi-char exceptions so it reaches Emacs from inside an agent buffer.
cj/ai-term-next now opens the project picker when no agent is running instead of erroring, so the swap key doubles as a "start an agent" key.
To free M-SPC, I removed jumper's M-SPC binding. Jumper's commands stay reachable via M-x, with a cleverer home pending review.
|
| |
|
|
|
|
| |
s-F9 (cj/ai-term-next) steps through the open agent buffers in name order. It's the "switch among existing agents" surface F9's toggle never provided. The cycle logic lives in a pure helper (cj/--ai-term-next-agent-buffer) with Normal/Boundary/Error coverage. The command is a thin window-mutating wrapper.
I dropped the C-S-F9 close alias, leaving M-F9 as the sole close binding. I moved cj/server-shutdown off C-<f10> to C-x C so the key keeps forwarding to the terminal program inside an agent buffer. I also removed the now-unused F10 entries from term-config's ghostel exceptions.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
In a layout where the agent had its own split window (e.g. code on top, a
working window, and the agent below), toggling the agent off deleted its
window correctly, but toggling back on reused the working window at the edge
-- displacing its buffer and collapsing three windows to two. The slot-reuse
that avoids a third window on a fresh show was firing on a re-show after the
agent's own window was already deleted.
Flag the toggle-off that deletes the agent's own window; on the next
toggle-on, reuse-edge-window consumes the flag and falls through to a fresh
re-split, so the agent returns to its own window and the other windows are
untouched. The flag only changes the 3+ window case -- after a delete in a
2-window slot-reuse layout one window remains, where re-split and reuse-edge
already coincide, so the existing reuse-edge tests are unaffected.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The F9 toggle captured the agent window's body-height and replayed it as
body-lines. Body-height subtracts the mode line's pixel height, which differs
between an active and an inactive mode line; the agent is captured active but
redisplayed inactive, so under a theme whose mode-line-inactive is shorter than
a text line the window lost ~1 line per toggle.
Capture and replay total-height for the vertical axis instead, via the renamed
cj/window-replay-size. Total-height is identical active or inactive and has no
mode-line-pixel dependence, so the round-trip is a fixed point. Width keeps
body-width (total-width has the position-dependent divider problem that total-
height does not). The shared lib fix covers the F12 terminal toggle too.
The shrink only manifests in a GUI frame, so it is not reproducible in the
batch harness; the unit tests pin the new total-height contract.
|
| |
|
|
|
|
|
| |
Lift the F9 toggle-off branch (~50 lines) out of cj/ai-term into
cj/--ai-term-toggle-off, leaving the pcase arm a one-liner. Fold the three
copies of the most-recent-non-agent-buffer swap idiom (two in the toggle-off
cases, one in cj/--ai-term-close-buffer) into cj/--ai-term-swap-to-working-buffer.
|
| |
|
|
|
|
| |
The F9 agent always docked as a right-side column on a landscape frame. On this 138-column frame that left ~68-column panes, too cramped to read code and the agent side by side. The F12 terminal and F10 playlist hardcoded a bottom split with no width-aware path.
I added cj/preferred-dock-direction and the cj/window-dock-min-columns defcustom (default 80) to the window-geometry lib: dock side-by-side only when the narrower pane keeps at least the minimum width, otherwise stack below. All three toggles now route through it. F9 drops its pixel-aspect rule. F12 and F10 gain a right-column width default and become adaptive. F10 keeps width and height size memory in separate vars so a resize on one axis doesn't leak to the other.
|
| |
|
|
| |
The agent window now docks from whichever edge conserves more space, chosen at display time: right on a landscape frame, bottom on a square or portrait one, replacing the host (laptop/desktop) branch. cj/--ai-term-direction-for-aspect is the pure decision; default-size pairs the width or height fraction with it.
|
| |
|
|
| |
M-F9 close deleted the agent's window, collapsing the layout. Close now swaps that window to the working buffer and kills the agent buffer, so the split stays put. F9 hide still collapses the split. Close no longer does.
|
| |
|
|
|
|
|
|
| |
F9 did nothing in an agent buffer: ghostel's semi-char mode forwards every key not in ghostel-keymap-exceptions to the pty, and ghostel-semi-char-mode-map outranks the major-mode map, so the F9-family and F12 bindings I'd put in ghostel-mode-map never fired. The keys went to Claude/the shell, which ignored them.
I added the F9 family (in ai-term) and F12 plus C-; (in term-config) to ghostel-keymap-exceptions and rebuilt the semi-char map with ghostel--rebuild-semi-char-keymap. add-to-list updates the list but not the already-built map, so the rebuild is what actually lets the keys through. C-; had the same latent bug for the same reason.
Two regression tests assert the keys are in the exceptions and that the rebuilt semi-char map no longer forwards them. I also corrected the spec note that claimed binding in ghostel-mode-map was enough (true for vterm, wrong for ghostel) and codified the gotcha.
|
|
|
I swapped the terminal engine from vterm to ghostel (libghostty-vt) everywhere. term-config replaces vterm-config (the F12 terminal, the C-; x menu, tmux history capture), and ai-term replaces ai-vterm (the F9 Claude-agent launcher). ghostel renders the agent TUI without vterm's flicker under heavy streaming, and one engine now covers every terminal workflow.
Two behavior changes fall out of the swap. F9 launches in a terminal frame now: ghostel renders in TTY frames, so the old GUI-only guard is gone. Terminal windows no longer dim when unfocused: ghostel resolves its palette into the native module per-terminal, so there's no per-window color hook to dim through the way vterm had.
auto-dim drops its vterm color-advice path, the dashboard Terminal button launches ghostel, and the vterm and vterm-toggle packages are removed. The tmux pane-history and copy-mode machinery carried over unchanged. It keys on the pty tty, which ghostel exposes.
|