summaryrefslogtreecommitdiff
path: root/.agents
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-30 23:04:26 -0300
committerGabriel Schneider <[email protected]>2026-10-01 00:12:17 -0300
commit074f113fa95a875a6bf17196f8e46e69806a247c (patch)
tree6ad2c8c3894c6f52a759dabd8850f1a7b6574331 /.agents
parentfb3f5ab5a7758a628a6e343f24a6510a58802599 (diff)
downloadpardes-074f113fa95a875a6bf17196f8e46e69806a247c.tar.gz
pardes-074f113fa95a875a6bf17196f8e46e69806a247c.zip
Scripting over 9P has one chapter, and the served README and the 9P skill shrink to a summary, the traps and a pointer to it
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to '.agents')
-rw-r--r--.agents/skills/pardes-9p/SKILL.md185
1 files changed, 23 insertions, 162 deletions
diff --git a/.agents/skills/pardes-9p/SKILL.md b/.agents/skills/pardes-9p/SKILL.md
index 83675b74..51e4f2f7 100644
--- a/.agents/skills/pardes-9p/SKILL.md
+++ b/.agents/skills/pardes-9p/SKILL.md
@@ -6,8 +6,12 @@ description: Inspect and drive a running Pardes editor through its 9P control fi
# Pardes over 9P
Pardes is files: `cat`, `echo >`, `ls` and shell scripts are the whole
-interface. [docs/fs.md](../../../docs/fs.md) is the reference for every
-file; this page is recipes. Paths are relative to the repository root.
+interface. The recipes are in the Scripting chapter
+([docs/typ/scripting.typ](../../../docs/typ/scripting.typ)), every file's
+semantics in the Reference chapter
+([docs/typ/reference.typ](../../../docs/typ/reference.typ)), and a running
+session serves a summary at `$m/README`. Paths are relative to the
+repository root.
## Find the session
@@ -21,167 +25,24 @@ cat "$m/index"
the session socket of the pane you run in, `$PARDES_PANE` that pane's
serial. Under `9ns --unix SOCK -- cmd` the session is the mount itself:
`m=$NINE_MOUNT`. In a `Tty9p` shell: `m=$PARDES_MOUNT`. A dead session's
-entry answers `Input/output error`: name the session, never glob. A running
-session serves the binary that started it.
-
-## Look around
-
-```sh
-cat "$m/index" # serial kind dirty name column, a pane a line
-cat "$m/layout" # columns, then `active <serial>`
-cat "$m/focus" # the pane with the keyboard
-cat "$m/README" # the one-screen guide
-cat "$m/commands" # every builtin and where it goes
-```
-
-Parse an index row with `rsplit(maxsplit=1)` first: names may hold blanks.
-Re-read the index after anything that opens or closes panes.
-
-## Open a file, go to a place
-
-```sh
-echo "$PWD/main.zig:120" > "$m/look"; cat "$m/look" # serial it landed in
-echo "main.zig:0/fn main/" > "$m/look" # first match; without 0, the next after dot
-```
-
-A miss opens nothing, reads back empty and logs an `err <serial> look: ...`
-line; the write itself succeeds. `L:C` columns are bytes.
-
-## Make, fill, name and save a pane
-
-```sh
-n=$(cat "$m/pane/new") # each open makes one: read it once
-printf 'hello\n' > "$m/pane/$n/body" # > replaces the text
-printf 'more\n' >> "$m/pane/$n/body" # >> appends
-echo "$PWD/notes.txt" > "$m/pane/$n/name"
-echo Save > "$m/pane/$n/ctl"
-rmdir "$m/pane/$n" # close it, saved or not
-```
-
-## Edit text
-
-```sh
-p=$m/pane/$n
-echo 'Edit ,x/foo/c/bar/' > $p/ctl # sam edit, one undo step
-echo '/old/' > $p/addr; printf 'new' > $p/data # replace the next match
-echo 0 > $p/addr # back to the top
-echo '3' > $p/addr; : > $p/data # delete line 3
-cp $p/addr $p/dot; cat $p/sel # select it, read the selection
-```
-
-`addr` searches on from the last address (it is the pane's, shared by all
-clients); after a `data` write it sits just past the text, so write `addr`
-before each replacement. A failed `addr` leaves none and `data` refuses:
-check the write's status. An Edit block of several lines goes in one open:
-`printf 'Edit ,x/foo/{\ni/</\na/>/\n}\n' > $p/ctl`.
-
-## Run a command
-
-```sh
-echo 'make test' > "$m/exec"; c=$(cat "$m/exec") # a command pane; its serial
-grep "^exit $c " "$m/log" | tail -1 # `exit <serial> <N>` once done
-```
-
-At a terminal's prompt, run and get the status and output in one go:
-
-```sh
-t=$(awk '$2=="term"{print $1; exit}' "$m/index")
-exec 3<>"$m/pane/$t/pty/run"; echo 'ls' >&3; cat <&3; exec 3<&- # `exit N`, then output
-printf 'q' > "$m/pane/$t/pty/data" # raw keystrokes (\r Enter, \x03 Ctrl-C)
-echo 'sig INT' > "$m/pane/$t/pty/ctl" # interrupt
-```
-
-`busy: <program> is running` means the prompt is not free: a program holds
-the terminal, so talk to it through `pty/data`.
-
-Ask the language server at a file pane's cursor (`addr` then `dot=addr`):
-
-```sh
-echo '/myFunc/' > "$m/pane/$n/addr"; echo 'dot=addr' > "$m/pane/$n/ctl"
-echo Hover > "$m/pane/$n/exec" # +Hover; Rename new, Diagnostics, Symbols
-echo Lspwhy > "$m/pane/$n/exec" # +Lsp: why the last query found what it did
-echo Lspinfo > "$m/pane/$n/exec" # which server, and its state
-```
-
-## Follow what happens
-
-```sh
-exec 3<>"$m/log"; echo 'follow new' >&3
-timeout 30 cat <&3 # one record a line, as they come
-exec 3<&-
-```
-
-`follow` (without `new`) replays the ring first. `tail -f` sees nothing new.
-Through FUSE `read -t` never times out: use `timeout`. After a `Restore`
-every connection is cut: dial again and restart the mount.
+entry answers `Input/output error`: name the session, never glob. Without a
+mount, `test/ninep.py`'s `Client` speaks 9P itself (Scripting, "No mount").
## Traps
- A refused write says only `Invalid argument` or `Input/output error`; the
- reason is the `err` line in `$m/log` (`tail -1 "$m/log"`).
-- Only writes log: a refused open, truncation (`> data` after a failed
- `addr`) or `rmdir` has its errno alone.
-- Settings and session words go to `$m/ctl`; pane words (`Undo`, `Save`,
- `Del`) to `$m/pane/<n>/ctl`. The wrong one is refused naming the right one.
-- `Exit`, `Restore`, `Del`, `get` refuse once over unsaved text; the same word
- again discards.
-- A word no builtin knows is a shell command (`exit 127` if a typo).
-- `lock` needs a held fd: `exec 3>$p/ctl; echo lock >&3; ...; exec 3>&-`.
-- plan9port `9p write` truncates: it replaces a whole `body`.
-- Through a mount, bash's `printf 'a\nb\n' > ctl` arrives one write per
- line, so a block that should be refused whole runs its good lines before
- the bad one fails. Send a multi-line block as one write: `cat block >
- "$p/ctl"`.
-
-## No mount: the Python client
-
-Use [test/ninep.py](../../../test/ninep.py) when nothing is mounted, or when
-a fid must stay open (`event`, `pty/data`, a followed `log`). Its paths are
-the served root (`/index`, `/pane/2/body`).
-
-```sh
-PYTHONPATH=test python3 -B - "$PARDES_9P" <<'PY'
-import sys
-from ninep import Client
-with Client(sys.argv[1]) as c:
- print(c.read('/index').decode(), end='')
- n = int(c.read('/pane/new'))
- c.write(f'/pane/{n}/body', b'hello\n', truncate=True)
- fid = c.open(f'/pane/{n}/event', 0) # hold event: clicks come here
- c.write(f'/pane/{n}/exec', b'Msg hi\n')
- print(c.read_fid(fid, 0, 4096)) # b'FX0 0 1 6 Msg hi\n'
- c.close(fid)
- c.remove(f'/pane/{n}')
-PY
-```
-
-`Client` takes a Unix socket path or `(ip, port)`; `client.screen()` returns
-the parsed `/screen`. For `event` records and writing them back, see
-[fs.md#event](../../../docs/fs.md#event).
-
-## An isolated session
-
-Never experiment on a session someone is using. Build a binary with
-`zig build install -Dplatform=tty` (a bare `zig build`, or `--prefix
-zig-out`, installs over `~/.local/bin`). `test/fs.py` starts a private
-session and cleans it up:
-
-```sh
-PYTHONPATH=test python3 -B - "$(realpath zig-out/bin/pardes)" <<'PY'
-import sys, tempfile
-from pathlib import Path
-from fs import session, new_pane, execute
-with tempfile.TemporaryDirectory(prefix='pardes-9p-') as d:
- with session(sys.argv[1], Path(d), 'probe') as (client, address):
- n = new_pane(client, b'fn main() void {}\n')
- execute(client, n, 'Msg ready')
- assert 'ready' in ''.join(c[0] for c in client.screen()['cells'])
-PY
-```
-
-By hand: `pardes --detach=NAME &` with its own `HOME` and `XDG_*`
-directories, then `9ns --mntgen` (the session is `$NINE_MOUNT/pardes/NAME`)
-or `9ns --unix $XDG_RUNTIME_DIR/pardes-9p-NAME.sock -- sh`. Strip every
-`PARDES_*` variable first, or a file argument goes to the session you are
-inside. `session(..., tty=True)` and
-[test/agent_session.py](../../../test/agent_session.py) drive terminals.
+ reason is the last `err` record in `$m/log`.
+- `addr` is the pane's and moves on: each `/re/` searches from the last
+ address, and a `data` write leaves it just past the text. Write `addr`
+ before each replacement; a failed address leaves none.
+- An Edit `x` that matches nothing succeeds: check the text afterwards.
+- Each open of `pane/new` makes a pane: read it once.
+- Through a mount, `printf 'a\nb\n' > ctl` arrives one write per line: send
+ a block as one write (`cat block > $p/ctl`), ending in a newline.
+- Pane words (`Save`, `Undo`, `Del`) go to `pane/<n>/ctl`, settings and
+ session words to `$m/ctl`.
+- `Exit`, `Restore`, `Del` and `get` refuse once over unsaved text.
+- Never experiment on a session someone is using: strip every `PARDES_*`
+ variable and start your own (Scripting, "An isolated session"); build
+ with `-Dplatform=tty`, since a bare `zig build` installs over
+ `~/.local/bin`.