{
  "markdown": "\n<pre>          \n                                                                       #####   #####              \n                                             ...                       #####   #####              \n                                             ...                               #####              \n                                         ...........     #########     #####   #####   ####  #### \n                                         ...........   ############    #####   #####   #### ##### \n                                           .....      #####    #####   #####   #####   ########## \n   ................   ...............      .....               #####   #####   #####   #####      \n   ...............   ................      .....       +############   #####   #####   #####      \n                                           .....      #####    .####   #####   #####   #####      \n                                           ......  . #####    .#####   #####   #####   #####      \n                                            ........  ######### ####   #####   #####   #####      \n                                             .......   ######   ####   #####   #####   #####      \n</pre>\n\n<h1 align=\"center\">Tailr</h1>\n\n### \n\n<p align=\"center\">\n  Mark up a running dev server and hand the changes to your coding agent as one batch.\n</p>\n\n<p align=\"center\">\n  <a href=\"https://trytailr.app\"><b>Website →</b></a>\n  &nbsp;·&nbsp;\n  <a href=\"https://github.com/gcrft123/tailr/releases\">Releases</a>\n  &nbsp;·&nbsp;\n  <a href=\"https://www.npmjs.com/package/@gcrft123/tailr\">npm</a>\n</p>\n\n<div align=\"center\">\n\n[![npm](https://img.shields.io/npm/v/%40gcrft123%2Ftailr)](https://www.npmjs.com/package/@gcrft123/tailr)\n[![CI](https://img.shields.io/github/actions/workflow/status/gcrft123/tailr/ci.yml?branch=main&label=CI)](https://github.com/gcrft123/tailr/actions/workflows/ci.yml)\n[![node](https://img.shields.io/node/v/%40gcrft123%2Ftailr)](https://github.com/gcrft123/tailr#readme)\n[![dependencies](https://img.shields.io/badge/dependencies-0-blue)](https://github.com/gcrft123/tailr/blob/main/package.json)\n[![license](https://img.shields.io/npm/l/%40gcrft123%2Ftailr)](LICENSE)\n\n</div>\n\n<h1> </h1>\n\n### \n\n### \n\n>[!TIP]\n>### Quick start\n>\n>Paste this to your coding agent:\n>\n>```\n>Set up Tailr so I can mark up my running dev server visually and hand you the changes as one batch. Fetch:\n>https://raw.githubusercontent.com/gcrft123/tailr/main/PROMPT.md\n>and follow it exactly, without summarizing it.\n>```\n\n<h1> </h1>\n\n### \n\n<!-- A user-attachments URL, on its own line and free of Markdown, because that is\n     the only way GitHub plays a video: its media-src policy allows only its own\n     upload hosts, so a file in this repository is blocked whether it is reached\n     through raw.githubusercontent.com or the github.com/.../raw/... redirect.\n     Nothing is wrong with the file — it plays in a <video> on any other site,\n     which is what site/ does with it. To replace this, drag the new mp4 into a\n     GitHub issue comment and paste the URL that comment gives you. -->\nhttps://github.com/user-attachments/assets/bb164693-9f44-4e16-95c4-7357c798ab38\n\n---\n\n## Get started\n\nPaste this to your coding agent:\n\n```\nSet up Tailr so I can mark up my running dev server visually and hand you the\nchanges as one batch. Fetch\nhttps://raw.githubusercontent.com/gcrft123/tailr/main/PROMPT.md\nand follow it exactly, without summarizing it.\n```\n\n\nOr run it yourself, if you'd rather your agent didn't fetch anything, then ask\nit to start a session:\n\n```bash\nnpx -y @gcrft123/tailr init\n```\n\nEither way, `init` edits four things in your project and nothing else:\n\n- adds `@gcrft123/tailr` to your devDependencies\n- registers its MCP server in `.mcp.json`, and in `.cursor/mcp.json` too if the\n  project already uses Cursor\n- writes the agent's operating rules into your `AGENTS.md` / `CLAUDE.md`,\n  between markers of its own\n- adds `.tailr/` to your `.gitignore`, if the project is a git repository\n\nRe-running it is safe: it rewrites its own block and leaves everything around it\nalone. `--no-mcp` and `--no-install` opt out of either half; `--file <path>` puts\nthe rules somewhere else.\n\nThe agent then starts a session against your dev server, hands you a review URL,\nand watches for your first batch. See [PROMPT.md](PROMPT.md) for what it follows.\n\n## Try it first\n\n```bash\nnpx -y @gcrft123/tailr demo\n```\n\nThat starts a small sample application, proxies it, and prints a review URL —\nnothing installed into a project, no agent involved. Hold Alt, mark a few things,\npress **Send**, then run `npx -y @gcrft123/tailr pull` from the same directory in\nanother terminal to print the batch an agent would receive.\n\n## As a plugin\n\nThe marketplace edits nothing in your project, and Tailr is fetched with `npx`\nwhen a session starts, so there is nothing to install first. You get the MCP\nserver, the review loop's operating rules as a skill, and a command that opens a\nsession and hands you the review URL.\n\n**Claude Code**\n\n```\n/plugin marketplace add https://github.com/gcrft123/tailr.git\n/plugin install tailr@tailr\n```\n\nUse the git URL, not the `owner/repo` shorthand: the Claude Code app clones the\nshorthand over SSH and has nothing to answer the host-key prompt with, so the add\nhangs and then fails. Updating is `/plugin` → **Update**, which moves you to the\nlast release: the listing is read from the default branch, but the plugin it\ninstalls is pinned to the release tag.\n\n**Cursor**\n\nImport `https://github.com/gcrft123/tailr` as a Team Marketplace (Dashboard →\nSettings → Plugins). To load it on this machine only, copy `plugin/` to\n`~/.cursor/plugins/local/tailr` and reload the window.\n\n**Codex**\n\n```\ncodex plugin marketplace add https://github.com/gcrft123/tailr.git\ncodex plugin add tailr@tailr\n```\n\n**GitHub Copilot CLI**\n\n```\ncopilot plugin marketplace add gcrft123/tailr\ncopilot plugin install tailr@tailr\n```\n\n**Antigravity CLI**\n\n```\nagy plugin install https://github.com/gcrft123/tailr\n```\n\nThat reads the same extension manifest Gemini CLI does, and brings the skills and\nthe MCP server with it. Gemini CLI still takes `gemini extensions install\nhttps://github.com/gcrft123/tailr`.\n\n**Everywhere else** — Windsurf, OpenCode, Cline, Amp, and the rest of the agents\nthat read a global `skills/` directory:\n\n```\nnpx skills add gcrft123/tailr -g\n```\n\nThat puts `tailr-start`, `tailr-review` and `tailr-config` in the shared\n`~/.agents/skills` directory those agents read, symlinked into the folders of the\nones that keep their own. It does not register the MCP server; for that, use a\nmarketplace or extension command above, or `tailr init`.\n\nNot every route namespaces a skill, so what the commands are called depends on\nthe way in:\n\n| Installed with | Commands |\n|---|---|\n| Claude Code, Codex or Copilot marketplace | `/tailr:start` `/tailr:config` |\n| Cursor | `/start` `/config` |\n| Antigravity, Gemini, `npx skills add` | `/tailr-start` `/tailr-config` |\n\nThis is an alternative to `tailr init`, not an addition. The plugin suits someone\nreviewing across several projects; `init` suits a project that wants Tailr in its\nown setup, and is the only one of the two that writes to your repository. Running\nboth is harmless — the rules are the same text either way.\n\n## Start a session\n\n```bash\nnpx tailr start --target http://localhost:3000\n```\n\n`start` detaches inside Tailr and returns once the review URL is ready — that is\nwhat agents should use. Bare `npx tailr --target …` still serves in the\nforeground if you want the process attached to your terminal.\n\nThat assumes Tailr is in the project, which `init` sees to; `npm install\n--save-dev @gcrft123/tailr` is the same thing by hand, and `npx\n@gcrft123/tailr start --target …` skips it altogether.\n\nTailr proxies your dev server on `http://localhost:4100` and injects its overlay\ninto the HTML. Your application is not modified — no script tag, no build step, no\nconfig. Hot-reload WebSockets pass through untouched.\n\n```bash\nnpx tailr                         # proxies http://localhost:3000 (foreground)\nnpx tailr --target <url>          # a different dev server (foreground)\nnpx tailr start --target <url>    # same, detached — returns when ready\nnpx tailr stop                    # stop the project's session\nnpx tailr --port <n>              # serve Tailr somewhere else\nnpx tailr -- npm run dev          # start the dev server too, then proxy it\nnpx tailr --notify <command>      # run this when Send is pressed, to wake the agent\nnpx tailr --no-notify             # don't, even if there is an agent to wake\n```\n\nReview at the Tailr URL, not the original one. A session writes nothing to your\nrepository except a locator under `.tailr/`, so the CLI can find it.\n\n## Marking up\n\nHold **Alt** to arm — **⌥ Option** on a Mac, or whichever key you have set under\n[Settings](#settings). While it is held:\n\n| Gesture | Result |\n|---|---|\n| Left-click | Comment on an element, and ask for versions or a slider if you want to compare |\n| Right-click | Stage an element for removal (right-click again to undo) |\n| Double-click text | Edit text in place |\n| Shift-click | Mark a spot rather than an element — to ask for something new there, or to note the place. Middle-click does the same, if you have one |\n\nRelease the key and you can control the application again. Double-tap it to latch\nmarkup on for keyboard use. Marks persist in the browser across reloads, span\nroutes, and survive the reload after the agent has worked.\n\nThe island in the corner shows what is staged; hover it for the list. Drag it to\nany corner if it's covering page content.\n\n## Versions and sliders\n\nA comment on an element, or on a spot, can ask for more than one answer. The\ncomposer carries a **1×** button next to Add; click it for 2×, 3×, 4×, and the\nagent builds that many versions of the change instead of one. The button beside\nit asks for a **slider** instead — one number you scrub on the page, for anything\ncontinuous like a glow, a depth, a scale. A mark can ask for both.\n\nAfter the reload, a small pill sits on the element. For versions it carries a tab\neach: hover one and the pill widens to the name the agent gave it while the page\nswitches to it live, so you compare the real thing rather than two descriptions of\nit. A slider's pill carries the control — drag it and the page follows.\n\nClick a tab to keep that version, or **Keep** to hold a slider where you left it.\nKeeping is itself a mark: it goes into your batch, and the next Send makes it\npermanent and clears the rest out of the source. The × on its row in the island\nkeeps none of it.\n\n## Ending a session\n\n**End session** is at the bottom of the island's panel. It asks first, and the\ncard says what you are agreeing to: marks you never sent are discarded, and Tailr\nstops proxying, so it names the address your app goes back to (or tells you the\ndev server is stopping too, if Tailr started it).\n\nConfirming runs a cleanup pass first. Anything you never decided — versions you\ndid not choose between, a slider you did not keep a value on — goes to the agent\nas one last batch that takes it, and the switches guarding it, out of your source.\nThat scaffolding is Tailr's, and it shouldn't outlive the session that asked for\nit. Then the server stops, the overlay clears what it kept in your browser, and it\ntakes itself off the page.\n\nIf the agent isn't listening, **End anyway** leaves without waiting; Tailr says so\non the way out rather than pretending the cleanup happened.\n\n## Settings\n\nTwo things about Tailr are yours to set rather than the project's:\n\n| Setting | Values | Default | What it does |\n|---|---|---|---|\n| `sfx` | `true` / `false` | `true` | A short sound on each action — a mark made or dropped, a batch sent, a version picked, a run closing |\n| `modifier` | `alt` `ctrl` `cmd` | `alt` | The key you hold to arm marking |\n\nAsk your agent with the config command your install gave you — see the table\n[above](#as-a-plugin) — or set them yourself:\n\n```bash\nnpx -y @gcrft123/tailr config sfx:false modifier:cmd\n```\n\nEither way they are written to `~/.tailr/config.json` and hold across every\nproject. With no arguments the command prints where they stand. A change made\nwhile a session is up lands on the open review page without a reload.\n\n## Waking the agent\n\n`tailr wait` is the handoff on any agent whose client can tell the model that a\nbackground process exited. Not every one can. On Codex a backgrounded `wait`\nexits into nothing, and the MCP `tailr_wait` gives up after a minute and ends the\nturn — so the session goes idle and the reviewer is back to saying \"I've sent you\na batch\", which is the thing Tailr exists to stop.\n\nSo on those the direction inverts, and Tailr pokes the agent instead:\n\n```bash\nnpx tailr --notify 'codex queue --thread %t --message \"%n Tailr marks are waiting\"'\n```\n\n`%n` is the number of marks, `%t` the agent's thread, `%u` the review URL, `%%` a\nliteral `%`. The command runs once per batch, and a Send never fails because it\ndid.\n\nIf the agent started the session itself, none of that is needed. Codex exports\n`CODEX_THREAD_ID` into every command it runs, and that is the same id `codex\nqueue --thread` takes — so Tailr finds it and says so on the way up:\n\n```\n  Send will wake codex thread 01a07c3e… on its own — nothing needs to watch for it.\n```\n\nThe reviewer presses Send, the idle Codex session wakes with the batch, and the\nloop runs. Nobody types anything. `tailr status` reports `wakesAgent` when this\nis on, which is how an agent knows not to bother with `wait`.\n\nStarted the session in your own terminal rather than through the agent? Then\nthere is no thread to find at startup — but the first Tailr command the agent\nruns registers it, and Send wakes the agent from then on.\n\n### Clearing the conversation\n\nA thread id is only good until you clear the conversation. Codex starts a new\nthread for a cleared session, does not record it anywhere until something is\nsent to it, and still accepts messages queued to the old one — so a wake aimed\nat the id Tailr captured would report success and arrive nowhere.\n\nNothing can look that up, so the agent corrects it instead: every Tailr command\ncarries the thread it is running on, and Tailr re-aims at it. Clear the\nconversation and the very next thing you ask the agent repairs the handoff,\nwhatever you ask for — the rules have it run `status` when a session is already\nup, and that alone is enough:\n\n```\n  ⌁ waking codex thread 01a07c57… from now on\n```\n\nBetween the clear and that first command there is a gap where Send reaches\nnobody. Tailr says so rather than pretending, if a batch goes unclaimed:\n\n```\n  ⌁ r1 not picked up. If the agent's conversation was cleared it is on a new\n    thread now — ask it for anything and it will re-register itself.\n```\n\nThe session itself is never the problem: it is a separate process, holding its\nstate in `.tailr/session.json`, and a cleared conversation does not touch it.\nOnly the address of who to wake goes stale.\n\n## The agent side\n\nRun these from the same project directory, while a session is up.\n\n```bash\ntailr start --target <url>  # start the session (detached); prefer this to shell &\ntailr stop                  # stop it\ntailr status          # is a batch waiting? exit 0 if yes, 3 if not\ntailr wait            # block until one is; exit 0 waiting, 3 timed out, 2 session ended\ntailr pull            # lease the pending batch, printed as JSON on stdout\ntailr pull --wait     # lease it, blocking until one arrives\ntailr variants <ref> \"First name\" \"Second name\"\n                      # name the versions you built for a mark that asked for several\ntailr slider <ref> --min 0 --max 100 --value 40 --label \"Glow\" --unit \"%\"\n                      # report the parameter you wired for a mark that asked for a slider\ntailr progress <ref>  # one mark applied — the reviewer sees it land, live\ntailr done            # the run finished\ntailr fail \"reason\"   # it returned incomplete\n```\n\n`tailr pull` prints:\n\n```json\n{\n  \"id\": \"r1\",\n  \"sentAt\": \"2026-08-30T01:00:06.545Z\",\n  \"marks\": [\n    {\n      \"ref\": \"01\",\n      \"type\": \"comment\",\n      \"route\": \"/invoices\",\n      \"address\": \"InvoiceTable.tsx:20\",\n      \"selector\": \"body > div > section:nth-of-type(2) > div:nth-of-type(3)\",\n      \"element\": \"Bellweather Ltd\",\n      \"comment\": \"Overdue pills should link to the invoice\",\n      \"orphaned\": false\n    }\n  ]\n}\n```\n\n`type` is one of `comment`, `remove`, `text`, `point`, `choice`. A `text` mark\ncarries `before` and `after`. A `point` mark carries page coordinates `x`/`y`\ninstead of an element, and its comment says whether the reviewer wants something\nnew there or is noting the spot. `orphaned: true` means the element was gone when\nthe batch was sent — the address is the last one known, and the mark is worth\nraising with the reviewer rather than guessing at.\n\n**Versions.** A mark carrying `\"variations\": 3` asks for three answers to the same\ncomment, built together so the reviewer can compare them on the running page.\nGuard each one on the attribute Tailr sets on `<html>` for that mark —\n`[data-tailr-var-03=\"2\"] .card { … }`, with version 1 also being what renders if\nthe attribute is absent — then name them in order with `tailr variants 03 \"Softer\nedges\" \"Full width\" \"Two columns\"`.\n\n**Sliders.** A mark carrying `\"slider\": true` asks for one continuous parameter\ninstead. Build it behind `data-tailr-slide-03` on `<html>`, with the default being\nwhat renders if the attribute is absent, then report the range: `tailr slider 03\n--min 0 --max 100 --value 40 --label \"Glow\" --unit \"%\"`. A mark can ask for\nversions and a slider together; do both.\n\n**Closing either.** What comes back later is a `choice` mark. For versions it\ncarries `variantOf` and `variant`: keep that version as plain code and take the\nothers and the guards out with it, or at `variant: 0` keep none of them. For a\nslider it carries `sliderOf` and `value`: bake that number in and remove the\nswitch, or at `value: null` put the element back as it was.\n\n**Where `address` comes from.** Nothing standard tells a page which file an\nelement came from, so Tailr reads whatever your dev tooling already emits:\n`data-v-inspector` (vite-plugin-vue-inspector), `data-inspector-relative-path`\n(react-dev-inspector), `data-astro-source-file`, Svelte's `__svelte_meta`, a\ngeneric `data-source`, and React 18's development fibers. Emit\n`data-tailr-source=\"Component.tsx:20\"` yourself and that wins. With none of them\npresent `address` is `null` and the mark still carries its selector, its text, and\nits route — which is the fallback, not a failure.\n\n**Don't wait to be told.** `tailr wait` hangs on the session's event stream and\nreturns within a moment of Send being pressed — no polling, and no asking the\nreviewer to announce every batch. Run it as a background process and treat its\nexit as the notification:\n\n```bash\ntailr wait && tailr pull\n```\n\nIt returns immediately if a batch is already waiting, so none can be missed.\n`--timeout <seconds>` bounds the wait; without it, it waits as long as the session\nlives.\n\n**Report progress as you go.** Each `tailr progress <ref>` empties that mark on\nthe reviewer's screen while they watch — the difference between a tool that looks\nstuck and one that looks like it is working.\n\n**Always close the run.** Until `tailr done` or `tailr fail` arrives, the reviewer\ncannot send another batch — and if you never answer, they can take that batch back\nand send it again. If you cannot finish, `tailr fail` with what happened; Tailr\ndoes not guess at causes, it points the reviewer back to you.\n\nThe whole contract, including the events to listen for when a version or a slider\nhas to re-render rather than restyle, is in the rules `tailr init` writes into\nyour agent instruction file.\n\n## As an MCP server\n\n`tailr init` registers this for you, and the [plugin](#as-a-plugin) brings it\nalong without touching your project at all. By hand, most clients take:\n\n```json\n{\n  \"mcpServers\": {\n    \"tailr\": { \"command\": \"npx\", \"args\": [\"-y\", \"@gcrft123/tailr\", \"mcp\"] }\n  }\n}\n```\n\nPrefer it to the CLI where you can: tool descriptions stay in the agent's context\nevery turn, so the protocol cannot quietly fall out the way a pasted prompt does.\n\nSame round trip as the CLI:\n\n| Tool | What it does |\n|---|---|\n| `tailr_start` | Start a session (detached); returns the review URL when ready |\n| `tailr_stop` | Stop the project's session |\n| `tailr_status` | Is a session running, is a batch waiting, and where should the reviewer go |\n| `tailr_wait` | Block until the reviewer sends a batch, so they never have to tell you |\n| `tailr_pull` | Lease the pending batch. `wait: true` blocks until one arrives |\n| `tailr_variants` | Name the versions you built for a mark that asked for several |\n| `tailr_slider` | Report the range of the parameter you wired for a slider mark |\n| `tailr_progress` | Report a `ref`, or several `refs`, as applied |\n| `tailr_done` | Close the run; the reviewer is prompted to reload |\n| `tailr_fail` | Close it as incomplete with a `reason`, releasing the send lock |\n| `tailr_config` | Read or change the reviewer's settings, when they ask |\n\nThe MCP tools talk to the same session the CLI does. If none is running,\n`tailr_start` (or `npx tailr start --target <url>`) creates one — do not\nshell-background a bare `tailr`, and do not edit anything under `.tailr/`.\n\n## Requirements\n\nNode 18 or newer, and nothing else — Tailr has no dependencies.\n\nOf your dev server it asks almost nothing. Tailr injects into HTML responses and\npasses everything else through, so there is no framework list here: if it serves\nHTML over http or https, it works. A self-signed certificate is fine, and so is a\nserver that compresses — Tailr asks for uncompressed HTML and decodes gzip,\ndeflate or brotli when one arrives anyway. The hot-reload WebSocket is relayed\nuntouched.\n\nSource addresses are the one part that depends on your setup — see [The agent\nside](#the-agent-side) for what Tailr reads, and what a mark still carries when a\nproject emits none of it.\n\n## License\n\nMIT.\n\nThe interaction sounds are [Cuelume](https://github.com/Danilaa1/cuelume) — copied\ninto `src/overlay/cuelume.js` rather than depended on, under its MIT licence,\nwhich travels with it at the top of that file. It synthesizes every sound through\nthe Web Audio API, so there are no audio files here either.\n",
  "bytes": 22414,
  "sha": "9cf3857a64cb57f884d3a813b9b046a05eda18c971d42969ec1fd60a56f2d9c6",
  "repo_slug": "gcrft123/tailr",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_gcrft123_tailr_56abee29/readme"
}