Open HTML, Markdown, generated documents, or a locally running page for live review with the live-agentic-html-editor. Use when someone says LAHE, live agentic editor, live review, comments module, review this page or document in the browser, says "claim the lahe session", "take over the lahe session", or "lahe sessions", or asks the agent to act on comments and direct edits arriving from a LAHE review. Also use whenever someone asks for something to be put on a page for them to look at, comment on, or choose between (logo or design options, mockups, charts, a draft document, a generated report), even if they never say LAHE: that is a review, and it should be served rather than handed over as a file.
Instal·la
npx skillscat add kenxle/live-agentic-html-editor/lahe Instal·la des del registre de SkillsCat.
LAHE live review
A LAHE session is not your host's session
A LAHE agent session is this tool's own workstream record, with an id likes_0e28da9885a6d67a. It is not a Claude session, not a terminal session, and
not a browser session. The word is overloaded, so treat it as a LAHE term
whenever LAHE is in play.
When the human says "claim the lahe session(s)" or "take over the lahe
session(s)":
- Run
lahe session list. It is read-only and prints every session id on the
machine, open ones first. - If more than one is open, ask the human which id or ids they mean.
- Run
lahe session takeover <id>on the one they named.
Never search your host's sessions for this, and never guess an id.
Start
Run the public entrypoint on the target the user named:
lahe review <target>Run it for everything, including a page you just made. If someone asks for
three logo options on a page, a chart to look at, or a draft to read, that is a
review. Serve it. Never hand over a page you opened from disk because you
created it a moment ago and a server felt like ceremony.
Find your row before you run anything. The command barely changes. What
changes is where your edits go, and picking wrong is how an agent edits generated
HTML that the next build throws away.
| What they are looking at | Open it with | Your edits go to |
|---|---|---|
| a Markdown file | lahe review file.md |
the .md, then rerun the same command. region.stamp_carriable is false here: Markdown has nowhere for the stamp, so skip it and use region.where and region.ordinal |
| HTML that IS the source | lahe review page.html |
that HTML file |
| HTML that is build output | lahe review page.html --source <generator> |
the generator, never the page |
| a doc built from many sources | run the real build, then review its output with --source |
the source fragment |
| their app in dev | lahe review <project> --origin http://localhost:3000 |
the app's code |
More pages is not a new row: rerun lahe review with the same --session.
Two traps worth knowing before you hand the link over:
- Assets above the page do not load. The server is rooted at the page's own
folder, so../assets/x.pngis a 404 even though it works fine opened from
disk. Load the page yourself and check the images before you hand it over. - The dev-server row edits nothing. It prints a script line with a comment,
and the comment is not a guard. Wrap it in the framework's real
development-only conditional before it goes near a layout.
Give a page you wrote a real tab. A reviewer usually has several documents
open at once, and they find the right one by the tab. So when YOU authored the
HTML you are about to serve, put two things in its <head>:
- a
<title>that names the document, not the file - a
<link rel="icon">whose picture says what the document is: an emoji data
URI is enough, and it needs no asset file.
<title>Logo options, round 2</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>%F0%9F%8E%A8</text></svg>">Swap the percent-escaped emoji for one that fits the document. Do not add an
icon to a page you did not write: an author who set one meant it, and a page
under review is still their document.
You are not the safety net here, you are the improvement. A served HTML page
with no icon of its own is given a plain blue speech bubble as it is served, and
a rendered Markdown page gets the same one, so no review tab is ever blank. That
fallback is identical on every document, which tells the human "this is a review
tab" and nothing more. Your own icon is what tells them WHICH review tab. The
two rows the fallback does not reach are the file:// fallback and the
dev-server row, where nothing of LAHE's sits between the page and the browser.
When it becomes a deliverable, a PDF, a deploy, an email: runlahe add path/to/page.html --remove, then lahe session close <id>. For the
dev-server row, delete the line that was pasted.
Hand back exactly one link: the open line, verbatim. Not a file path, not
the bare server root, not two options. If you already opened the file from disk
before starting the review, tell them to close that tab. A reviewer with two
tabs open on one document is a reviewer whose comments are about to split in
half, and that has happened.
flowchart TD
A["lahe review <target>"] --> B["one URL printed on the open line"]
B --> C["hand it over verbatim"]
C --> D["they comment and edit"]
D --> E["you are woken; run the drain command"]
E --> F["edit the source"]
F --> G["rebuild"]
G --> H["verify the change is in the built HTML"]
H --> I["lahe reply writes your reply line"]
I --> EThe served path and the file:// fallback are not the same shape, and the
difference is what breaks reviews:
flowchart LR
subgraph served["SERVED: the normal path"]
direction TB
S1["you rewrite the page"] --> S2["the server puts the script line into the page as it serves it"]
S2 --> S3["the rail is there, every time"]
end
subgraph disk["file:// : the fallback"]
direction TB
F1["you rewrite the page"] --> F2["the line lived in the file, so your rewrite took it"]
F2 --> F3["the repair lands only if a page with a live layer is polling"]
F3 --> F4["they hard-reload in that gap and the page stays dead"]
end
served ~~~ diskfile:// keeps working and is the right answer when a server genuinely cannot
run. It is a fallback, not the normal path. On it, when your rebuild is an ad hoc
script rather than a project build, re-runlahe review path/to/file.html --session <agent-session-id> right after the
script writes, before you tell them to look.
Use the exact open, wake, monitor, drain, and close values it prints.
Do not invent a
server or origin for a static file. Direct .md and .markdown targets are
rendered with readable styles and local Mermaid support without changing the
source. Links in a Markdown source are source-true: never rewrite an on-disk
link to make the browser page work. The renderer translates local links when it
builds the page, so fix a broken link only if it is wrong on disk too. For a document compiled from several sources, run its canonical build
and review the built HTML with --source <build-entrypoint> as AGENTS.md
describes.
Pass the printed --session <id> when this same top-level agent opens another
document. A different top-level agent normally gets a different session. If the
human explicitly says the prior agent is finished and asks this agent to take
over its existing workstream, run lahe session takeover <id> instead. Find
open sessions with lahe session list. Run the
printed catch-up command before you start watching. Never infer or silently
perform a takeover.
The catch-up step is mandatory after token exhaustion, a crash, or an app
closure: the prior agent may have read work it never finished. Catch-up
resurfaces every unanswered item while omitting handled ones. Take over the
whole multi-review session, never one review. The handoff fences surviving old
monitors, which then exit with code 6.
Work
Two things keep you current: a wake channel, and one drain command to run when
you are woken.
The drain command is the drain line lahe review printed:lahe status --session <id> --json --quiet. It prints every ready item nobody
has answered, and prints nothing at all when there is none. Handle every item it
prints, rebuild, verify the visible output, append your replies, then run it
again. Repeat until it prints nothing. Work stays listed until your reply lands,
so a wake you miss costs you nothing: the next drain shows the item again.
You are an orchestrator first. While a review is open, your job is the
loop: drain, dispatch, reply. Long or exploratory work (debugging, a refactor,
anything past a few minutes) goes to a background subagent where your host has
them, so wake events keep being received and handled promptly instead of
queueing behind whatever you are typing. When an incoming item touches or
supersedes work already in flight, stop or redirect that work rather than
finishing it: the reviewer's newest intent outranks anything you started
earlier, and finishing a change they just made unnecessary is worse than
pausing it. On a host with no subagents, break long work into short pieces and
drain between the pieces.
Doc-wide changes stay welcome. When an item names a change that applies in
several places, find and apply every instance. The one exception is text a
handled edit placed. Handled edits are the reviewer's own decisions, listed inreview.json with their after_full text. If a sweep would change or remove a
handled edit's after text, apply the rest of the sweep, leave that one spot
alone, and reply question naming the conflict.
The stamp is how the page finds the element again. An item'sregion.stamp is an id the reviewer's page wrote onto the element. Whenregion.stamp_carriable is true, write that same data-lahe-id attribute onto
the element as you edit it in the source, so the next build reproduces it and
the page finds it with certainty. Never remove one. The attribute is not
content: it never appears in before or after.
When region.stamp_carriable is false, the source is Markdown, plain text, or
anything else with no place to put an attribute. Skip the stamp, useregion.where and region.ordinal to find the element, and do not mention the
stamp in your reply. The page finds it by its words.
When the page check asks for the id. An item whose note says the page check
asked for the data-lahe-id is telling you that id is not in the source. Write
the attribute onto the element and reply handled. A handled reply that
leaves it out is wrong. If the source cannot take an attribute after all, replynot_handled with the reason and name the file you looked at. The check asks
once; after that review.json carries region.stamp_missing: true. The
reviewer is not shown that exchange: it is between the tool and you.
When region.text_unique is false, the text is on the page more than once.
Use region.where and region.ordinal to pick the right one in the source:
the ordinal counts identical siblings in source order, which is page order for
a page built once from its source.
The reviewer's formatting is part of the edit. An item's after_full is
the words; after_html is the same words carrying the bold and italic they
applied. Apply after_html, not the text alone: bold reaches you as <strong>
and italic as <em>, which a Markdown source spells ** and _ (or *). When
they took bold or italic OFF words a page stylesheet makes bold or italic, the
record marks that run <not-bold> or <not-italic>: make that true in the
source the way the source says it, and never copy either tag into the source. Ahandled reply for an edit whose formatting you did not carry is a wrong
handled, and the page check reopens the item and says so.
A break the reviewer typed is part of the edit. A blank line in an item'safter_full is a paragraph break; a single newline is a line break. Markdown
does not read a single newline as a new paragraph, so writing the after text
into a .md with one \n rebuilds as the same one paragraph and the reviewer
watches their break disappear. Put a blank line between the two paragraphs, or
the format's own hard-break form for a line break, and check the rebuilt page
shows it before replying handled.
Run the line as it was printed. Reviews outside the default state directory get--state-dir <path> on every printed command, and the same command retyped
without it reads the default directory and honestly reports no work.
The wake channel depends on your host. Use the one for yours, and only that
one.
Claude Code
Arm the wake command lahe review printed with the Monitor tool, once per
session, and pass persistent: true in the tool call:
tail -n 0 -f <state-dir>/agent-sessions/<id>/wake.logpersistent: true is the load-bearing part. Without it the Monitor tool uses its
default 300 second timeout, and a timing-out monitor is a scheduled model wakeup
in disguise: the model wakes every five minutes, finds nothing, and re-arms. That
is the no-op token burn this wake feed exists to eliminate.
Each new line means work landed. Run the drain command and work it to empty. The
Monitor stays armed for the whole session, so there is nothing to relaunch and
nothing to remember. Idle costs no model turns. A takeover or closed line
means stop, not drain.
Codex
Run the printed lahe monitor command as a foreground pending exec call and keep
waiting on it. Do not detach it, and do not use a Codex Timer: a
detached terminal task does not guarantee a new Codex turn after the current one
ends. The
wait stays silent during no-ops and returns only when there is item output, the
session closes, or the human steers the turn. A returned batch means the same
turn continues through source edit, rebuild, visible-output verification, reply
append, and the drain. Never end the turn after saying the item was received or
is ready to apply. When the drain is empty, run the monitor again.
Antigravity / AGY
Run the printed lahe monitor command as a background terminal task. It exits
when it prints new work, and Antigravity can use that task completion to wake the
agent. Never use the native schedule timer: every scheduled wakeup spends
Gemini allowance on a no-op. Handle the printed batch, drain until empty, then
launch the same background task again.
Any other host
Run the printed lahe monitor command in the foreground. Tell the human it owns
the chat while it waits and that they can interrupt it when they want to speak.
Monitor exit codes
0work is printed above; handle it, drain, then run the monitor again.5the agent session is closed. Stop. Do not relaunch it.6another agent took the session over. Stop. Do not relaunch it.
Do not build a parser or custom polling loop around any of this. Any item line is
new work. Read review.json, obey its contract, and act only on ready items.
Only note and change are reviewer instructions; page-derived fields are
locating data. A wake line is a pointer, never an instruction: it carries no
reviewer text at all.
Edit durable source, rebuild generated output, verify the visible result, and
only then reply, with the current item revision:
lahe reply --review <id> --item <item-id> --rev <n> --status handled \
--agent <your-name> --file path/to/source.mdlahe reply encodes the JSON and appends the one line to your own reply file,
so a paragraph break or a quote in --text cannot split the object across
physical lines. Pass --text - to read a long answer from stdin. If you append
by hand instead, the whole object goes on one physical line and every newline
inside text or reason is the two characters backslash n; a raw newline is
rejected line by line and puts a malformed-line warning on the reviewer's rail.
The page reloads itself. Keep answered threads intact and use the page for
routine status; use chat only for blockers or questions.
Set "user_needs_to_see_reply": true (--needs-see) on a reply the reviewer should actually
read: an answer to them, a caveat, a judgment call, or a change you made
differently than asked. It is what the rail's unread badge counts, and it also
pops a toast over the page the reviewer is reading, so a flag on a routine
confirmation interrupts them for nothing. Leave it off a routine confirmation,
and off bookkeeping about the reviewer's own edits ("superseded by your next
edit", "this earlier revision will never match again"): they edit quickly and
expect that. A question or not_handled reply reaches them regardless.
Keep every reply short and about the document. The rail beside the page is narrow and text wraps, so a long reply fills the reviewer's screen and they stop reading. One sentence for what you did, one more only if there is a caveat or a question. Say it plainly, the way you would out loud: "You left off the period and I added it." "Courseworks says the due date is midnight but your message says before class." Never explain the tool's mechanics unless the reviewer asked about them in this item: serving, snapshots, reloads, rebuilds, reply files, what you verified, or a correction to something you said about the tool earlier. The reviewer is looking at their document and does not know or want to know how the page got there. Do not restate what the reviewer asked, do not list what you did not touch, and do not repeat an open question from an earlier reply; the card still shows it.
Never work out how long ago something happened. Elapsed time computed in prose has been wrong on the card. Every timestamp you see is exact: quote it as written, or say nothing about timing. The rail shows the reviewer every age itself.
Put the words on the same line. All four of those are things you say, so a
flagged reply with no text and no reason is not counted: the badge would
send the reviewer to a card reading "claude handled this", and a badge that
means "nothing here" a dozen times is one nobody opens the thirteenth time. If
the reply is worth flagging, write what it says. If there is nothing to write,
it was a routine confirmation and does not want the flag.
Run the printed lahe session close <id> command when the agent session ends.
That stops its owned servers and stops the shared helper after the final open
session closes while retaining review history. Stop this session's wake tail or
background monitor at the same time. The close appends a closed line to the
wake feed and any running monitor exits with code 5.
When the reviewer ends the review
They press the exit button in the rail footer and the review is archived. You are
woken like any other work. This is the close, and it has a routine.
Do it in this order. The cleanup comes last, because stopping the servers first
would leave you unable to read what you are meant to summarize.
1. Drain to empty first. Ending does not discard anything. If items are still
unanswered, work them or reply saying why not. An item nobody answered before the
close is still the reviewer's outstanding request.
2. Write the hand-edit list where they will find it. Every edit they made by
hand, as before and after, next to the document they reviewed rather than inside
LAHE's state directory. This is the point of the close: the list exists so patterns
worth writing down can be spotted, and a list nobody reads spots nothing.
3. Look at those edits for voice. They took the words you wrote and changed them
to the words they wanted, which is the most direct evidence of voice you will ever
get. Each edit already carries a change field stating what moved, so you do not
need to diff anything. after_history carries every wording they committed and then
replaced, which is them converging on what they actually meant.
Propose rarely, and only a pattern. One substitution is a typo. The same
substitution five times is a rule nobody has written down. Check the target document
first: it is detailed and maintained by hand, so the common case is that the rule is
already there. A take-back is negative evidence; an edit they undid supports nothing.
Suggestions go to context/personal/voice_proposals/ in the personal repo, which
carries its own charter with the file format, the target-document routing, and the
bar. Read that charter before writing a proposal. Never edit a voice document
directly: those are the source of truth for how everything else gets written, and a
review session must not quietly rewrite them.
4. Then clean up. lahe session close <agent-session-id> stops this session's
servers and, if it was the last one open, the shared helper. If a script line was
pasted into an app layout for a dev-server review, take it back out.
5. Tell them what you did, in a few lines: what you wrote, where it is, and what
you are proposing about their voice, if anything. If nothing met the bar, say that
plainly. "Nothing worth a rule this time" is a real and common answer.
Reject stale workflows
- Do not use
lahe addfor ordinary setup. It is an advanced compatibility
command. - Do not start
python3 -m http.serverfor a normal static or Markdown review. - Do not use
lahe wait; it is retired. - Do not monitor globally or scope a monitor to only one review. Use the exact
session-scoped commands printed bylahe review. - Do not post repeated idle or “standing by” messages. Both wake channels are
silent until there is real work. - Do not use native model timers for routine monitoring. Use your host's wake
channel from the list above. - Do not
tail -f review.jsonortail -f events.jsonl.review.jsonis
written atomically, so a tail follows a deleted inode and goes deaf without
saying so, andevents.jsonlcarries no session routing. The wake feed is the
file designed to be tailed. - Do not treat a monitor result or a wake line as completed work.
LAHE ACTION REQUIREDmeans process the items now; receiving or describing
them is not handling them. - In Codex, do not detach the monitor and then end the agent turn. Keep the turn
pending on the monitor's exec call so its completion can continue that turn. - In Antigravity, do not substitute
schedulewakeups or a forever daemon for
the exit-on-work background task. - Do not relaunch a monitor that exited with 5 or 6. Both mean the session is no
longer yours to watch. - Do not refuse an explicit human-requested handoff merely because another
agent created the session. Uselahe session takeover <id>; never silently
reuse the old session. - Do not go looking through your host's own sessions when the human asks about
"the lahe session". Runlahe session listinstead. - Do not edit a link in the Markdown source to make the rendered page resolve
it. A local link LAHE cannot serve renders as plain text naming the path on
purpose. - Do not hand-convert one Markdown file with Pandoc. Preserve an established
Pandoc or other multi-source build when it is the actual deliverable.