@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Doc Review
2
2
 
3
- Edit HTML and Markdown files directly, leave comments like a Google Doc, and send all your feedback to your AI agent at once.
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
  ![Doc Review visual editor](assets/doc-review.png)
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 the file in your browser. Make direct edits, leave comments, and click Send. Your agent receives all your feedback in one batch, updates the source, and refreshes the page for another review.
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
- Note: For HTML files, direct edits and resizes save automatically. For Markdown and localhost pages, click Send so your agent can apply them to the source.
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** anchored to the exact text.
93
- - **Comment on an image, chart, or section** by clicking the element.
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.7.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 in the browser so the user can edit text directly and leave comments on specific parts, then send all edits and comments back to you. Use after writing or updating something the user will read — specs, plans, reports, newsletter drafts, landing pages, slide decks, and locally running web pages.
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
- The user reviews your HTML, Markdown, or localhost page in a real browser: they fix small things
9
- by typing, select anything to comment on it, and send you the whole batch at once.
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. Write or update the HTML or Markdown file, or start the local page being reviewed.
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 makes the block
143
- clickable as a comment target.
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.
@@ -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();