Skip to content

aterm 0.102.0

Choose a tag to compare

@alabsystems alabsystems released this 10 Oct 04:08
· 39 commits to main since this release

aterm is the terminal for AI. New here? What each file is:

  • aterm-0.102.0.dmg — the signed, notarized app as a drag-install DMG. The ALab toolchain installs itself on first launch (or on demand via aterm pkg install --default-set).
  • aterm-0.102.0-mac.zip — the same app as a zip; this is the container the in-app updater and Homebrew stage from.
  • aterm.dmg / aterm-mac.zip — permanent releases/latest/download/ names for the DMG and the zip above (byte-identical copies).
  • .sha256 files verify a download: shasum -a 256 -c <asset>.sha256.
  • aterm-appcast.toml / aterm-machines.toml (and their .sig) are consumed by the in-app self-updater — not for humans.
  • Releases named atpkg-index-N are machine-readable package indexes, not app releases.

Added

  • Code has one home, ~/Developer, and aterm measures that Spotlight skips it. Spotlight
    indexes every text file in your home and, since macOS 27, attaches an embedding to each, so
    source trees there cost mds_stores real CPU. Apple documents one way to keep a folder out
    of the index: System Settings ▸ Spotlight ▸ Search Privacy. Add ~/Developer there once per
    Mac. aterm doctor and aterm pkg doctor gain two rows: devroot: names the git checkouts
    and worktrees under your home that are not in ~/Developer, and spotlight: MEASURES
    whether Spotlight skips ~/Developer (a probe file and a later control, both removed), never
    assuming it from the name (a ~/Developer that is a link is never opened, and is not
    measured). aterm pkg devroot lists each checkout outside, where it would go and what
    holds it; aterm pkg devroot move --all (or named checkouts) moves them, refusing the
    whole run while any has uncommitted work, a process working in it or in another checkout
    of its repository, or an agent session, and never splitting a repository that has
    submodules (their links are relative). It moves one repository at a time and repairs what
    names the old paths — worktree links (git worktree repair), the git config and
    alternates of every git directory, submodules' included (a cut clone's
    aterm-release.receiptsFrom), Claude Code's ~/.claude/projects/<path-key> folders (not
    the transcripts in them), aterm pkg link dev links and the publication engine's registry
    rows — then checks that every checkout still answers git status. A ^C waits for it to
    finish. aterm pkg devroot verify measures the root on demand. The window's six-hourly look
    raises one entry in Messages ▸ To look at, only when it measures Spotlight indexing the
    root, and its button opens Search Privacy. The move is a one-time migration: it goes once
    every owner Mac on the release roster reports a clean devroot: row; the survey, the rows
    and the measurement stay. The window's disk cleanup looks for idle build output in the
    checkouts in ~/Developer as it does in your home's.

Changed

  • The supervisor's trust roots and the publication engine follow ~/Developer.
    [harness] trust_roots defaults to ~/Developer/aterm*, ~/Developer/ay*,
    ~/Developer/trust* and /private/tmp/claude-*, so a checkout left in your home is
    no longer answered for. The release tools and the export content scan read the engine
    at $PUBLICATION_ENGINE, else ~/Developer/publication. The agent primer (v14) says
    where code lives. Run aterm pkg devroot move --all right after updating: until the
    checkouts move, the merge gate's export content stage is NOT RUN (its reason names
    ~/Developer/publication and the move), and the supervisor answers for no session in
    the old checkouts.

  • A rerouted tool name warns in one line and runs, instead of stopping your command.
    Inside an aterm session, clippy, rustfmt, rustdoc and lean with no Trust tool
    installed now run the upstream copy after a line naming aterm pkg install <program> or
    aterm pkg repair (they exited 127), z3 runs upstream after a line naming ay (it exited
    2), and so does a stub that cannot reach aterm's package manager. Only a name with nothing
    to run still exits 127; [reroute] announce = false silences the lines and [reroute] strict = true brings back the refusals (aterm help reroute). One place refuses by design:
    inside a Trust source checkout a cargo or rustc that is not Trust (next entry).

  • A bare cargo or rustc in a session now says what it is about to run — Trust or
    stock, by name — and inside a Trust source checkout a stock one does not run.
    The line
    used to say "running upstream 'rustc' (no proof claim)" whatever that name ran, so after
    rustup default trust it said it over Trust itself. Now aterm asks the copy on PATH what
    it is (through rustup which when it is rustup's proxy, +toolchain included, then that
    file's -vV) and names it: -> Trust 7d5533b2, via rustup trust (the rustup default), or
    -> STOCK rustc 1.97.1 (…), via rustup 1.97.1-… (pinned by ./rust-toolchain.toml); not Trust, plus one line naming the file. STOCK is said only when nothing in the answer marks
    Trust: a build that prints a Trust mark but no trust: version (the cargo of the Trust
    seal e9ca4908) is "not known to be Trust", and so is a copy that does not answer as a Rust
    tool; neither is ever called Trust. Inside a Trust source checkout (an x.py beside a
    src/stage0 saying compiler_dist_channel=trust) a cargo/rustc that is not Trust
    exits 2 without running, naming the Trust spelling and how to run it anyway, a session
    started with aterm --no-reroute: there a stock compiler would build part of Trust. A
    cargo/rustc a tool spawns there by name is judged the same — a cargo subcommand, an
    xtask, a build script running rustc +nightly, the compiles a stock cargo started by its
    path spawns — including beneath a Trust cargo the reroute let run (its children are not
    announced again, but no longer inherit the escape), with no package store resolving, and
    with aterm's package manager unreachable (then a stub refuses there outright). A compiler
    run by its own path, or handed to cargo as RUSTC, is looked up by no name and is not
    judged. [reroute] announce = false does not silence the refusal. The answer is cached in
    the package prefix (reroute-identity.cache), but only for rustup's answers and a
    toolchain's own compiled cargo/rustc; a wrapper or shim in front of them is asked on
    every call, because it can run a different compiler in each directory.

Removed

  • The Spotlight rename pass and [machine] spotlight_noindex. aterm no longer walks your
    home renaming cargo target folders to target.noindex behind a symlink: the name trick is
    not an Apple mechanism, and the developer root above replaces it for your checkouts.
    aterm pkg noindex scan|migrate|apply, the Spotlight half of aterm pkg machine apply,
    its row on Settings ▸ Security and the spotlight_noindex key are gone (a file that still
    sets the key gets the ordinary unknown-key note). aterm pkg noindex verify <dir> stays,
    as the measurement. Folders an earlier pass renamed are left as they are, and still build.
    The trees aterm's own gate and release tools make beside a checkout keep their .noindex
    names for now (<checkout>-verify.noindex, -verify-cells.noindex,
    <clone>-release.noindex, <checkout>-cut.noindex, dist/cut-<build>.noindex): on a
    Mac whose code is not yet in ~/Developer they keep Spotlight off build output, and they
    go once every owner Mac has moved.

Fixed

  • A pane kept open after its command ended has no clickable links in its scrollback after an
    update.
    Since 0.101.0 a --hold pane's scrollback crosses an update along with its last
    screen. The links on that screen were removed, because nothing runs behind the pane, but the
    links in the scrollback above it could still be clicked. The scrollback now crosses with every
    word and colour and no links, the same as the screen.

  • Resizing the window no longer deletes spaces or misplaces the prompt. A space just before a wide
    character (CJK, emoji) at the end of a wrapped line was deleted by a width change, words in history
    could run together after a rewrap, and after a full-screen program exited following a resize the
    prompt could land on top of earlier output.

  • Neovim's cursor stays on the right character on lines with CJK or emoji. Neovim 0.11 and later
    turn on a grapheme mode in which aterm moved the cursor by whole characters instead of by cells.

  • Copy and find give back exactly what is on screen. Copying a wrapped line no longer drops the
    space at the wrap point; with right-to-left text (Hebrew, Arabic) the selection now copies the words
    it highlights; find and double-click measure wide ambiguous characters as drawn; find gives the same
    results for a line on screen and in history; and dragging over double-width text selects what is
    under the pointer.

  • Wide ambiguous symbols are two cells in wide mode. With ambiguous_width = "wide", symbols such
    as ★ ♪ ☎ ❶ were drawn one cell wide.

  • A terminal session no longer freezes when you paste into a program that is not reading. Running
    aterm as a session in another terminal, pasting or typing ahead more than about a kilobyte into a
    program that prints but does not read its input (ping, tail -f, a build) stopped everything for
    good: no output, no keystrokes, no resize. The program's output keeps flowing now, and what it has not
    read yet waits until it does.

  • A resize while aterm starts reaches the program. A terminal resized in the moment a session
    started (a window manager or split laying it out) left the program at its starting size until the next
    resize.

  • Zooming the font over a picture no longer uses gigabytes of memory. An inline image (imgcat,
    sixel, Kitty) was decoded at the size of its cells at the new font size, so zooming a window-wide
    picture to the largest font asked for about 2 GB a step and could reach 13 GB. A picture now decodes
    to at most a full-window raster, however far the font is zoomed.

  • Screens full of pictures or large emoji repaint without starting over. Showing more images than
    the image cache held at once (two window-wide photos, layered Kitty images, a sheet of thumbnails)
    decoded every one again on every repaint, a tenth of a second to seconds each time; drawn without a
    GPU, a screen of distinct emoji at a large font was rasterized again on every full repaint. What the
    screen shows now stays decoded and rasterized between repaints.

  • Caps Lock and non-US keyboard layouts type what the key shows. Caps Lock + A typed a; on a
    German, French, Russian or other non-US layout, Shift typed the US character for that key (German
    Shift+2 gave @, French number-row digits could not be typed, Russian capitals came out lowercase),
    and Ctrl with a Russian letter typed the letter instead of the control key (Ctrl+С now interrupts).
    Typing now sends the character your layout produced, and Ctrl+letter on a non-Latin layout sends the
    same control key as on a US keyboard.

  • Option keeps working on Backspace, Enter, Tab and Escape when Option is not Meta. With
    option_as_meta = false, these keys lost the Option modifier (Option+Backspace erased one character
    instead of a word in shells that bind it).

  • Ctrl+Alt+Backspace deletes a word and Ctrl+Alt+Space reaches the program. They were sent without
    the Alt. Ctrl+~ now sends the control character other terminals send.

  • The mouse over a split pane talks to that pane. Hovering, scrolling or right-clicking over a pane
    that does not have focus went to the focused pane's program instead; a button released after switching
    tabs or panes went to the wrong program; releasing one of two held buttons stopped drag reports; and a
    program tracking the mouse got a report for every pixel moved inside a character cell.

  • Output that scrolls a huge count no longer freezes the window. CSI 65535 S (scroll up) archived one
    history line per count, so a few kilobytes of it from any program, a cat of a file or an ssh session,
    held the terminal for seconds and froze the window's redraw. A scroll of a screenful or more now
    archives exactly that screenful and leaves the screen blank, as xterm does.

  • Links in scrollback can no longer use gigabytes of memory. The web addresses of links (OSC 8) that
    had scrolled into the in-memory history were counted by no limit, so output full of long, distinct
    links could hold about 4 GB in one tab, out of reach of the memory-pressure trim. They now count
    toward the tab's history budget: past a quarter of it, the oldest rows' links are dropped (the text
    stays), and the memory-pressure trim frees them.

  • Copy mode's cursor stays on its text. With output streaming, the vi cursor on a fixed row below a
    scrolling area (Codex's composer, apt's progress bar) was dragged up a row per line of output; it now
    stays put while rows in the scrolling area and in history still carry it with their text. A resize,
    clear or a lowered history limit no longer leaves it off the grid or on other text.

  • A control client that stalls or stops reading no longer turns the other clients away.
    Eight connections could tie up every control thread, and every other client (aterm ctl,
    each supervised tab's harness) was then answered ERR control server busy; retry until
    they let go. A connection did this by stopping part way through its AUTH line (no token
    needed), stopping part way through a request or a feed-bin/paste-bin body, or not
    reading a long answer (aterm ctl help --full into a pager that pauses). A control thread
    now takes a connection only once it holds a whole request, and it does not wait for the
    client to read the answer; a pager that pauses still gets every byte.

  • The control verbs wait, ready and await refuse a timeout that is not a whole
    number of milliseconds.
    wait 2s, ready timeout=1.5 and await idle 500 timeout=5s
    waited the 30 s default instead, and wait 2s answered a command that had already
    finished as if 2s were fine. They now answer their usage line, as turn timeout=
    already did. A number still waits that long, held at 600 s.

  • The cursor pet no longer flashes an old cat you could not see after a screen or pane switch.
    When the pet changed breed under a text selection, the cat it was replacing was hidden by the
    selection, yet an alternate-screen enter or a split-pane switch brought it back at full
    brightness for a moment (or let the new cat jump straight to full brightness beside it). A cat
    now crosses those switches at the brightness it was last drawn at, and one that was hidden
    does not come back.

  • aterm --headless no longer connects to the WindowServer (macOS). A headless
    instance built the real AppKit event loop, and creating its application object opened
    a WindowServer connection, so WindowServer ran a synchronous privacy check of the
    binary on its own main thread for every headless launch: one per idle instance, 50 in
    one run of the headless integration tests. From a build directory holding a very large
    number of files that check has outlasted WindowServer's watchdog and taken every window
    on the machine down with it. A headless launch now runs a display-free event loop with
    no application object, so neither opens a WindowServer connection and LaunchServices
    holds no record for the process. (A headless render on the GPU of a Mac with several
    GPUs, none of them low-power, still asks Metal for the system default device, which
    does connect.) aterm ctl chrome on a headless instance now answers menu (none) (it
    answered AppKit's default menu, which only existed because of that object), and a
    headless instance paces its frames at the fixed 8 ms interval, as a display-free Linux
    run always did.

  • Vi mode moves by the text you see when the view is scrolled back. Scrolled back, a
    motion on a line of the live screen (w, e, b, ^, %, {, }, f, t) read the
    history line that many rows above it and landed mid-word; H, M and L jumped to the
    live screen instead of the top, middle and bottom rows on view; and output that scrolled
    the screen left the cursor on its row while its text moved up, so it sat on the wrong line.
    Every motion now reads the line the cursor is drawn on, and the cursor moves with its text.

  • aterm pkg verify no longer calls an untouched install drifted. On a Mac's default
    case-insensitive disk, an archive that laid two directories whose names differ only in case
    or Unicode form (Dir/ and dir/) merged them on disk while aterm recorded both, and a
    home folder on an HFS+ disk stored an accented name under other bytes than the archive's, so
    the install's recorded fingerprint described files that were not there. Such an archive is
    now refused at install, with nothing left behind.

  • A Containment shell can no longer change your preferences or register a login item.
    Under --containment containment (aterm --sandbox), a contained program could write any
    app's preferences (defaults write), which the macOS preferences daemon saves outside the
    sandbox, and could ask the ServiceManagement daemon to register a login item or launch
    daemon that runs outside the sandbox later. Both are refused now; shells, git, Python,
    compilers and codesign work as before.

  • A program's output can no longer change what Escape and Enter send. Output holding
    the sequence a Windows console host uses to ask for key records (cat of a file, an ssh
    session) switched the tab's Escape, Ctrl+[, Alt+[, Alt+Backspace and Shift/Ctrl+Enter to
    Windows key-record sequences for every program after it, until a reset: vim read them as
    stray keys, and an agent's Escape did not stop it. Only a Windows console host turns that
    mode on now; a web host in front of one turns it on with set_win32_input_enabled.

  • Dropping a file into PowerShell or Git Bash can no longer run code from its name
    (Windows).
    A dropped or pasted file whose name held $(…), a backtick or a curly double
    quote (a download, a file from an archive) ran a command when you pressed Enter. Such a
    name is now inserted in single quotes.

  • Pasting text that holds a NUL character or half an emoji works (macOS). A clipboard
    holding a NUL character pasted only the text before it, and one holding half of an emoji
    (left by an app that cut text in the middle of one) pasted nothing. Copied text beginning
    with an invisible byte-order mark (U+FEFF) lost it. Text now crosses the clipboard, input
    methods, the tab-rename field and dropped file names exactly, and a lone half of an emoji
    shows as �.