@levistudio/redline 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/LICENSE +21 -0
- package/README.md +132 -0
- package/ROADMAP.md +39 -0
- package/SECURITY.md +33 -0
- package/bin/redline.cjs +61 -0
- package/package.json +61 -0
- package/scripts/install-skill.sh +78 -0
- package/skills/redline-review/SKILL.md +102 -0
- package/src/agent.ts +283 -0
- package/src/cli.ts +332 -0
- package/src/client/cards.ts +385 -0
- package/src/client/diff.ts +100 -0
- package/src/client/firstRunBanner.ts +26 -0
- package/src/client/lib.ts +299 -0
- package/src/client/main.ts +119 -0
- package/src/client/render.ts +413 -0
- package/src/client/selection.ts +253 -0
- package/src/client/sse.ts +179 -0
- package/src/client/state.ts +56 -0
- package/src/client/styles.css +994 -0
- package/src/contextBlock.ts +16 -0
- package/src/diff.ts +166 -0
- package/src/parseReply.ts +115 -0
- package/src/pickModel.ts +38 -0
- package/src/promptEnvelope.ts +58 -0
- package/src/render.ts +83 -0
- package/src/resolve.ts +290 -0
- package/src/server-page.ts +119 -0
- package/src/server.ts +634 -0
- package/src/sidecar.ts +190 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to Redline are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Node-compatible launcher (`bin/redline.cjs`) so `npx @levistudio/redline <file>` works alongside `bunx`.
|
|
9
|
+
- `ROADMAP.md` and this changelog.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- Package renamed to scoped `@levistudio/redline` for npm publishing.
|
|
13
|
+
- README rewritten around the AI-doc-review use case.
|
|
14
|
+
|
|
15
|
+
## [0.1.0] - 2026-05-09
|
|
16
|
+
|
|
17
|
+
Initial public release.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- Local review reader (Bun + Hono server, browser UI) for Markdown files.
|
|
21
|
+
- Inline select-to-comment with quote + 32-char-context anchoring.
|
|
22
|
+
- Sidecar JSON at `.review/<filename>.json` as the source of truth for a review.
|
|
23
|
+
- Per-round history snapshots at `.review/history/<file>.<iso>.md`.
|
|
24
|
+
- Real-time conversation with a Claude agent over SSE — the agent replies within seconds of a comment landing.
|
|
25
|
+
- Verdict-aware resolve: every agent reply ships with `requires_revision` so the round-level button auto-defaults to **Revise document** or **Accept as-is**.
|
|
26
|
+
- One-shot revision command: `redline resolve <file> [--model <id>]`.
|
|
27
|
+
- `redline-review` skill for outer-agent handoff (Claude Code etc.).
|
|
28
|
+
- CSRF token on every mutating `/api/*` request.
|
|
29
|
+
- Cross-process file lock around sidecar transactions.
|
|
30
|
+
- `realpath` check on the static-asset route to block symlink escapes.
|
|
31
|
+
- Per-prompt UUID envelopes around user-controlled prompt fields.
|
|
32
|
+
- Auto-restart of the agent subprocess (capped to 5 restarts / 60s).
|
|
33
|
+
- Auto-installs missing dependencies on first CLI run.
|
|
34
|
+
- Initial test suite: server, sidecar, parsing, model-picking, rendering, diff, SSE, integration, happy-dom client.
|
|
35
|
+
|
|
36
|
+
[Unreleased]: https://github.com/alevi/redline/compare/v0.1.0...HEAD
|
|
37
|
+
[0.1.0]: https://github.com/alevi/redline/releases/tag/v0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alon Levi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Redline
|
|
2
|
+
|
|
3
|
+
**Inline comments on Markdown files, designed for human-in-the-loop AI doc review.**
|
|
4
|
+
|
|
5
|
+
Open a Markdown file, highlight text, leave inline comments, discuss changes with Claude, and apply accepted revisions back to the document.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Try it in 30 seconds
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
bunx @levistudio/redline ./spec.md
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
(or `npx @levistudio/redline ./spec.md` — Bun is required either way.)
|
|
16
|
+
|
|
17
|
+
The terminal prints a `localhost` URL. Cmd-click it. Select any text in the document, type a comment, hit reply. Claude responds in the thread within ~2s. Resolve each comment, then click **Revise document** to have Claude apply the agreed edits back to the file on disk.
|
|
18
|
+
|
|
19
|
+
Requires [Bun](https://bun.sh) ≥ 1.0 and an authenticated [Claude Code](https://claude.com/claude-code) session. The agent inherits your existing Claude Code OAuth — no `ANTHROPIC_API_KEY` needed.
|
|
20
|
+
|
|
21
|
+
## Why Redline exists
|
|
22
|
+
|
|
23
|
+
A lot of recent docs start as a Claude draft. Reviewing those drafts in a chat window is awkward. Comments scroll out from under the text. "Fix paragraph 3" loses its anchor as soon as the doc is rewritten. Google-Docs-style tools don't speak the file on disk.
|
|
24
|
+
|
|
25
|
+
Redline puts an inline-comment layer on top of a local Markdown file, with a Claude agent participating in the review thread. Comments are anchored to the text they're about. The conversation is real-time. Accepted changes are applied back to the file you started with, no copy-paste.
|
|
26
|
+
|
|
27
|
+
## Who it's for
|
|
28
|
+
|
|
29
|
+
People shipping docs with the help of AI agents:
|
|
30
|
+
|
|
31
|
+
- Engineers reviewing **AI-generated PRDs and architecture specs** before they go to a team.
|
|
32
|
+
- PMs and tech leads reviewing **Claude Code / Codex / Cursor output** before merging.
|
|
33
|
+
- Tech writers running a **human-in-the-loop AI doc review** on README drafts.
|
|
34
|
+
- Anyone who wants **inline comments on Markdown** without uploading the file to a SaaS.
|
|
35
|
+
|
|
36
|
+
## How it works
|
|
37
|
+
|
|
38
|
+
Two long-lived processes:
|
|
39
|
+
|
|
40
|
+
- **Server** ([src/server.ts](src/server.ts)) — renders Markdown, serves the review UI, accepts comment / reply / resolve POSTs, broadcasts SSE events.
|
|
41
|
+
- **Agent** ([src/agent.ts](src/agent.ts)) — child process listening to the SSE stream. Calls `claude -p` to compose replies and post them back. When you accept a round, the agent runs the document revision pass and writes the result to disk.
|
|
42
|
+
|
|
43
|
+
Review state lives in a sidecar JSON file at `.review/<filename>.json` next to the doc. History snapshots of every revision land in `.review/history/<filename>.<iso>.md` *before* the revision is written, so you can roll back from disk if a revision goes sideways. Both should be gitignored unless you want them in the repo.
|
|
44
|
+
|
|
45
|
+
After each revision, the browser overlays a side-by-side diff against the previous round so you can read the agent's changes in context. From there you either open another round of comments or click **Looks good** to finish.
|
|
46
|
+
|
|
47
|
+
[CLAUDE.md](CLAUDE.md) has the full architecture tour: sidecar schema, SSE event vocabulary, model picking, frontend gotchas.
|
|
48
|
+
|
|
49
|
+
## Use cases
|
|
50
|
+
|
|
51
|
+
- **Reviewing AI-generated PRDs.** Hand Claude Code a one-line brief, let it draft the PRD, then redline it line by line before passing it on.
|
|
52
|
+
- **Reviewing architecture specs.** Mark assumptions you want challenged, ask the agent to expand sections, ship the revised spec.
|
|
53
|
+
- **Reviewing README drafts.** Claude wrote your README — read through, leave inline comments where the framing is off, accept the revision in one click.
|
|
54
|
+
- **Approving Claude Code output before merge.** Use the bundled [redline-review skill](skills/redline-review/SKILL.md) so your outer agent automatically hands you the doc to sign off on.
|
|
55
|
+
- **Human approval loops for agent-written docs.** Anywhere an agent needs your sign-off on prose before continuing, run it through Redline.
|
|
56
|
+
|
|
57
|
+
## Reviewing for a specific lens
|
|
58
|
+
|
|
59
|
+
Pass `--context` at startup to tell Claude what you're focused on for the review. The context is shown in a banner above the doc and threaded into both the reply and revision prompts, so the agent can weight its responses accordingly.
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
bunx @levistudio/redline ./spec.md --context "Reviewing for technical accuracy, not prose style."
|
|
63
|
+
bunx @levistudio/redline ./api-rfc.md --context "Looking for missing security considerations and breaking changes."
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The context is persisted in the sidecar on first run, so it carries across rounds and is available the next time you reopen the same review.
|
|
67
|
+
|
|
68
|
+
## Manual annotation (no agent)
|
|
69
|
+
|
|
70
|
+
If you just want inline comments on a Markdown file without a Claude conversation, pass `--no-agent`:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
bunx @levistudio/redline ./spec.md --no-agent
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The server still runs, you can still leave comments and resolve them, but no agent process is spawned and no `claude` binary is required on your PATH. The header shows a "Manual mode" pill so the absence of replies is intentional, not broken. Useful for:
|
|
77
|
+
|
|
78
|
+
- Annotating a doc you don't want sent to Claude
|
|
79
|
+
- Trying Redline before configuring Claude Code
|
|
80
|
+
- Quick inline-comment passes where you don't want a revision step
|
|
81
|
+
|
|
82
|
+
## One-shot revision
|
|
83
|
+
|
|
84
|
+
If you already have a sidecar with resolved comments and just want to apply the revision without the live UI:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
bunx @levistudio/redline resolve ./spec.md
|
|
88
|
+
bunx @levistudio/redline resolve ./spec.md --model claude-sonnet-4-6
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Outer-agent handoff (optional)
|
|
92
|
+
|
|
93
|
+
Want your AI coding agent (Claude Code etc.) to invoke Redline automatically whenever it produces a Markdown doc you need to sign off on? Install the bundled skill:
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
git clone https://github.com/alevi/redline.git && cd redline
|
|
97
|
+
./scripts/install-skill.sh
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This copies `skills/redline-review/` into `~/.claude/skills/` with absolute paths baked in. After installation, your outer agent will reach for `redline-review` whenever it has Markdown that needs human review before it can continue.
|
|
101
|
+
|
|
102
|
+
## Local-first / security
|
|
103
|
+
|
|
104
|
+
- Single-player. Server binds to `127.0.0.1`. No auth, no audit log, no cloud.
|
|
105
|
+
- Rendered Markdown is sanitized at the render boundary; raw HTML, inline event handlers, and `javascript:` URLs are stripped before the document reaches the browser.
|
|
106
|
+
- The agent inherits your existing Claude Code OAuth session; no `ANTHROPIC_API_KEY` required.
|
|
107
|
+
- **Prompt injection in document content is not defended against.** Run Redline on docs you trust.
|
|
108
|
+
|
|
109
|
+
Full threat model: [SECURITY.md](SECURITY.md).
|
|
110
|
+
|
|
111
|
+
## Known limitations
|
|
112
|
+
|
|
113
|
+
- Markdown only. PDFs, Word docs, raw HTML — out of scope.
|
|
114
|
+
- Single-player. No multi-reviewer mode, no auth, no comment-permalink sharing.
|
|
115
|
+
- Single file per session. No folder mode or repo-wide review.
|
|
116
|
+
- Concurrent `redline` processes on the same `.md` can corrupt the sidecar.
|
|
117
|
+
- Bun required at runtime — there's no plain-Node mode.
|
|
118
|
+
|
|
119
|
+
## Develop locally
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
git clone https://github.com/alevi/redline.git
|
|
123
|
+
cd redline
|
|
124
|
+
bun install
|
|
125
|
+
bun link # exposes `redline` on PATH
|
|
126
|
+
redline ./sample.md
|
|
127
|
+
bun test
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
[MIT](LICENSE).
|
package/ROADMAP.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Roadmap
|
|
2
|
+
|
|
3
|
+
This roadmap captures where Redline is heading. It's directional, not a commitment — items may move, drop, or arrive in a different shape based on what users actually hit. File an [issue](https://github.com/alevi/redline/issues) if any of this matters to you and you want to nudge the order.
|
|
4
|
+
|
|
5
|
+
## Now
|
|
6
|
+
|
|
7
|
+
- **Polished launch.** Scoped npm publish (`@levistudio/redline`), `npx` / `bunx` parity, GitHub Release, demo GIF that captures the magic moment, landing page.
|
|
8
|
+
- **Stability under real workloads.** Long-document rendering, large sidecars, many-round sessions.
|
|
9
|
+
|
|
10
|
+
## Next
|
|
11
|
+
|
|
12
|
+
- **Folder / multi-doc review.** Open a directory, walk through Markdown files in sequence, share a single agent across the session.
|
|
13
|
+
- **Sharper diff overlay.** Per-paragraph applied-edit indicators in the post-revision view so you can see exactly which comments translated into which edits.
|
|
14
|
+
- **Comment templates.** Optional structured actions layered on top of free-text comments — `expand`, `challenge`, `cut`, `tighten` — so common requests don't need to be retyped each time.
|
|
15
|
+
- **Read-only share link.** Optional, opt-in, ephemeral — lets a teammate spectate a review without write access.
|
|
16
|
+
- **Editor integrations.** VS Code / Cursor / JetBrains panel that opens the review for the active Markdown file.
|
|
17
|
+
|
|
18
|
+
## Later / wishlist
|
|
19
|
+
|
|
20
|
+
- **Multi-reviewer mode.** Real names, threaded comments from multiple humans, conflict resolution. Big change to the threat model — explicitly out of scope for now.
|
|
21
|
+
- **Agent-side initiative.** The agent flags weak claims or missing sections proactively, instead of only responding to human comments.
|
|
22
|
+
- **Non-Markdown formats.** PRD review for `.docx` or `.pdf` is a separate product surface; not a near-term direction.
|
|
23
|
+
|
|
24
|
+
## Known limitations
|
|
25
|
+
|
|
26
|
+
These are intentional today. They're listed here so you can decide whether Redline fits before you try it.
|
|
27
|
+
|
|
28
|
+
- **Markdown only.** The renderer, the diff, and the agent prompts all assume Markdown.
|
|
29
|
+
- **Single-player.** No auth, no audit log, no concurrent reviewers. Designed for one human and one agent on a developer's laptop.
|
|
30
|
+
- **Single file per session.** Each `redline <file>` invocation tracks one document.
|
|
31
|
+
- **Local sidecar lock only.** Concurrent `redline` processes on the same `.md` can corrupt review state — don't run two sessions on the same file.
|
|
32
|
+
- **Bun-only at runtime.** The Node launcher exists for `npx` ergonomics, but Bun is required for the server to run.
|
|
33
|
+
- **Prompt injection in document content is not defended against.** Run on docs you trust.
|
|
34
|
+
|
|
35
|
+
## Want to influence the roadmap?
|
|
36
|
+
|
|
37
|
+
- Open an issue with the use case you have in mind. Concrete > abstract — "I want to redline a 12-doc spec folder before our quarterly planning" is more useful than "support folders".
|
|
38
|
+
- Comment on existing issues to upvote / disagree.
|
|
39
|
+
- For security issues, use [private vulnerability reporting](https://github.com/alevi/redline/security/advisories/new) instead of an issue.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
Redline is a **single-player, localhost-only** review tool that runs on the operator's machine. Reviewed Markdown is rendered in a browser and fed to a local Claude subprocess. This document describes what is defended against, what is out of scope, and how to report issues.
|
|
4
|
+
|
|
5
|
+
## Threat model
|
|
6
|
+
|
|
7
|
+
**In scope.** Two threats Redline actively defends against:
|
|
8
|
+
|
|
9
|
+
1. **Network exposure of the local server.** The server binds to `127.0.0.1` only — it is not reachable from other machines on the LAN, even when the user is on shared wifi. The printed `localhost` URL matches the actual bind.
|
|
10
|
+
2. **XSS via the rendered Markdown.** A reviewed `.md` may contain raw HTML, inline event handlers, or `javascript:`/`data:` URLs — these are stripped at the render boundary before the document reaches the browser. Sanitization covers both the main reader and the diff overlay.
|
|
11
|
+
|
|
12
|
+
**Out of scope.** Redline is a developer tool, not a sandbox. It is appropriate to run on documents you authored, generated, or trust. The following are **not** defended against:
|
|
13
|
+
|
|
14
|
+
- **Adversarial Markdown trying to manipulate the agent via prompt injection.** The agent reads the document text and comment thread as input to Claude; carefully crafted content can attempt to redirect the model. Treat agent output the same way you'd treat any other AI output you didn't fully verify.
|
|
15
|
+
- **Multiple `redline` processes operating on the same file.** The sidecar lock is in-process; concurrent runs on the same `.md` can corrupt review state.
|
|
16
|
+
- **Malicious CLI flags or environment.** `redline` runs with the operator's full file-system permissions and shells out to `claude -p` with the operator's auth.
|
|
17
|
+
- **Sharing reviews across operators.** Redline is single-player — it has no auth, no access control, and no audit log.
|
|
18
|
+
|
|
19
|
+
## What "single-player, localhost" means in practice
|
|
20
|
+
|
|
21
|
+
- Run Redline only on documents from sources you trust. The HTML render is sanitized; the content the agent ingests is not.
|
|
22
|
+
- Don't run Redline on a multi-user machine where another user has shell access — they could read the document directly from disk and connect to your loopback port.
|
|
23
|
+
- Don't expose the redline server through tunnels, reverse proxies, or `socat`. It assumes loopback semantics throughout.
|
|
24
|
+
|
|
25
|
+
## Reporting a vulnerability
|
|
26
|
+
|
|
27
|
+
If you find a security issue, please **do not** open a public issue. Use GitHub's [private vulnerability reporting](https://github.com/alevi/redline/security/advisories/new) — that opens a private channel for the report and a coordinated fix.
|
|
28
|
+
|
|
29
|
+
For non-security bugs, regular [issues](https://github.com/alevi/redline/issues) are the right place.
|
|
30
|
+
|
|
31
|
+
## Supported versions
|
|
32
|
+
|
|
33
|
+
Redline is built for the latest published `main`. There is no LTS branch and no backport policy — security fixes land on `main` and are picked up by re-pulling.
|
package/bin/redline.cjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Node-compatible launcher so `npx @levistudio/redline <file>` works even when the
|
|
3
|
+
// caller doesn't have Bun on PATH yet. Bun is still required at runtime — the
|
|
4
|
+
// server is `Bun.serve()` — but we surface the missing-Bun case as one clean
|
|
5
|
+
// message rather than an opaque shebang/parse failure.
|
|
6
|
+
//
|
|
7
|
+
// On `bunx @levistudio/redline`, this file is also the entry point; it just finds
|
|
8
|
+
// the same Bun that ran it and re-execs `bun run src/cli.ts`. No-op for the
|
|
9
|
+
// caller, single code path for us.
|
|
10
|
+
"use strict";
|
|
11
|
+
|
|
12
|
+
const { spawn, spawnSync } = require("node:child_process");
|
|
13
|
+
const fs = require("node:fs");
|
|
14
|
+
const path = require("node:path");
|
|
15
|
+
|
|
16
|
+
const CLI = path.resolve(__dirname, "..", "src", "cli.ts");
|
|
17
|
+
|
|
18
|
+
function findBun() {
|
|
19
|
+
if (process.env.BUN_INSTALL_BIN) {
|
|
20
|
+
const candidate = path.join(process.env.BUN_INSTALL_BIN, process.platform === "win32" ? "bun.exe" : "bun");
|
|
21
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
22
|
+
}
|
|
23
|
+
const probe = spawnSync(
|
|
24
|
+
process.platform === "win32" ? "where" : "which",
|
|
25
|
+
["bun"],
|
|
26
|
+
{ encoding: "utf8" }
|
|
27
|
+
);
|
|
28
|
+
if (probe.status === 0 && probe.stdout) {
|
|
29
|
+
const first = probe.stdout.split(/\r?\n/)[0].trim();
|
|
30
|
+
if (first) return first;
|
|
31
|
+
}
|
|
32
|
+
const home = process.env.HOME || process.env.USERPROFILE;
|
|
33
|
+
if (home) {
|
|
34
|
+
const fallback = path.join(home, ".bun", "bin", process.platform === "win32" ? "bun.exe" : "bun");
|
|
35
|
+
if (fs.existsSync(fallback)) return fallback;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const bun = findBun();
|
|
41
|
+
if (!bun) {
|
|
42
|
+
process.stderr.write(
|
|
43
|
+
"\nRedline requires Bun. Install it and re-run:\n" +
|
|
44
|
+
" https://bun.sh\n\n" +
|
|
45
|
+
"Then: bunx @levistudio/redline <file.md> (or rerun the same npx command)\n\n"
|
|
46
|
+
);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const child = spawn(bun, ["run", CLI, ...process.argv.slice(2)], { stdio: "inherit" });
|
|
51
|
+
child.on("exit", (code, signal) => {
|
|
52
|
+
if (signal) {
|
|
53
|
+
try { process.kill(process.pid, signal); } catch { process.exit(1); }
|
|
54
|
+
} else {
|
|
55
|
+
process.exit(code == null ? 0 : code);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
child.on("error", (err) => {
|
|
59
|
+
process.stderr.write(`\n[redline] Failed to launch bun: ${err.message}\n`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@levistudio/redline",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Inline comments on Markdown files, for human-in-the-loop AI doc review.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"markdown",
|
|
7
|
+
"code-review",
|
|
8
|
+
"ai",
|
|
9
|
+
"claude",
|
|
10
|
+
"bun",
|
|
11
|
+
"documentation"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/alevi/redline#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/alevi/redline/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/alevi/redline.git"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"author": "Alon Levi <alonlevi@gmail.com>",
|
|
23
|
+
"bin": {
|
|
24
|
+
"redline": "bin/redline.cjs"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin",
|
|
28
|
+
"src",
|
|
29
|
+
"!src/**/*.test.ts",
|
|
30
|
+
"scripts/install-skill.sh",
|
|
31
|
+
"skills",
|
|
32
|
+
"README.md",
|
|
33
|
+
"SECURITY.md",
|
|
34
|
+
"ROADMAP.md",
|
|
35
|
+
"CHANGELOG.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"bun": ">=1.0.0"
|
|
40
|
+
},
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"start": "bun src/cli.ts",
|
|
46
|
+
"test": "bun test"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"hono": "^4.7.0",
|
|
50
|
+
"marked": "^15.0.0",
|
|
51
|
+
"proper-lockfile": "^4.1.2",
|
|
52
|
+
"sanitize-html": "^2.17.3"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@happy-dom/global-registrator": "^20.9.0",
|
|
56
|
+
"@types/bun": "latest",
|
|
57
|
+
"@types/proper-lockfile": "^4.1.4",
|
|
58
|
+
"@types/sanitize-html": "^2.16.1",
|
|
59
|
+
"happy-dom": "^20.9.0"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install the redline-review skill into the user's global Claude skills directory
|
|
3
|
+
# so it's reachable from any project, not just this repo.
|
|
4
|
+
#
|
|
5
|
+
# This is a copy, not a symlink: the repo lives in paths that move (worktrees,
|
|
6
|
+
# renamed projects), and a stale symlink that vanishes is worse than a copy
|
|
7
|
+
# that needs an explicit refresh after pulling skill changes.
|
|
8
|
+
#
|
|
9
|
+
# We also generate a self-contained launcher next to the installed skill,
|
|
10
|
+
# with absolute paths to `bun` and `src/cli.ts` baked in. SKILL.md points
|
|
11
|
+
# at the launcher. This removes every $PATH dependency from the invocation
|
|
12
|
+
# chain — both `redline` itself AND the `#!/usr/bin/env bun` shebang it would
|
|
13
|
+
# otherwise resolve through PATH at runtime. (Real-usage failure: an outside
|
|
14
|
+
# session hit `env: bun: No such file or directory` because ~/.bun/bin isn't
|
|
15
|
+
# on $PATH in non-interactive Claude Bash subprocesses.)
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
20
|
+
SRC="$REPO_ROOT/skills/redline-review"
|
|
21
|
+
DEST="${CLAUDE_HOME:-$HOME/.claude}/skills/redline-review"
|
|
22
|
+
PLACEHOLDER="__REDLINE_BIN__"
|
|
23
|
+
|
|
24
|
+
if [ ! -d "$SRC" ]; then
|
|
25
|
+
echo "error: skill source not found at $SRC" >&2
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
CLI_ABS="$REPO_ROOT/src/cli.ts"
|
|
30
|
+
if [ ! -f "$CLI_ABS" ]; then
|
|
31
|
+
echo "error: redline cli not found at $CLI_ABS" >&2
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Resolve bun. Prefer $PATH; fall back to ~/.bun/bin/bun.
|
|
36
|
+
if command -v bun >/dev/null 2>&1; then
|
|
37
|
+
BUN_ABS="$(command -v bun)"
|
|
38
|
+
elif [ -x "$HOME/.bun/bin/bun" ]; then
|
|
39
|
+
BUN_ABS="$HOME/.bun/bin/bun"
|
|
40
|
+
else
|
|
41
|
+
echo "error: bun not found in PATH or at ~/.bun/bin/bun (install: https://bun.sh)" >&2
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
mkdir -p "$(dirname "$DEST")"
|
|
46
|
+
rm -rf "$DEST"
|
|
47
|
+
cp -R "$SRC" "$DEST"
|
|
48
|
+
|
|
49
|
+
# Generate a self-contained launcher with absolute paths to bun and cli.ts.
|
|
50
|
+
LAUNCHER="$DEST/redline"
|
|
51
|
+
cat > "$LAUNCHER" <<EOF
|
|
52
|
+
#!/bin/bash
|
|
53
|
+
# Auto-generated by scripts/install-skill.sh — re-run that script to refresh.
|
|
54
|
+
exec "$BUN_ABS" run "$CLI_ABS" "\$@"
|
|
55
|
+
EOF
|
|
56
|
+
chmod +x "$LAUNCHER"
|
|
57
|
+
|
|
58
|
+
# Substitute the launcher path into the installed SKILL.md.
|
|
59
|
+
SKILL_FILE="$DEST/SKILL.md"
|
|
60
|
+
if ! grep -q "$PLACEHOLDER" "$SKILL_FILE"; then
|
|
61
|
+
echo "error: placeholder '$PLACEHOLDER' not found in $SKILL_FILE — source skill is out of sync" >&2
|
|
62
|
+
exit 1
|
|
63
|
+
fi
|
|
64
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
65
|
+
sed -i '' "s|$PLACEHOLDER|$LAUNCHER|g" "$SKILL_FILE"
|
|
66
|
+
else
|
|
67
|
+
sed -i "s|$PLACEHOLDER|$LAUNCHER|g" "$SKILL_FILE"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
echo "Installed redline-review → $DEST"
|
|
71
|
+
echo " launcher: $LAUNCHER"
|
|
72
|
+
echo " bun: $BUN_ABS"
|
|
73
|
+
echo " cli: $CLI_ABS"
|
|
74
|
+
echo
|
|
75
|
+
echo "Your AI coding agents (Claude Code, etc.) can now use the redline-review skill."
|
|
76
|
+
echo "Try it: ask your agent to redline a markdown file you'd like feedback on."
|
|
77
|
+
echo
|
|
78
|
+
echo "Re-run this script after pulling skill changes."
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: redline-review
|
|
3
|
+
description: Hand a markdown file you produced (spec, RFC, brief, plan) to the human for inline review, wait for them to finish, then continue with the approved document. Use whenever the human's sign-off on a document is the next step in the work.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Handing off a markdown doc for human review
|
|
7
|
+
|
|
8
|
+
When you've produced a markdown document that the human needs to read, comment on, and approve before you continue, use Redline. It opens a browser-based reader where the human leaves inline comments, an agent subprocess Redline spawns replies to them, the human signs off, and the document on disk is left in its final approved state.
|
|
9
|
+
|
|
10
|
+
## How to invoke it
|
|
11
|
+
|
|
12
|
+
The redline binary lives at `__REDLINE_BIN__` (substituted at install time — if you see the literal placeholder string, the skill was installed incorrectly; tell the human to re-run `scripts/install-skill.sh` from the redline repo). Always invoke it by this absolute path. Do not call bare `redline` and do not try to "fix" PATH issues by running `bun link` or guessing where the repo lives.
|
|
13
|
+
|
|
14
|
+
**Always background the launcher and poll.** Never run `__REDLINE_BIN__` as a foreground/blocking Bash call: the Bash tool buffers stdout until the process exits, so you would never see the URL the human needs to click and your "I'll wait while you review" message would be a lie. Use this pattern:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
FILE=/abs/path/to/file.md
|
|
18
|
+
DIR=$(dirname "$FILE"); BASE=$(basename "$FILE")
|
|
19
|
+
STARTUP="$DIR/.review/$BASE.startup.json"
|
|
20
|
+
RESULT="$DIR/.review/$BASE.result"
|
|
21
|
+
LOG=/tmp/redline-$BASE.log
|
|
22
|
+
|
|
23
|
+
# Kick off the review in the background.
|
|
24
|
+
__REDLINE_BIN__ "$FILE" > "$LOG" 2>&1 &
|
|
25
|
+
|
|
26
|
+
# Step 1: wait for startup, read the URL.
|
|
27
|
+
for i in $(seq 1 60); do [ -f "$STARTUP" ] && break; sleep 0.5; done
|
|
28
|
+
if [ ! -f "$STARTUP" ]; then
|
|
29
|
+
echo "redline did not start; check $LOG" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
URL=$(grep -o '"url": *"[^"]*"' "$STARTUP" | sed 's/.*"\(http[^"]*\)".*/\1/')
|
|
33
|
+
PID=$(grep -o '"pid": *[0-9]*' "$STARTUP" | grep -o '[0-9]*')
|
|
34
|
+
echo "REDLINE_URL: $URL"
|
|
35
|
+
echo "REDLINE_PID: $PID"
|
|
36
|
+
|
|
37
|
+
# Step 2: surface the URL to the human (you do this after the Bash call returns
|
|
38
|
+
# — see the next section), then wait for the redline process to exit. Watching
|
|
39
|
+
# the PID (essentially free) instead of polling for the result file means you
|
|
40
|
+
# wake up within ~0.5s of the human clicking Done, not up to 30s later.
|
|
41
|
+
while kill -0 "$PID" 2>/dev/null; do sleep 0.5; done
|
|
42
|
+
cat "$RESULT"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The startup file at `.review/<basename>.startup.json` is written synchronously when the server begins listening; it contains `url`, `port`, `file`, `result_file`, `started_at`, `pid`. The result file at `.review/<basename>.result` is written when the session ends (approved, abandoned, or error).
|
|
46
|
+
|
|
47
|
+
In practice, run the script above as **two separate Bash calls** so you can tell the human the URL between steps:
|
|
48
|
+
1. First call: everything through `echo "REDLINE_PID: $PID"`. Returns in ~1s with the URL and PID on stdout.
|
|
49
|
+
2. Surface the URL to the human in your reply text (see "Surfacing the URL" below).
|
|
50
|
+
3. Second call: just the `while kill -0` loop waiting for the PID, then `cat "$RESULT"`. Long timeout (`timeout: 1800000` = 30 min, or longer).
|
|
51
|
+
|
|
52
|
+
If invocation fails (binary missing, startup file never appears, etc.), surface the error verbatim and stop — do not try to recover. The human will re-run the install script.
|
|
53
|
+
|
|
54
|
+
### Pass context with `--context`
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
__REDLINE_BIN__ /abs/path/file.md --context "Draft of the auth-rewrite RFC — focus on the migration plan in §4."
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The context string is shown in the reader's header so the human knows what they're being asked to review and what you'd like them to focus on. Use it whenever the file alone doesn't make the ask obvious. One sentence is plenty.
|
|
61
|
+
|
|
62
|
+
### Surfacing the URL
|
|
63
|
+
|
|
64
|
+
After the first Bash call returns with `REDLINE_URL: http://localhost:NNNN`, surface that URL in your reply text. The human has no other signal that something is waiting for them. One short sentence:
|
|
65
|
+
|
|
66
|
+
> "Opening this in Redline for review at http://localhost:NNNN — cmd-click to open. I'll continue once you click Done."
|
|
67
|
+
|
|
68
|
+
(There is an `--open` flag that auto-launches the browser, but prefer leaving it off — the human may not be at the keyboard the moment the session starts, and a stolen-focus browser tab is worse than a URL they click when ready.)
|
|
69
|
+
|
|
70
|
+
## How to interpret the result
|
|
71
|
+
|
|
72
|
+
When the polling loop's Bash call returns, the `cat "$RESULT"` at the end of it has printed the result JSON to stdout.
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{ "status": "approved", "file": "/abs/path/to/file.md", "rounds": 2, "comments": 5 }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Statuses:
|
|
79
|
+
|
|
80
|
+
- **`approved`** — Human signed off. Re-read the file from disk and continue. Note: the file may be byte-identical to what you handed off — if every comment was Q&A the agent answered with `accept-as-is`, no revision pass ran. That's still a valid approval, not a no-op.
|
|
81
|
+
- **`abandoned`** — Human closed the tab or Ctrl+C'd without clicking Done. The doc is in whatever state it was last revised to, but has not been signed off. Ask the human what they want to do.
|
|
82
|
+
- **`error`** — A revision pass failed. The result file includes a `reason` field with the failure message; `.review/errors.log` next to the file has more detail. Surface both to the human.
|
|
83
|
+
|
|
84
|
+
## Outer-agent handoff pattern
|
|
85
|
+
|
|
86
|
+
The full loop, when you are the outer agent producing the doc:
|
|
87
|
+
|
|
88
|
+
1. Write the markdown file to disk at an absolute path.
|
|
89
|
+
2. Tell the human in one sentence what's about to happen.
|
|
90
|
+
3. First Bash call: launch `__REDLINE_BIN__ <abs-path> --context "<one-liner>"` in the background and poll for `.startup.json`. Returns in ~1s with the URL.
|
|
91
|
+
4. Surface the URL to the human in your reply text so they can cmd-click to open.
|
|
92
|
+
5. Second Bash call: wait on the redline PID (`while kill -0 "$PID" 2>/dev/null; do sleep 0.5; done`) then `cat "$RESULT"`, with a long timeout (30+ min). While the session runs, you are idle — do not start unrelated work, do not run other tools.
|
|
93
|
+
6. On `approved`: re-read the file from disk (it may have been revised) and continue with whatever required sign-off.
|
|
94
|
+
7. On `abandoned` or `error`: stop and ask the human how to proceed; do not retry automatically.
|
|
95
|
+
|
|
96
|
+
You do not need to reply to comments — Redline spawns its own agent subprocess for that. You do not need to invoke `redline resolve` separately — revisions happen inside the session when the human accepts.
|
|
97
|
+
|
|
98
|
+
## When *not* to use this
|
|
99
|
+
|
|
100
|
+
- The doc doesn't need human sign-off — just commit it.
|
|
101
|
+
- The human is not at the keyboard (e.g. an autonomous run). Redline requires a live browser session.
|
|
102
|
+
- The doc is something other than markdown.
|