Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,76 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.9.0] - 2026-08-22

### Changed

- **`--rm` is docker's `--rm`: the workspace goes when the session ends.**
`dl <ws> --rm` and `dl <ws> --rm -- <cmd>` hand over a session and delete the
workspace and its clone once it ends — which is what `--autorm` did, under the
name docker gives it. The `rm` verb is unchanged and is the only way to delete a
workspace *now*, so the whole grammar is `docker rm` beside `docker run --rm`,
and neither spelling has to be read twice to work out which was meant:

```bash
dl kinisi/repo@fix/x rm # delete it now
dl kinisi/repo@fix/x --rm # shell; it goes when you exit
dl kinisi/repo@fix/x --rm -- make test # one command, then it goes
aid kinisi/repo@fix/x 'fix the flaky test' --rm # the agent runs, then it goes
```

Everything the removal already promised is unchanged: it stops at work that is
nowhere else and leaves the workspace standing, it collects a build that died in
`postCreateCommand`, and the exit code is the session's and never the removal's.
`--force` still does not compose with it — that is `dl <ws> rm --force`, which is
where docker keeps its `-f` too.

### Removed

- **`--autorm` is now spelled `--rm`.** A rename and nothing else; the behaviour
above is what it always did. The old spelling is recognised and refused with the
new one rather than dropped, so a line recalled from history says what happened
instead of quietly doing nothing:

```
$ dl <ws> --autorm
--autorm is now spelled --rm: 'dl <workspace> --rm' opens the workspace and deletes
it when the session ends, the way 'docker run --rm' does. Use 'dl <workspace> rm' to
delete one now.
```

- **`--stop` is retired, and so is appending `--rm` to cancel a line.** Both were
the *suffix* form of a verb: typed at the end of a line that already asked for
something and winning over it, so `aid <ws> 'review this pr' --rm` deleted the
workspace and printed `--rm overrode the rest of the line`. That shape cannot
survive a `--rm` that means "delete when the session ends" — the two spellings
look like a pair, and one cancelling the line while the other runs it is exactly
the pair nobody can keep straight. So `aid <ws> 'review this pr' --rm` now runs
the review and deletes afterwards, and `--stop` refuses with the word to use:

```
$ dl <ws> --stop
--stop is no longer a flag: the flag spellings now modify a session (--rm deletes
the workspace once one ends) rather than name a verb. Use 'dl <workspace> stop' to
stop a workspace.
```

For "I am done with this workspace", `dl <ws> rm` names it and `dl rm` picks it —
and the pick marks several with TAB, which for a long `aid` prompt line is fewer
keystrokes than recalling it to type at the end. What is genuinely gone is
deleting a workspace without naming or picking it, by appending to whatever the
last line happened to be.

One consequence worth knowing: `dl prune <ws> --rm` used to remove `<ws>`, and
is now the `prune` retirement's refusal, since nothing overrides a line any more.
Add `--force` to that line and the `--force`-beside-`--rm` refusal is what you
get, because the pair is the more confused half and is named first.
`dl --prune` is unchanged.

- **`dl <ws> rm --rm` is refused as the two requests it is**, rather than being
quietly treated as one of them. The sentence names the verb, which is the
spelling that already does what such a line most likely meant.

## [0.8.0] - 2026-08-22

### Added
Expand Down
133 changes: 78 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,92 +134,78 @@ transport, which has no terminal; `dl <ws> restart` republishes the alias. Set
| `dl <user/repo> reset` | Clean slate (remove all, recreate) |
| `dl <user/repo> dotfiles` | Refresh dotfiles in the running workspace (`chezmoi update`) |
| `dl <user/repo> -- <command>` | Run shell command in workspace (with a terminal, when `dl` has one) |
| `dl <user/repo> --autorm` | Attach, and [delete the workspace when the session ends](#--autorm-the-throwaway-workspace) |
| `dl <user/repo> --rm` | Attach, and [delete the workspace when the session ends](#--rm-the-throwaway-workspace) |

Every verb in that table also takes the workspace second — `dl stop <user/repo>` — and with no
workspace at all it opens the selector and applies itself to what you pick — everything you pick,
for the verbs the selector lets TAB mark several of. `stop` and `rm` answer to
`--stop` and `--rm` as well, since the flag spellings were documented long before they worked.
for the verbs the selector lets TAB mark several of.

### `--stop` and `--rm` can be appended to a line that says something else
`rm` is a word and `--rm` is a flag, and they are two different requests — docker's
split, described next. `--stop` is not a spelling of anything: it was the flag form of
the `stop` verb and is [retired](#--stop-and---autorm-are-retired).

The flag spellings do one thing the words cannot: they may be typed at the *end* of a
line that already asked for something, and they win over it.
### `--rm`: the throwaway workspace

```bash
aid kinisi/repo@fix/x 'review this pr' # work happens
aid kinisi/repo@fix/x 'review this pr' --rm --force # ↑, with `--rm --force` appended
```

Both `dl` and `aid` accept it, and a leading verb word on the recalled line is not
mistaken for the workspace, so `dl prune <ws> --force` recalled with `--rm` appended
still removes `<ws>`. This exists because a shell makes appending to the previous line
cheap and rewriting the front of it expensive — deleting the workspace you were just
working in should not cost an edit in the middle of a long prompt.

What the suffix beat is named on stderr before anything is removed:

```
--rm overrode the rest of the line: 'review this pr' was not acted on.
```

That notice is the price of the convenience and is not optional: the line is now
allowed to carry an instruction it will not carry out, so a deliberate `--rm` and a
slip have to be told apart. Note that a `--` command tail cannot be overridden this
way — everything after `--` belongs to the workspace's command, so a `--rm` typed
there is an argument to that command and not a verb.

### `--autorm`: the throwaway workspace

`--autorm` deletes the workspace once the session ends, the way `docker run --rm` does.
It applies to the two forms that hand a session over and come back from it:
`--rm` deletes the workspace once the session ends, the way `docker run --rm` does. It
applies to the two forms that hand a session over and come back from it:

```bash
dl kinisi/repo@fix/x --autorm # shell; the workspace goes when you exit
dl kinisi/repo@fix/x --autorm -- make test # one command, then the workspace goes
aid kinisi/repo@fix/x 'fix the flaky test' --autorm
dl kinisi/repo@fix/x --rm # shell; the workspace goes when you exit
dl kinisi/repo@fix/x --rm -- make test # one command, then the workspace goes
aid kinisi/repo@fix/x 'fix the flaky test' --rm
```

**The word and the flag are docker's two commands, not two spellings of one.**
`docker rm` deletes a container now; `docker run --rm` deletes one when what it ran
has finished; and no docker subcommand takes a `--rm` meaning the first of those. Here
too: `dl <ws> rm` deletes now, `dl <ws> --rm` deletes after, and neither has to be read
twice to work out which was meant. `--force` follows docker as well — it is `dl <ws> rm
--force`'s, never `--rm`'s.

**It stops at work that is nowhere else.** The removal is `dl <ws> rm`'s, guard included,
so a clone holding uncommitted or unpushed work — or one git could not read to find out —
refuses, says which, and leaves the workspace standing:

```
--autorm: the session has ended, removing kinisi/repo@fix/x.
--rm: the session has ended, removing kinisi/repo@fix/x.
kinisi-repo-fix-x-1a2b holds 1 uncommitted change(s) (scratch.txt). Push or commit it,
or run: dl kinisi/repo@fix/x rm --force
```

That is what makes it safe to leave on a line you recall: the flag never decides that
your work was disposable. For the same reason `--force` does not compose with it — a
`--force` habitually appended to a recalled `--autorm` line would destroy work hours
`--force` habitually appended to a recalled `--rm` line would destroy work hours
later, unattended, with nobody reading the sentence explaining it. Run
`dl <ws> rm --force` when that is what you mean.

**A build that failed is collected too.** The removal runs whenever the launch got as
far as asking devpod for the workspace — including when `devpod up` died in
`postCreateCommand`, which leaves the container *running* and the clone cut. That is
the case an unattended `dl owner/repo --autorm -- make test` in CI most needs covered.
the case an unattended `dl owner/repo --rm -- make test` in CI most needs covered.
A launch that stopped earlier — an unknown workspace, a branch that could not be named,
a devpod that would not run — created nothing, so nothing is removed and nothing is
said about it.

Three more things it does not promise:

- **The exit code is the launch's.** `dl repo --autorm -- make test` exits with the
- **The exit code is the launch's.** `dl repo --rm -- make test` exits with the
test's status, and a failed build exits with devpod's; a removal that refused is
never what the code reports. The refusal is on stderr and the workspace is still
there.
- **It is best-effort, by construction** — but Ctrl-C out of a session is *not* one
of the gaps. See "How you exit decides whether it fires" below.
- **It does not know about your other shells.** Nothing serialises two sessions on
one workspace — the launch lock covers the build, not the session — so a second
`dl <ws>` in another terminal is attached to the same container, and the `--autorm`
run exiting first removes it from under that one. Use `--autorm` for the workspace
`dl <ws>` in another terminal is attached to the same container, and the `--rm`
run exiting first removes it from under that one. Use `--rm` for the workspace
you opened to throw away, not for one you may already be sitting in elsewhere.

On an `aid` line it is appendable like `--rm`, and unlike `--rm` it **keeps the prompt** —
the agent still runs, and the workspace goes when it is done.
On an `aid` line it is **appendable**, and it keeps the prompt: recall the line, type
`--rm` at the end, and the agent still runs — the workspace goes when it is done. That
is the shape a shell makes cheap, appending to the previous line rather than editing
the front of it. Note that a `--` command tail is not appendable this way: everything
after `--` belongs to the workspace's command, so a `--rm` typed there is an argument
to that command.

### How you exit decides whether it fires

Expand All @@ -230,7 +216,7 @@ the session or kills `dl`.
a pty — a bare `dl <ws>` runs `devpod ssh <id>`, and `dl <ws> -- <cmd>` on a terminal
runs `ssh -t` — which puts your local terminal in raw mode and clears `ISIG`. Ctrl-C is
then a byte travelling to the remote pty, not a signal to `dl`: the program *inside* the
container gets the interrupt. So `aid repo 'fix it' --autorm` and Ctrl-C twice to leave
container gets the interrupt. So `aid repo 'fix it' --rm` and Ctrl-C twice to leave
Claude Code ends the remote command, ends the session, and the workspace goes. In an
interactive shell Ctrl-C just hands you a fresh prompt — `exit` or Ctrl-D is what ends
that session, and either fires the removal.
Expand All @@ -241,17 +227,55 @@ removal (a signal handler may not allocate or lock, and this one `_exit`s):
- Ctrl-C during the clone or the container build, before any pty exists.
- Closing the terminal window — SIGHUP, which `dl` does not handle at all.

One-line check for your own setup: start `dl <ws> --autorm` and press Ctrl-C once. A
One-line check for your own setup: start `dl <ws> --rm` and press Ctrl-C once. A
fresh prompt *inside* the container means Ctrl-C is being forwarded and the removal will
fire when you leave. Landing back on the host means it reached `dl`, and it will not.

Those two forms and no others. Every verb word refuses the flag rather than ignoring it,
and `code` is the one worth knowing about: it returns while VS Code is still connecting,
so honouring `--autorm` there would delete the container out from under a window that is
so honouring `--rm` there would delete the container out from under a window that is
still opening. `restart`, `recreate` and `reset` do end in a session and would work, but
they are out too, because `--autorm` is the throwaway workspace and not a cleanup modifier
they are out too, because `--rm` is the throwaway workspace and not a cleanup modifier
on every verb that ends in a shell.

### `--stop` and `--autorm` are retired

Both moved because `--rm` changed meaning, and both are still recognised so that a
line recalled from history says what happened instead of quietly doing something else:

```
$ dl <ws> --autorm
--autorm is now spelled --rm: 'dl <workspace> --rm' opens the workspace and deletes it
when the session ends, the way 'docker run --rm' does. Use 'dl <workspace> rm' to
delete one now.

$ dl <ws> --stop
--stop is no longer a flag: the flag spellings now modify a session (--rm deletes the
workspace once one ends) rather than name a verb. Use 'dl <workspace> stop' to stop a
workspace.
```

`--autorm` is a rename and nothing else: the behaviour above is what it always did.

`--stop` is a genuine withdrawal, and so is the thing `--rm` used to do. Both were the
*suffix* form of a verb — appended to a line that already asked for something, and
winning over it, so that `aid <ws> 'review this pr' --rm` deleted the workspace and
printed `--rm overrode the rest of the line`. That shape cannot survive `--rm` meaning
"delete when the session ends": the two spellings look alike, and one cancelling the
line while the other runs it is the one pair a person cannot keep straight.

What replaces it, for "I am done with this workspace":

```bash
dl <ws> rm # the workspace named
dl rm # or pick it — TAB marks several, and rm takes each in turn
```

For a long `aid` prompt line that is the cheaper edit anyway: `dl rm` and a pick
beats recalling the line to type at the end of it. What is genuinely gone is deleting
a workspace *without naming or picking it*, by appending to whatever the last line
happened to be.

### `prune` is no longer a spelling of the `rm` verb

`dl <ws> prune` used to delete one workspace and `dl --prune` removes clone
Expand All @@ -267,10 +291,10 @@ $ dl <ws> prune
or 'dl --prune' to remove the clone directories no workspace opens any more.
```

`dl --prune` is unchanged. The word is still *recognised* rather than forgotten, so
it is never read as a workspace name: a `dl prune <ws> --force` line recalled with
`--rm` appended still removes `<ws>`, and a workspace that really is called `prune`
is still reachable as `dl stop prune`. Use `dl <ws> rm` from now on.
`dl --prune` is unchanged. The word is still *recognised* rather than forgotten, so it
is never read as a workspace name — `dl prune <ws>` says what moved instead of
reporting an unknown workspace called `prune` — and a workspace that really is called
`prune` is still reachable as `dl stop prune`. Use `dl <ws> rm` from now on.

## Global Commands

Expand Down Expand Up @@ -350,8 +374,7 @@ are unaffected, and `dl <ws> -- claude` still runs exactly what you typed.
|--------|-------------|
| `--claude`, `--codex`, `--gemini` | Pick the agent (default: `claude`) |
| `--devcontainer <variant\|path>` | Passed through to `dl` |
| `--rm`, `--stop` | Do that to the workspace instead of starting an agent; appendable to a recalled line, `--force` with `--rm` deletes despite unsaved work |
| `--autorm` | Run the agent, then delete the workspace when the session ends |
| `--rm` | Run the agent, then [delete the workspace when the session ends](#--rm-the-throwaway-workspace). Appendable to a recalled line, prompt and all. To delete one *now* instead, that is `dl <ws> rm` |
| `DEVLAUNCH_AID_AGENT=<agent>` | Change the default agent |
| `DEVLAUNCH_NO_TTY=1` | No prompt question, no pty: the old one-shot behaviour |
| `DEVLAUNCH_NO_HERDR=1` | Do not report agent state to a host-side [herdr](#agent-state-in-a-herdr-session) session |
Expand Down Expand Up @@ -1052,7 +1075,7 @@ Removing a workspace removes three things: the devpod workspace, the local clone
(unless it holds work that exists nowhere else — see [cleaning up
workspaces](#cleaning-up-workspaces)), and **the named Docker volumes that
workspace's devcontainer created**. Every path that removes a workspace does all three:
`dl <ws> rm`, the appended `--rm`, `--autorm`, and `--purge`.
`dl <ws> rm`, `dl <ws> --rm`, and `--purge`.

Two volumes per workspace, both named from what devpod recorded substituting into
the devcontainer:
Expand Down
Loading