<feed xmlns='http://www.w3.org/2005/Atom'>
<title>sl0ppi.git/cli/test/cli.test.ts, branch main</title>
<subtitle>a sl0p.foo repository</subtitle>
<id>https://git.sl0p.foo/sl0ppi.git/atom/cli/test/cli.test.ts?h=main</id>
<link rel='self' href='https://git.sl0p.foo/sl0ppi.git/atom/cli/test/cli.test.ts?h=main'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppi.git/'/>
<updated>2026-08-12T20:24:55Z</updated>
<entry>
<title>cli: up, ls, open, close</title>
<updated>2026-08-12T20:24:55Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-12T20:24:55Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/sl0ppi.git/commit/?id=2cd247828d863cdc779c7178c988152835096ca2'/>
<id>urn:sha1:2cd247828d863cdc779c7178c988152835096ca2</id>
<content type='text'>
Slice 2 of docs/cli-design.md. The commands read the world (discovery +
list-tabs), compute a plan, then apply it -- never mutating while walking, which
is how `up` stays idempotent.

  sl0ppij.ts   process wrapper; every piece of state comes back out of
               list-tabs/list-panes, so there is nothing to desync
  layout.ts    composes the tab node around a project's fragment
  root.ts      root resolution, session naming, the root registry
  workspace.ts up / ls / open / close
  index.ts     dispatch + the fail-open wrapper

Four things this turned up that reading the code would not have:

  - `attach --create-background` fails with "There is no active session!" when
    the caller is itself inside a session -- which sl0ppi almost always is,
    being run from the user's daily-driver terminal. Session creation now runs
    with the SL0PPIJ*/ZELLIJ* markers stripped: we are creating a new detached
    session, not nesting.

  - falling open by passing our own argv to sl0ppij just produces "unrecognized
    subcommand: up" and still leaves the user without a terminal. Our verbs and
    flags are stripped first, so what is handed over is something sl0ppij
    actually understands.

  - attaching panics in the client when stdin is not a tty, so `up` from a
    script or CI step died. It now reports the session is ready and exits.

  - a start_suspended pane reports `is_held`, not `exited`. Conflating them made
    `ls` claim every agent was running when none had started. Plugin panes (the
    status bar, the link handler) also inflated pane counts and are now excluded
    as chrome.

Index conventions were verified against a live session rather than inferred,
because guessing closes the wrong tab: list-tabs `position` and
`close-tab --tab-id` are 0-based, `go-to-tab` is 1-based, and closing renumbers
every tab after it, so batched closes run in descending position order.

Verified end to end against a real session (three projects, one shipping a
custom fragment): up creates the session with all tabs and their purposes; up
again reports "up to date" with tabs unchanged; ls reports panes and
agent:suspended correctly; open creates-and-focuses and resolves unique
prefixes; close removes a tab; deleting a project auto-closes its tab; and with
the root itself missing, nothing is closed at all.

64 tests green.
</content>
</entry>
</feed>
