Add explore skill: interactive pseudocode browser

Serves a localhost page in the pseudo-skill register with the
reference markers as clickable buttons: clicks land in
<workdir>/events.jsonl, a persistent Monitor tail wakes the session,
the session rewrites page.html, and the browser live-reloads over SSE.
Actions per marker: expand / show real code / explain / ask, plus a
page-level ask box; anchors become forge deep-links when the project
has a reachable remote; incidental findings are surfaced on the page
as anchored notes.

The port is configured in one place (the PORT constant atop
scripts/server.py; --port overrides per run). Mechanics proven live
against the aura repo before extraction into this skill.
This commit is contained in:
2026-07-20 12:11:57 +02:00
parent fe39658d67
commit c68b2303a0
3 changed files with 453 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
---
name: explore
description: Use when the user wants to explore a project interactively in the browser — invoked as `/explore <project>` or when the user asks for a clickable/served pseudocode page. Serves a localhost page showing the project as commented pseudocode in the pseudo-skill register, with the reference markers rendered as clickable buttons; every click (expand / show real code / explain / ask, plus a free-text box) lands as an event that wakes the session through a Monitor back-channel, the session rewrites the page, and the browser live-reloads via SSE. Anchors become forge deep-links when the project has a web-reachable remote; incidental findings (doc/code drift, stale comments) are surfaced on the page itself as anchored finding notes.
---
# explore — interactive pseudocode browser
## Overview
`/explore <project>` turns the `pseudo` skill's register into a served,
clickable page. The user reads pseudocode in the browser and zooms by
clicking instead of typing: each marker is a button, each click wakes
the session, the session rewrites the page, SSE reloads the browser.
The loop:
```
browser click ──POST /event──▶ server.py ──append──▶ <workdir>/events.jsonl
Monitor "tail -F -n 0 events.jsonl"
this session
browser ◀──SSE "reload"── server.py ◀──mtime── rewrites <workdir>/page.html
```
Latency expectations are conversational, not app-like: every click costs
one thinking cycle. Tell the user this once at start.
## Moving parts
- `scripts/server.py` — stdlib localhost server. **The port is
configured in exactly one place: the `PORT` constant at the top of
this file.** A per-run override exists (`--port N`) for a second
concurrent instance; the constant is the default every instance
shares.
- `templates/page.html` — the page scaffold (CSS, click menu, ask box,
SSE client). Placeholders: `{{TITLE}}` (project name, appears in
tab and header), `{{HINT}}` (one line: what the project is + current
altitude), `{{CONTENT}}` (the pseudocode block, see content rules).
- Workdir `~/.cache/explore/<project-slug>/` — holds the generated
`page.html` and `events.jsonl`. Scratch, never committed, safe to
delete after the session.
## Start procedure
1. `mkdir -p ~/.cache/explore/<project-slug>`
2. Generate `<workdir>/page.html` from `templates/page.html`, filling
the three placeholders. `{{CONTENT}}` starts at the project's main
entry point (pseudo rule 9) unless the user pointed elsewhere.
3. Start the server in the background:
`python3 <skill-dir>/scripts/server.py <workdir>` (add `--port N`
only for a second concurrent instance).
4. Arm the back-channel as a persistent Monitor:
`tail -F -n 0 <workdir>/events.jsonl` (`-n 0` is load-bearing:
without it, stale events replay at arm time).
5. Give the user the URL the server printed.
## Content rules
The `pseudo` skill's Iron Law governs everything inside `{{CONTENT}}`:
anchor line first, one altitude per view, markers as pointing handles
explained inline (never a legend), data shapes beside control flow,
reduce/omit the rest. Read that skill's rules before generating the
first page. On top of it, the served medium adds:
- **Escape first, tag second.** `{{CONTENT}}` and every `.note` body
are substituted into HTML: escape `&`, `<`, `>` in all source and
pseudocode text before insertion (Rust is full of `Vec<T>`, `&mut`,
`a < b`), and only then wrap tokens in highlight spans. A stray `<`
swallows the rest of the page, including the SSE script.
- **Markers are buttons.** `[N]` renders as
`<button class="marker" data-marker="N">[N]</button>`. Expansions
introduce sub-markers (`4a`, `4b`) — the zoom is recursive.
- **Anchors are links.** When the project has a web-reachable forge
remote (Gitea/GitHub), every file anchor becomes an
`<a class="src" href="…#L10-L20" target="_blank" rel="noopener">`
deep-link. Verify the URL pattern once per project (curl the file
URL, expect 200) before emitting the first link; skip links when
there is no reachable remote.
- **Real code** (`code` action): a `.note.code` block at the marker —
caption line = linked `file:fromto` anchor, then the code verbatim
with light span highlighting (`.c` comments, `.k` keywords, `.s`
strings).
- **Explanations** (`explain` action): a `.note` block anchored at the
marker. Page-level `ask` answers go in a `<section id="qa">` between
the pseudocode and the menu div (question dimmed in `.q`, answer in
`.note`).
- **Findings.** Anything noticed in passing — doc/code drift, a stale
comment, a suspicious contract — is surfaced on the page itself: an
amber `.note.finding` (⚠) anchored at the spot with deep-links to
the evidence, and a wavy `.warn` span (with `title` tooltip) on the
stale phrase where it is displayed. A one-line chat mention suffices
beside it; filing a tracker issue stays a separate ask.
- **Language.** Scaffold and pseudocode are English (repo register).
Answers to `ask` events are written in the user's language.
## Handling events
Each Monitor line is one click: `{marker, action, text?, ts}`
(`marker: null` = page-level; `text` only on `ask`). Respond by
rewriting `<workdir>/page.html` — the SSE watcher reloads the browser
automatically, so the rewrite IS the reply. Every rewrite MUST
preserve the full template scaffold: the SSE `<script>` (EventSource +
reload listener), the `#menu` div, and the `#ask` form — after each
reload that script is what re-binds every marker button. Edit only the
pseudocode, the anchored notes, and the `#qa` section; dropping the
scaffold silently kills the loop. Read the real source
before expanding or explaining; keep each response at the altitude the
click asked for. Batch related edits into as few writes as possible
(each write triggers a visible reload).
## Stop
`TaskStop` the server task and the Monitor. The workdir is scratch and
may be deleted. To switch projects: stop the server, start it again on
the new project's workdir, and re-arm the Monitor on the NEW workdir's
`events.jsonl` — server and monitor are both bound to one workdir at
startup, so neither survives a workdir switch.