@erdemtuna/doc-review 0.7.0 → 0.8.1
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/README.md +58 -5
- package/package.json +3 -2
- package/src/SKILL.md +54 -7
- package/src/anchor-text.js +45 -0
- package/src/atomic-write.js +53 -0
- package/src/chrome-client.js +1272 -252
- package/src/chrome-session.js +70 -0
- package/src/chrome.css +278 -329
- package/src/chrome.html +81 -35
- package/src/cli.js +58 -132
- package/src/comment-anchor.js +22 -0
- package/src/comment-target.js +77 -0
- package/src/edit-limits.js +23 -0
- package/src/icons.js +251 -0
- package/src/paths.js +5 -1
- package/src/poll-transport.js +222 -0
- package/src/positioning.js +107 -0
- package/src/review-mode.js +59 -0
- package/src/sdk.js +792 -69
- package/src/serialize.js +0 -14
- package/src/server.js +39 -9
- package/src/setup-guidance.js +128 -0
- package/src/setup.js +41 -15
- package/src/state.js +37 -21
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Doc Review
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Review HTML, Markdown, and localhost pages, edit when you choose, leave contextual comments, and send all feedback to your AI agent at once.
|
|
4
4
|
|
|
5
5
|
[Read the original Human Review launch post](https://creatoreconomy.so/p/use-my-human-review-skill-to-edit-html-markdown-visually)
|
|
6
6
|
|
|
@@ -37,6 +37,20 @@ no longer need them.
|
|
|
37
37
|
|
|
38
38
|
## How to use /doc-review
|
|
39
39
|
|
|
40
|
+
Review is opt-in: explicitly invoke `/doc-review` or ask to open an interactive
|
|
41
|
+
browser review. Writing, updating, or asking for a general review of content does
|
|
42
|
+
not automatically open the browser or start polling. An explicitly started review
|
|
43
|
+
continues until you end it or switch tasks.
|
|
44
|
+
|
|
45
|
+
After updating installed skill instructions, start a fresh agent session or reload
|
|
46
|
+
skills (in Copilot CLI, `/skills reload`). Setup copies the executing package's
|
|
47
|
+
skill template; rerunning setup from an older package can restore older behavior.
|
|
48
|
+
Setup updates its own `AGENTS.md` guidance inside `<!-- BEGIN doc-review -->` and
|
|
49
|
+
`<!-- END doc-review -->` markers. Exact, known older generated blocks are migrated.
|
|
50
|
+
Custom guidance is preserved with a migration message rather than overwritten;
|
|
51
|
+
update it yourself if it requests automatic review. Invalid or duplicate markers
|
|
52
|
+
must be corrected before setup can proceed.
|
|
53
|
+
|
|
40
54
|

|
|
41
55
|
|
|
42
56
|
Open an HTML or Markdown file:
|
|
@@ -51,9 +65,15 @@ Review a page running on localhost:
|
|
|
51
65
|
/doc-review (localhost URL)
|
|
52
66
|
```
|
|
53
67
|
|
|
54
|
-
Doc Review opens
|
|
68
|
+
Doc Review opens in **View** (`Editing off, comments enabled`) so links, buttons, summaries, tabs, and application controls work normally. The centered mode selector switches to **Edit** (`Direct editing on`) when you want to change content directly. Commenting stays available in both modes.
|
|
69
|
+
|
|
70
|
+
Select text, or hover or focus an element, then use the nearby comment icon. `Ctrl+Alt+M` (`Cmd+Option+M` on macOS) opens a comment for the current selection. Press Enter to submit or Shift+Enter for a new line. On desktop the composer stays beside its target, or pins to the effective top or bottom scrolling edge when that target leaves view. **Back to selection** reveals the target without changing the draft. The full-width sheet is used only at the narrow responsive breakpoint.
|
|
55
71
|
|
|
56
|
-
|
|
72
|
+
**Comments** is the single toolbar entry point for the closed-by-default review drawer. Its feedback inventory scrolls independently while the overall note and **Send to agent** controls remain fixed. Submitting a comment creates its normal highlight and increments the count, but keeps the card closed until you activate its mark or choose **Jump to**. Focus returns to the exact element or selection you reviewed.
|
|
73
|
+
|
|
74
|
+
Aligned cards show **Edit**, **Close**, and **More**; drawer cards show **Jump to**, **Edit**, and **More**. Delete lives only in **More** and requires an inline confirmation. Quote hints preserve both the beginning and ending of long selections. Editing has explicit **Save** and **Cancel** controls: Enter saves, Shift+Enter adds a line, Escape cancels, and moving focus never autosaves. A draft follows its comment between aligned and drawer cards and keeps its caret through target movement.
|
|
75
|
+
|
|
76
|
+
For writable HTML files, Edit saves direct changes automatically. Markdown and localhost remain editable feedback-only surfaces: their rendered HTML is never written over the source, so click Send and let the agent apply those edits.
|
|
57
77
|
|
|
58
78
|
File and rendered Markdown reviews run with authored scripts and inline handlers
|
|
59
79
|
blocked, an opaque iframe origin, and no popup or download permission. Their
|
|
@@ -81,6 +101,38 @@ npx -y @erdemtuna/doc-review poll path/to/file.html --ack b_0123456789abcdef --t
|
|
|
81
101
|
A stale or repeated batch ID is harmless: it never clears newer feedback. The
|
|
82
102
|
complete acknowledgement command is included in each response's `next_step`.
|
|
83
103
|
|
|
104
|
+
### Feedback reliability and limits
|
|
105
|
+
|
|
106
|
+
`poll` without `--timeout` waits for at most 12 hours. Explicit timeouts include
|
|
107
|
+
server discovery, reconnection, and retry backoff, not just time spent connected.
|
|
108
|
+
Recoverable connection drops are retried; invalid responses, authorization errors,
|
|
109
|
+
and incompatible servers fail visibly. A timeout does not discard feedback: use
|
|
110
|
+
`status` to check it, then start another poll if the review is still wanted.
|
|
111
|
+
|
|
112
|
+
Each edit text or HTML field is limited to 200,000 Unicode code points. Oversized
|
|
113
|
+
edits carry `truncated: true` and `truncated_fields` naming incomplete fields.
|
|
114
|
+
The agent must not use partial text or HTML as a complete replacement or invent
|
|
115
|
+
the rest. It must obtain the full edit from an authoritative source or ask you
|
|
116
|
+
for it before acknowledging the batch.
|
|
117
|
+
|
|
118
|
+
External writes to a Markdown source refresh its rendered baseline without
|
|
119
|
+
clearing unsent feedback. That preserves your edits; it does not automatically
|
|
120
|
+
resolve conflicts with the changed source. HTML autosaves keep their existing
|
|
121
|
+
behavior.
|
|
122
|
+
|
|
123
|
+
### Upgrading from v0.8.0
|
|
124
|
+
|
|
125
|
+
The updated CLI requires server protocol 13. An older server that is still
|
|
126
|
+
running is not silently reused or forcibly replaced. End active reviews and
|
|
127
|
+
shut down that specific old server (or let it exit when idle) before restarting
|
|
128
|
+
Doc Review. Keep the `.doc-review` state directory: pending batches and their
|
|
129
|
+
exact receipt IDs survive a controlled restart.
|
|
130
|
+
|
|
131
|
+
After installing the updated package, rerun `doc-review setup --global` for
|
|
132
|
+
personal skills and `doc-review setup` in projects with generated guidance.
|
|
133
|
+
Reload skills or start a fresh agent session afterward. A newer CLI paired with
|
|
134
|
+
older instructions is not a complete upgrade.
|
|
135
|
+
|
|
84
136
|
## What this skill lets you do
|
|
85
137
|
|
|
86
138
|
- **Edit text directly and tweak basic formatting** (e.g., bold, italic).
|
|
@@ -89,8 +141,8 @@ complete acknowledgement command is included in each response's `next_step`.
|
|
|
89
141
|
- **Resize images** by dragging their corner, and **move images** by dragging them to a new spot.
|
|
90
142
|
- **Rearrange the page** — hover any block and drag the handle on its left edge to move the whole block somewhere else.
|
|
91
143
|
- **Paste images** from your clipboard — file reviews save them beside the document; localhost reviews stage them for the agent to place in the app source.
|
|
92
|
-
- **Select a phrase and leave a comment**
|
|
93
|
-
- **Comment on an image, chart, or section**
|
|
144
|
+
- **Select a phrase and leave a comment** from the contextual icon, or press Ctrl+Alt+M / Cmd+Option+M.
|
|
145
|
+
- **Comment on an image, chart, control, or section** from its hover or keyboard-focus affordance without taking over its normal click.
|
|
94
146
|
- **Remove elements** without explaining the deletion in chat.
|
|
95
147
|
- **Command-click links** to review multiple pages without losing your feedback.
|
|
96
148
|
- **Send every edit and comment at once** instead of writing a long chat message.
|
|
@@ -107,6 +159,7 @@ Doc Review works well for editing AI-generated plans, updating landing pages, re
|
|
|
107
159
|
- [`SKILL.md`](src/SKILL.md) teaches Claude Code, Codex, and other agents how to use Doc Review.
|
|
108
160
|
|
|
109
161
|
Everything runs on your computer. Doc Review doesn’t require an account, cloud service, database, or API key.
|
|
162
|
+
Comment geometry remains local, transient presentation data and is never written to review state or sent to the agent. After the agent acknowledges the exact delivered `batch_id`, the comments carried by that batch disappear; newer comments and corrections remain.
|
|
110
163
|
|
|
111
164
|
## Upstream project
|
|
112
165
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erdemtuna/doc-review",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Review and edit agent-generated files and localhost pages in the browser, then send the whole batch back to your agent.",
|
|
5
5
|
"author": "Peter Yang",
|
|
6
6
|
"homepage": "https://github.com/erdemtuna/doc-review#readme",
|
|
@@ -49,7 +49,8 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@playwright/test": "^1.62.1",
|
|
52
|
-
"jsdom": "^29.1.1"
|
|
52
|
+
"jsdom": "^29.1.1",
|
|
53
|
+
"lucide-static": "^1.34.0"
|
|
53
54
|
},
|
|
54
55
|
"dependencies": {
|
|
55
56
|
"marked": "^18.0.7"
|
package/src/SKILL.md
CHANGED
|
@@ -1,20 +1,56 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: doc-review
|
|
3
|
-
description: Open an HTML file, Markdown file, or localhost page
|
|
3
|
+
description: Open an HTML file, Markdown file, or localhost page for View-first interactive browser feedback. Use only when the user explicitly invokes /doc-review or requests an interactive browser review. Do not invoke merely because you write, update, discuss, or review a document or web page.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# doc-review
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
## Activation
|
|
9
|
+
|
|
10
|
+
Start only when the user explicitly invokes `/doc-review` or asks to open an
|
|
11
|
+
interactive browser review. A generic request to review, proofread, analyze, write,
|
|
12
|
+
or update content is not permission to open this workflow. Another skill's
|
|
13
|
+
automatic review step is not user permission.
|
|
14
|
+
|
|
15
|
+
Without that request, respond normally without opening a review or polling for
|
|
16
|
+
feedback. Once the user starts a review, continue its feedback loop until they
|
|
17
|
+
end it; stop if they cancel or switch to a different task.
|
|
18
|
+
|
|
19
|
+
## Review behavior
|
|
20
|
+
|
|
21
|
+
The user reviews your HTML, Markdown, or localhost page in a real browser. It starts in View,
|
|
22
|
+
where normal page controls work. They can switch to Edit, comment explicitly in either mode,
|
|
23
|
+
and send the whole batch at once.
|
|
10
24
|
|
|
11
25
|
Markdown files open rendered. Their quotes and edits reference the rendered text,
|
|
12
26
|
and the file itself is never touched — apply every change to the Markdown source,
|
|
13
27
|
keeping its formatting syntax.
|
|
14
28
|
|
|
29
|
+
Comments open from a contextual icon after a text selection or an element hover/focus.
|
|
30
|
+
The keyboard shortcut is Ctrl+Alt+M, or Cmd+Option+M on macOS. Enter submits the
|
|
31
|
+
comment and Shift+Enter adds a new line. On desktop the composer stays attached
|
|
32
|
+
to its target or pins to the effective top or bottom clipping edge; Back to
|
|
33
|
+
selection reveals an offscreen target without changing the draft.
|
|
34
|
+
|
|
35
|
+
Comments is the single toolbar entry point. The drawer inventory scrolls
|
|
36
|
+
independently while the overall note and Send to agent controls remain fixed.
|
|
37
|
+
Submitting creates the normal target mark and count but leaves the card closed
|
|
38
|
+
until the user explicitly activates the mark or chooses Jump to. Focus returns
|
|
39
|
+
to the reviewed element or selection. Aligned cards expose Edit, Close, and
|
|
40
|
+
More; drawer cards expose Jump to, Edit, and More. Delete is available only
|
|
41
|
+
through More and an inline confirmation. Long quote hints preserve both ends.
|
|
42
|
+
|
|
43
|
+
Existing-comment editing uses explicit Save and Cancel controls and never saves
|
|
44
|
+
on blur. Enter saves, Shift+Enter inserts a line, and Escape cancels. The draft,
|
|
45
|
+
focus, and caret follow the comment between aligned and drawer cards and survive
|
|
46
|
+
target movement. Closing an aligned card only hides its active presentation.
|
|
47
|
+
Acknowledging the exact delivered batch removes only the comments that batch
|
|
48
|
+
carried; newer comments and corrections survive.
|
|
49
|
+
|
|
15
50
|
## The loop
|
|
16
51
|
|
|
17
|
-
1.
|
|
52
|
+
1. After the explicit review request, use the requested file or localhost route.
|
|
53
|
+
Create or update content, or start a local page, only as needed for that request.
|
|
18
54
|
2. Open it for the user:
|
|
19
55
|
|
|
20
56
|
```sh
|
|
@@ -34,6 +70,12 @@ keeping its formatting syntax.
|
|
|
34
70
|
npx -y @erdemtuna/doc-review poll path/to/file.html --timeout 600
|
|
35
71
|
```
|
|
36
72
|
|
|
73
|
+
Without `--timeout`, the CLI stops after 12 hours. An explicit timeout covers
|
|
74
|
+
the entire operation, including server discovery, reconnects, and backoff.
|
|
75
|
+
Recoverable connection drops retry within that deadline; terminal errors
|
|
76
|
+
require action rather than another automatic poll. The 600-second command
|
|
77
|
+
above deliberately uses a shorter deadline.
|
|
78
|
+
|
|
37
79
|
Keep this command in the foreground. Do not end your turn while it is waiting.
|
|
38
80
|
If your shell returns a process or session handle, keep waiting on that handle
|
|
39
81
|
until the command exits. If it prints `{"status":"timeout"}`, no feedback has
|
|
@@ -97,8 +139,13 @@ One batch covers every page the user visited, grouped by file or localhost URL.
|
|
|
97
139
|
|
|
98
140
|
## Rules
|
|
99
141
|
|
|
142
|
+
- Edits marked **`truncated`** contain incomplete fields listed in
|
|
143
|
+
`truncated_fields`. Each text/HTML field is limited to 200,000 Unicode code
|
|
144
|
+
points. Never apply a truncated field as a complete replacement or guess the
|
|
145
|
+
missing content. Obtain the complete edit from an authoritative source, or ask
|
|
146
|
+
the user for it. Do not acknowledge the batch until all feedback is handled.
|
|
100
147
|
- **`edits` are changes the user already made.** `after` is their exact wording —
|
|
101
|
-
carry it across verbatim and never revert it. If the HTML was generated from
|
|
148
|
+
unless marked truncated, carry it across verbatim and never revert it. If the HTML was generated from
|
|
102
149
|
something else (MDX, Markdown, a template), apply `after` to the **source** too,
|
|
103
150
|
or their fix disappears on the next build.
|
|
104
151
|
- When `before_html`/`after_html` are present, the user changed formatting, not
|
|
@@ -139,5 +186,5 @@ guessing from the DOM:
|
|
|
139
186
|
<div data-container="Metrics callout">…</div>
|
|
140
187
|
```
|
|
141
188
|
|
|
142
|
-
`data-block` names a region for the edit list. `data-container` also
|
|
143
|
-
|
|
189
|
+
`data-block` names a region for the edit list. `data-container` also gives the block a
|
|
190
|
+
stable label for its hover/focus comment affordance.
|
package/src/anchor-text.js
CHANGED
|
@@ -113,3 +113,48 @@ export function tidy(text, limit = 0) {
|
|
|
113
113
|
if (!limit || flat.length <= limit) return flat;
|
|
114
114
|
return `${flat.slice(0, limit - 1).trimEnd()}…`;
|
|
115
115
|
}
|
|
116
|
+
|
|
117
|
+
function graphemes(text) {
|
|
118
|
+
if (typeof Intl?.Segmenter === "function") {
|
|
119
|
+
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
120
|
+
return [...segmenter.segment(text)].map(({ segment }) => segment);
|
|
121
|
+
}
|
|
122
|
+
// Array.from is deterministic and, unlike string slicing, never splits a
|
|
123
|
+
// surrogate pair. Older engines may split a multi-code-point grapheme.
|
|
124
|
+
return Array.from(text);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function nearbyHeadBoundary(parts, ideal) {
|
|
128
|
+
for (let index = ideal; index >= Math.max(1, ideal - 8); index -= 1) {
|
|
129
|
+
if (/^\s$/u.test(parts[index - 1])) return index - 1;
|
|
130
|
+
}
|
|
131
|
+
return ideal;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function nearbyTailBoundary(parts, ideal) {
|
|
135
|
+
for (let index = ideal; index < Math.min(parts.length - 1, ideal + 8); index += 1) {
|
|
136
|
+
if (/^\s$/u.test(parts[index])) return index + 1;
|
|
137
|
+
}
|
|
138
|
+
return ideal;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Collapse whitespace and preserve both ends of a long quote. `limit` counts
|
|
143
|
+
* grapheme clusters, including the ellipsis.
|
|
144
|
+
*/
|
|
145
|
+
export function tidyMiddle(text, limit = 0) {
|
|
146
|
+
const flat = String(text == null ? "" : text)
|
|
147
|
+
.replace(/\s+/g, " ")
|
|
148
|
+
.trim();
|
|
149
|
+
if (!limit) return flat;
|
|
150
|
+
const parts = graphemes(flat);
|
|
151
|
+
if (parts.length <= limit) return flat;
|
|
152
|
+
if (limit <= 1) return "…";
|
|
153
|
+
|
|
154
|
+
const available = limit - 1;
|
|
155
|
+
const idealHead = Math.ceil(available * 0.6);
|
|
156
|
+
const idealTailStart = parts.length - (available - idealHead);
|
|
157
|
+
const headEnd = nearbyHeadBoundary(parts, idealHead);
|
|
158
|
+
const tailStart = nearbyTailBoundary(parts, idealTailStart);
|
|
159
|
+
return `${parts.slice(0, headEnd).join("").trimEnd()}…${parts.slice(tailStart).join("").trimStart()}`;
|
|
160
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
|
|
4
|
+
const RETRY_DELAYS_MS = [10, 20, 40, 80, 160];
|
|
5
|
+
const TRANSIENT_RENAME_ERRORS = new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
6
|
+
const waitState = new Int32Array(new SharedArrayBuffer(4));
|
|
7
|
+
|
|
8
|
+
// Keep Store transactions synchronous; rename backoff blocks for at most 310ms.
|
|
9
|
+
const sleepSync = (ms) => Atomics.wait(waitState, 0, 0, ms);
|
|
10
|
+
|
|
11
|
+
export function createAtomicWriter({ fileSystem = fs, sleep = sleepSync, report = console.error } = {}) {
|
|
12
|
+
return function atomicWrite(file, data) {
|
|
13
|
+
const tmp = `${file}.${process.pid}.${crypto.randomBytes(6).toString("hex")}.doc-review.tmp`;
|
|
14
|
+
let fd;
|
|
15
|
+
let created = false;
|
|
16
|
+
try {
|
|
17
|
+
fd = fileSystem.openSync(tmp, "wx");
|
|
18
|
+
created = true;
|
|
19
|
+
fileSystem.writeFileSync(fd, data);
|
|
20
|
+
fileSystem.closeSync(fd);
|
|
21
|
+
fd = undefined;
|
|
22
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
23
|
+
try {
|
|
24
|
+
fileSystem.renameSync(tmp, file);
|
|
25
|
+
return;
|
|
26
|
+
} catch (err) {
|
|
27
|
+
if (!TRANSIENT_RENAME_ERRORS.has(err.code) || attempt === RETRY_DELAYS_MS.length) throw err;
|
|
28
|
+
sleep(RETRY_DELAYS_MS[attempt]);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} catch (err) {
|
|
32
|
+
if (fd !== undefined) {
|
|
33
|
+
try {
|
|
34
|
+
fileSystem.closeSync(fd);
|
|
35
|
+
} catch (cleanupError) {
|
|
36
|
+
report(`Could not close atomic-write temporary file ${tmp}: ${cleanupError.message}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (created) {
|
|
40
|
+
try {
|
|
41
|
+
fileSystem.unlinkSync(tmp);
|
|
42
|
+
} catch (cleanupError) {
|
|
43
|
+
if (cleanupError.code !== "ENOENT") {
|
|
44
|
+
report(`Could not remove atomic-write temporary file ${tmp}: ${cleanupError.message}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const atomicWrite = createAtomicWriter();
|