HTML Clay
  1. HTML Clay
  2. Guide
  3. Let a program edit your file

Let a program edit your file

Build a page that asks a program in your terminal to change the file it is running from, and watch the change arrive without a reload.

A page open in HTML Clay can ask a program on your machine to change the file it is running from, and watch the change arrive. You point at a paragraph and say “make this shorter”, and an AI agent, a script, or a formatter running in your own terminal does it.

Shell
htmlclay wire serve ~/notes/page.htmlclay -- ./my-agent.sh

No HTML travels between the two: the page sends a small request, your program edits the file, and the edit reaches the page as an ordinary file change. The file is the only thing they share, so nothing needs an account, a service, or a protocol you have to trust.

See docs/wire.md for the page API, the handler contract, the CLI, and the security model.

Before you start#

  • HTML Clay 1.6 or newer, and a file that opens and saves. If you have not made one yet, get started covers it in five minutes.
  • A terminal window you are willing to leave open.
  • For the second half, a command line AI tool. The example uses Claude Code. Anything that takes a prompt and can edit a file works the same way, and the page says exactly what you see if you do not have one.

The commands below are written for a Mac and use nothing beyond what macOS already ships with. On Linux everything is identical except one line you get to skip, noted where it appears. Windows is covered at the bottom.

1 A page with an ask box#

Save this as robot-notes.htmlclay, the same way you saved your first file: any plain text editor, plain text mode, keep the .htmlclay ending. Put it on your Desktop.

HTML
<!DOCTYPE html>
<html lang="en" autosave>
<head>
  <meta charset="utf-8">
  <title>Robot notes</title>
  <style>
    body {
      font-family: system-ui, sans-serif;
      max-width: 30rem;
      margin: 3rem auto;
      padding: 0 1rem;
    }
    form { display: flex; gap: 0.5rem; }
    input { flex: 1; font: inherit; padding: 0.4rem 0.6rem; }
    button { font: inherit; padding: 0.4rem 0.9rem; }
  </style>
</head>
<body>
  <h1 editable>Robot notes</h1>

  <form id="ask">
    <input id="said" placeholder="Ask for a change" autocomplete="off">
    <button>Send</button>
  </form>

  <ul id="log">
    <li>Lines the robot adds will show up here.</li>
  </ul>

  <div id="notes" editable>
    <p>This part is yours. Click and type.</p>
  </div>

  <script src="https://clayjs.com/v1/clay.js?plugins=sync,wire"></script>
  <script>
    clay.ready.then(function () {
      var ask = document.getElementById("ask");
      var box = document.getElementById("said");

      // Built here, never typed into the file: everything you can see
      // in the page is what gets saved.
      var status = document.createElement("p");
      status.setAttribute("clay", "no-save no-snapshot no-watch");
      ask.after(status);

      clay.wire.on(function (request) {
        status.textContent = request.error
          || request.text
          || request.state;
      });

      ask.addEventListener("submit", function (event) {
        event.preventDefault();
        var words = box.value.trim();
        if (!words) return;
        box.value = "";
        status.textContent = "sent";
        clay.wire.send({ said: words }, { text: words });
      });
    });
  </script>
</body>
</html>

Three lines do the work.

  • The script tag enables two extras. wire gives the page a way to ask for something. sync makes the answer appear in the open tab without a reload.
  • clay.wire.send sends the request. You hand it what the person typed. Everything else on the page is an ordinary form.
  • The status line is built in JavaScript, not written into the file. Everything you can see in the page is what gets saved, so a line reading “thinking” would be written into your document and still be there tomorrow. The clay attribute makes the line visible without saving it. Build parts like that in code. If you type one into the file by hand, the first save deletes it.

Double-click the file so it is open in a tab, then press Send. A moment later the status reports that nothing is attached instead of hanging.

2 The robot#

A robot here is any program. HTML Clay runs it once each time the page asks for something, and three plain rules connect the two.

  1. The request arrives on the program’s standard input, and the file’s location arrives in an environment variable called HTMLCLAY_WIRE_FILE.
  2. Every line the program prints shows up on the page as progress.
  3. When the program exits cleanly, the page is told the work is done. Any other exit code and the page shows an error.

Here is a whole working robot. Save it as robot.sh, next to the page file.

Shell
#!/bin/bash
echo "working on it"

NOW=$(date "+%H:%M:%S")
LINE="<li>The robot was here at $NOW.</li>"
perl -pi -e "s|</ul>|$LINE\n  </ul>|" "$HTMLCLAY_WIRE_FILE"

echo "added a line at $NOW"

It prints a progress line, adds one line to the list in the file stamped with the current time, and prints that it finished. It ignores what you typed. This first script lets you watch the connection work. The next script reads what you typed.

3 Connect them#

Open Terminal and paste these three lines.

Shell
alias htmlclay="/Applications/HTMLClay.app/Contents/MacOS/htmlclay"
cd ~/Desktop
htmlclay wire serve robot-notes.htmlclay -- bash robot.sh

The first line teaches this terminal window the htmlclay command, because on a Mac the command lives inside the app rather than on your path. It lasts until you close the window, and putting the same line in your ~/.zshrc keeps it. On Linux, skip it: htmlclay is already a command. On Windows it is htmlclay.exe, in the folder you unzipped.

The last line sends requests from this file to this program. The terminal answers with one line and then waits.

plaintext
[wire] attached as handler to http://htmlclay1.localhost:1212 (/Users/you/Desktop/robot-notes.htmlclay)

Your hostname and your username will differ. Leave it running. The robot is attached to your file while this command runs.

If it says no HTML Clay site is serving this file; open it first, the file has not been opened since HTML Clay last started. Double-click it and run the command again.

4 Press Send#

Go back to the tab, type anything into the box, and press Send. Watch the page. Within a second or two, in this order:

  1. The status under the box reads working on it. That is your robot’s first line, arriving on the page as it runs.
  2. A new line appears in the list: The robot was here at 14:03:52. The robot edited the file on your disk, and the page picked the change up and showed it to you, with no reload.
  3. The status settles on done.

Press Send a few more times. Each press adds one line.

The page saved itself before asking, so the robot read what you were looking at. The robot edited robot-notes.htmlclay itself, so its lines stay after you close and reopen the tab. HTML Clay put a complete copy of the file from before the edit in Backups, as it does before any other save, so you can go back.

Now make it think#

The setup stays the same, with a different program.

This program reads what you typed. Open the demo file banana-bread.htmlclay to try it on a real document: the page is a recipe for banana bread, serves 4 — 3 ripe bananas, 1 stick butter, melted, ¾ cup sugar, 2 eggs, 1½ cups flour, 1 tsp baking soda, baked at 350°F for 55 minutes — with an ask box that saves your request into the file. The terminal answers the same way:

plaintext
[wire] attached as handler to http://htmlclay1.localhost:1212 (/Users/you/Desktop/banana-bread.htmlclay)

The oven temperature should not double, and the program left it alone. This demonstration is shortened for the page. An AI request takes fifteen seconds to a minute.

For this step, you need an AI you can run from the terminal. The example uses Claude Code. If typing claude --version prints a version number, you are ready. If it prints command not found, you can stop here. The robot above uses the same connection without an AI.

Save this as agent.sh, next to the others.

Shell
#!/bin/bash
REQUEST=$(cat)

claude --permission-mode acceptEdits -p "The file at
$HTMLCLAY_WIRE_FILE is an HTML page I have open on my screen.
This request came from inside the page: $REQUEST. Make the change
it asks for by editing that file directly. Leave everything else in
the file exactly as it is, and do not create any other files." 1>&2 &

agent=$!
while kill -0 "$agent" 2>/dev/null; do
  echo "thinking"
  sleep 5
done
wait "$agent"

Stop the robot with Ctrl+C, because one file has one program attached at a time, and start this one instead.

Shell
htmlclay wire serve robot-notes.htmlclay -- bash agent.sh

Try one of these requests. “Add everything I need for a beach trip to the list.” “Rewrite my notes so they are half as long.” “Turn my notes into a haiku.”

The status says thinking while the agent works. When it finishes, the page shows new list items or rewrites your notes in place. A small ask usually takes somewhere between fifteen seconds and a minute.

Two lines in that script need explaining.

  • The heartbeat. HTML Clay stops waiting after two minutes without output, and every line resets that clock. Printing “thinking” every five seconds lets a slow agent take as long as it needs.
  • 1>&2 sends the agent’s own chatter to your terminal rather than the page. The page only needs to know that something is happening.

Where your file goes. An AI agent reads your file and sends what it reads to its own provider, under your own account with them. HTML Clay does not send the file anywhere. It starts the program you named.

The agent also runs with your permissions, in the folder you started it from. acceptEdits is what lets it write without stopping to ask about every change. If you would rather approve each one, use your agent’s stricter setting instead.

What just happened#

The document stays in the file on disk.

The page does not connect directly to your program, and it sends no page content to it. It sends a short message through HTML Clay. Your program edits the file with whatever tools it already has, and HTML Clay updates the page as it does after any file change. HTML Clay calls this channel the wire. Four rules apply.

  • The program runs only while your command runs. Ctrl+C detaches it.
  • A page cannot pretend to be your program. Browsers announce themselves in ways they cannot suppress, so a web page can ask, but only a program you started yourself can answer.
  • A page can only reach its own file. It cannot send requests about some other file on your disk, even one open in the next tab.
  • Every edit is versioned. A program’s change is backed up exactly like yours, even when no tab is open at the time.

The rough edges#

Known limitations.

Open the file before you attach#

The command finds files through the app, so attaching to a file that is not open answers no HTML Clay site is serving this file; open it first. Double-click the file, then run the command.

One program per file#

A second attach to the same file is refused while the first is running. Stop the old one with Ctrl+C first.

A program gets two minutes without output#

Each line resets the timer, which is why both example scripts print while they work. A large request can time out if the agent prints nothing for two minutes. Small requests are fine.

Your next save after a robot edit warns you#

The first time you edit the page yourself after a program has changed the file, the page reports that the file changed outside this tab. Nothing is lost, both versions are in Backups, and your save carries the program’s edit forward.

Give the parts a program rewrites an id#

Changes arrive in the open page by matching elements up, so a program should edit inside elements with a stable id or data-id, like log and notes in the example file. A change inside an anonymous element may not appear until the next reload.

Windows#

The app and the wire both work, but the example robots are bash scripts. Write the same three rules in PowerShell or any language you like: read standard input, print progress, edit the file named by HTMLCLAY_WIRE_FILE, exit 0. The command is the same, with the path to your htmlclay.exe in place of the alias.

Where next#

Try it on a file of your own.

The app is free to download. Download HTML Clay.