aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-17 13:08:06 +0200
committeruser <user@clank>2026-08-17 13:08:06 +0200
commit244068e86c03b6c1bf457fac7e576bf5f68a43b7 (patch)
treec294bf9fdd75d6fb487cc3d820ec955d0f799146 /docs
parentsl0ppty: workspaces, and layouts that write themselves (diff)
downloadslosh-244068e86c03b6c1bf457fac7e576bf5f68a43b7.tar.gz
slosh-244068e86c03b6c1bf457fac7e576bf5f68a43b7.tar.xz
slosh-244068e86c03b6c1bf457fac7e576bf5f68a43b7.zip
sl0ppty: a skill for driving it, and the two variables that made one possible
`.agents/skills/driving-sl0ppty/SKILL.md`. The convention is `.agents/skills/<name>/SKILL.md` with YAML frontmatter -- already in this tree under `vendor/libghostty-vt`, so it is not an invention -- and it means an agent working in a checkout picks the skill up without being told, while a directory per skill leaves room for adjuncts later. `contrib/README.md` and `docs/scripting.md` say where it is and how to use it elsewhere: copy or symlink the directory into wherever your agent keeps skills. **Writing it turned up the reason there was no skill worth writing.** A pane is told `SL0PPTY=1` and nothing else, so a program inside one knows it is in a session and cannot find out *which* -- `ls` lists them all and none of them is labelled "the one you are in". So the whole most valuable case, an agent driving the session it is running in, was guesswork. The server now sets `SL0PPTY_SESSION` and `SL0PPTY_BIN` in its own environment, which every pane inherits through fork: one place, no signatures threaded, and unset under `--script`, which has no socket to name. `SL0PPTY_BIN` is read from `/proc/self/exe` rather than `argv[0]`, and exists because the failure it prevents is the one I hit writing the examples -- a session started from a build tree, and `sl0ppty` not on the pane's `PATH`, so the skill's own commands did not run. What the skill actually teaches, none of which `docs/scripting.md` says because a reference is not allowed to have opinions: - Work belongs in a pane that was **given** a command, not typed into somebody's shell. Then `alive` and `exit_code` off `panes` are how you wait, and the output survives the program (D14) so it is still there to read. - There is **no wait primitive over the socket**: `settle` is headless-only, and `deadline` is about repaints and says nothing about program output. Poll. - `purpose` is the handle. Titles change underneath you; a declared purpose cannot be relabelled by the program in the pane, which is what makes it safe to address. - `snapshot` is the whole composited session, and only what is on screen. Cut a pane out with its `content_*` rect; do not expect scrollback. - **The echoed command line matches your own marker.** `echo DONE` puts `DONE` on screen twice. Count, or use a marker you did not send. - Write `\e`, never `\033`: the unescaper takes `\0` first, so `\033` arrives as NUL followed by `33` and nothing complains. That bug was live in this repo's own test suite until last week. - A program in a pane can draw its own status and real buttons with OSC 5577 and read the click off its stdin, which is how it asks a question instead of printing a prompt and hoping. - Close what you made; do not `quit` a session you did not start. `tests/test_skill.py` is the part that keeps it true. A skill is documentation an agent acts on without a human reading it first, so a stale one is worse than a missing one: a wrong verb is a failed tool call somebody else has to debug. Every verb the file names is sent to a live session and must not answer `unknown cmd`; every `panes` field its central recipe polls must exist; the environment it promises is read out of a real pane; the recipe itself is run end to end and must report exit status 3 with its output still on screen. It also greps the file for paths out of a home directory, because "plug and play" is a property somebody has to keep. That test found `close-pane` in the draft, which is the *keybinding* action name -- the verb is `close`. Exactly the class of error the test exists for, found before anybody's agent ran it. Verified: 1795 checks, all green, 43 of them in `tests/test_skill.py`. Then the skill's recipe run verbatim against a real session -- tagged pane, polled to `alive:false`, `exit status: 3`, `compiling`/`built`/`exited: status 3` still readable -- and from inside a pane, `session=agentdemo bin=/…/sl0ppty` and a `building 3/7` status drawn in its own frame.
Diffstat (limited to 'docs')
-rw-r--r--docs/scripting.md18
1 files changed, 18 insertions, 0 deletions
diff --git a/docs/scripting.md b/docs/scripting.md
index 3dddc9b..89dd835 100644
--- a/docs/scripting.md
+++ b/docs/scripting.md
@@ -119,6 +119,24 @@ it is sent (`cat`, a shell with echo on, a REPL waiting for a line) would
otherwise be answered into a loop, which is exactly what `hello` used to do:
4 MB of hellos in a second and a half.
+## Driving it from an agent
+
+Everything above is what an agent needs, and none of it says which parts matter.
+`.agents/skills/driving-sl0ppty/SKILL.md` is the same socket written as
+instructions: how a program in a pane finds out which session it is in
+(`SL0PPTY_SESSION`, `SL0PPTY_BIN`), why work belongs in a pane that *was given a
+command* rather than typed into somebody's shell, that `alive` and `exit_code` are
+how you wait rather than reading the screen for a marker your own echo matches, and
+that `purpose` is the handle to find things by because titles change underneath
+you.
+
+It follows the `.agents/skills/<name>/SKILL.md` convention, so an agent working in
+a checkout picks it up without being told. To use it elsewhere, copy or symlink the
+directory into wherever your agent looks for skills. `tests/test_skill.py` checks
+every verb, variable and `panes` field it names against the program, because a
+stale skill is worse than a missing one: an agent acts on it without a human
+reading it first.
+
## Headless
`sl0ppty --script` is the whole program without a terminal: commands on stdin,