@erdemtuna/doc-review 0.8.0 → 0.9.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/README.md CHANGED
@@ -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,7 +65,7 @@ Review a page running on localhost:
51
65
  /doc-review (localhost URL)
52
66
  ```
53
67
 
54
- 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.
68
+ Doc Review opens in **Review**, with **View** selected, so you can read, use page controls, and comment. Switch to **Edit** to change content directly. Plain HTML saves edits to the file; scripted HTML, Markdown, and localhost pages send edits to your agent. Commenting stays available in both modes.
55
69
 
56
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.
57
71
 
@@ -59,23 +73,42 @@ Select text, or hover or focus an element, then use the nearby comment icon. `Ct
59
73
 
60
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.
61
75
 
62
- 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.
63
-
64
- File and rendered Markdown reviews run with authored scripts and inline handlers
65
- blocked, an opaque iframe origin, and no popup or download permission. Their
66
- per-render message capability is rotated for every load and navigation. It is
67
- kept out of document URLs, HTML attributes, authored DOM, and global JavaScript
68
- state; the single-use artifact URL loads a same-origin bootstrap module under a
69
- nonce-based CSP. Relative assets, including a same-artifact `<base>`, resolve
70
- beside the reviewed file, while review navigation always stays relative to the
71
- source file. External bases are ignored.
72
-
73
- Localhost reviews keep `allow-same-origin`, popup, and download compatibility so
74
- application behavior still works. Because localhost application scripts are
75
- trusted in that mode, the render capability provides correlation and stale
76
- message rejection rather than an authorization boundary. The authenticated
77
- parent still validates links and never exposes the raw-file save route to a
78
- localhost review.
76
+ ### Files and page interactions
77
+
78
+ Self-contained HTML runs its inline scripts and event handlers automatically.
79
+ There is no approval switch or renewed approval after an agent updates the file.
80
+ Plain HTML, including ordinary JSON data blocks and escaped code examples, keeps
81
+ direct autosave and Revert. Scripted HTML is **feedback-only**: your edits are sent
82
+ to the agent to apply to the original source, rather than writing script-generated
83
+ DOM into the file. The renderer re-evaluates that distinction when the source changes.
84
+ Markdown and localhost also remain feedback-only.
85
+
86
+ If a scripted page is broken, **More > Reload without scripts** provides a
87
+ temporary recovery mode. **Use page interactions** restores automatic behavior.
88
+ That preference belongs to this page in this review session, not to a saved
89
+ approval record. Disabling scripts does not make a scripted file's preview
90
+ edits writable. A changed preference takes effect when the frame is replaced;
91
+ reload handling preserves comment drafts and reports genuine source conflicts.
92
+
93
+ The supported interactive-file mode is for self-contained documents. Separate
94
+ script files, application imports, workers, and embedded applications are not
95
+ made compatible by this change. Use the existing localhost review route for
96
+ application workflows. Dependency limitations appear in contextual details,
97
+ not as a permission task or text inserted into the authored document.
98
+
99
+ Files retain an opaque iframe origin. The parent owns API authentication and
100
+ checks the frame identity for messages and writes. Authored code shares the
101
+ document with the SDK, so frame correlation is not proof of human authorship.
102
+ Relative assets stay scoped to the reviewed file. Localhost reviews retain their
103
+ existing nonopaque artifact origin, popup, and download behavior; they are fetched
104
+ and rewritten by the review server, not guaranteed to reproduce the application's
105
+ original cookies, storage, or origin-sensitive requests.
106
+
107
+ **Compatibility:** the old per-version `/trust` API is retired and returns 410.
108
+ Existing decision files are left unused. The CLI does not reuse background
109
+ servers with the previous protocol. Writable HTML save and revert requests must
110
+ identify their current served frame and source hash so delayed edits cannot
111
+ overwrite a newer file.
79
112
 
80
113
  Agent polling returns an immutable `batch_id`. After applying the batch, the
81
114
  agent acknowledges that exact receipt and keeps waiting:
@@ -87,6 +120,109 @@ npx -y @erdemtuna/doc-review poll path/to/file.html --ack b_0123456789abcdef --t
87
120
  A stale or repeated batch ID is harmless: it never clears newer feedback. The
88
121
  complete acknowledgement command is included in each response's `next_step`.
89
122
 
123
+ ### Review and Changes
124
+
125
+ **Review** keeps the document interactive. **Changes** shows a selected review
126
+ round, with round selection and comparison navigation instead of live-page controls.
127
+ The default comparison is the content captured when feedback was sent against
128
+ the result captured after the agent acknowledged that exact batch. Previous
129
+ rounds retain their own fixed endpoints rather than changing on every reload.
130
+
131
+ Content comparisons use a continuous aligned before/after reading surface with
132
+ expandable unchanged context. Source comparisons use line-numbered source hunks.
133
+ At narrow widths, a unified view retains each change's before/after attribution.
134
+ Content comparisons cover text, structure, formatting, links, and image
135
+ references. File-backed reviews also retain source for a source comparison.
136
+ Deleted passages remain readable even when there is no current element to jump
137
+ to, and uncertain matches do not pretend to identify an exact current target.
138
+ These are changes observed during a round, not proof of agent authorship or that
139
+ every comment was resolved.
140
+
141
+ History works in the normal browser for HTML, Markdown, and localhost pages.
142
+ It does not use screenshots, a browser extension, or automatic Git commits.
143
+ Live history captures the reviewed page's available content, not every possible
144
+ application state. Changes to image bytes at an unchanged URL, canvas pixels,
145
+ external stylesheet appearance, and inaccessible embedded content are outside
146
+ the comparison guarantee.
147
+
148
+ Send waits for edit persistence and writable HTML saves, then makes a short
149
+ best-effort baseline capture. Missing comparison data does not require a second
150
+ decision or prevent feedback from being sent. Available snapshots are retained;
151
+ inactive pages can have incomplete Content coverage. A last-visited live page is
152
+ never described as a fresh Send-time snapshot. Actual save or delivery failures
153
+ are reported without discarding your feedback.
154
+
155
+ When an active tab has reliable authored tab/panel identifiers, Content capture
156
+ records that visible view. A result on a different tab remains pending and asks
157
+ you to return to the original tab. Returning retries capture without clicking
158
+ through other tabs automatically. Unknown views and older snapshots are labelled
159
+ as visible-content comparisons whose matching view is unverified. This does not
160
+ capture all hidden panels or every application state. File Source comparison
161
+ still covers the whole file independently.
162
+
163
+ After acknowledgement, file-source results are captured independently of the
164
+ browser. Content results are captured when the appropriate reviewed page is
165
+ ready. These observations can have different timestamps; neither timestamp
166
+ claims that the document was captured at the instant of acknowledgement.
167
+ Background capture does not disable document interaction and retries transient
168
+ failures a bounded number of times. An unchanged file does not need a new reload
169
+ solely to capture an acknowledged result. Available Source remains readable
170
+ while Content is pending. Contextual retry or return-to-view actions handle
171
+ remaining failures; a continuously changing page may still have no stable Content
172
+ snapshot. Permanent finalization is optional in capture details, not a prerequisite
173
+ for reading the comparison. Failed or limited captures are not reported as zero
174
+ changes, and handled feedback remains archived even when history is unavailable.
175
+
176
+ Snapshots stay in the local Doc Review state directory. The latest five
177
+ completed rounds are retained automatically, while active rounds, pending
178
+ captures, and unsent feedback protect their referenced revisions. History starts
179
+ with this capability; earlier documents cannot be reconstructed from old
180
+ feedback receipts. Review snapshots can contain document content, so treat the
181
+ state directory as private.
182
+ Unreferenced snapshot files are collected at startup and during periodic
183
+ maintenance, with a one-hour grace period protecting interrupted publications.
184
+ Capture and comparison limits fail explicitly rather than publishing truncated
185
+ content as a complete comparison.
186
+
187
+ Default capture limits are 8 MiB of file source and 4 MiB of normalized content,
188
+ with at most 2,000 semantic blocks and 1,000,000 text characters. A single block
189
+ is limited to 100,000 characters. Comparisons have independent size, token, and
190
+ edit-work limits, including a 1,000,000-character budget and a 250 ms processing
191
+ budget. A stored snapshot can therefore exceed comparison limits; the UI reports
192
+ that limitation rather than claiming there were no changes.
193
+
194
+ ### Feedback reliability and limits
195
+
196
+ `poll` without `--timeout` waits for at most 12 hours. Explicit timeouts include
197
+ server discovery, reconnection, and retry backoff, not just time spent connected.
198
+ Recoverable connection drops are retried; invalid responses, authorization errors,
199
+ and incompatible servers fail visibly. A timeout does not discard feedback: use
200
+ `status` to check it, then start another poll if the review is still wanted.
201
+
202
+ Each edit text or HTML field is limited to 200,000 Unicode code points. Oversized
203
+ edits carry `truncated: true` and `truncated_fields` naming incomplete fields.
204
+ The agent must not use partial text or HTML as a complete replacement or invent
205
+ the rest. It must obtain the full edit from an authoritative source or ask you
206
+ for it before acknowledging the batch.
207
+
208
+ External writes to an HTML or Markdown source refresh its rendered baseline without
209
+ clearing unsent feedback. That preserves your edits; it does not automatically
210
+ resolve conflicts with the changed source. HTML autosaves keep their existing
211
+ behavior.
212
+
213
+ ### Upgrading from servers using protocol 14 or earlier
214
+
215
+ The updated CLI requires server protocol 15. An older server that is still
216
+ running is not silently reused or forcibly replaced. End active reviews and
217
+ shut down that specific old server (or let it exit when idle) before restarting
218
+ Doc Review. Keep the `.doc-review` state directory: pending batches and their
219
+ exact receipt IDs survive a controlled restart.
220
+
221
+ After installing the updated package, rerun `doc-review setup --global` for
222
+ personal skills and `doc-review setup` in projects with generated guidance.
223
+ Reload skills or start a fresh agent session afterward. A newer CLI paired with
224
+ older instructions is not a complete upgrade.
225
+
90
226
  ## What this skill lets you do
91
227
 
92
228
  - **Edit text directly and tweak basic formatting** (e.g., bold, italic).
@@ -113,7 +249,7 @@ Doc Review works well for editing AI-generated plans, updating landing pages, re
113
249
  - [`SKILL.md`](src/SKILL.md) teaches Claude Code, Codex, and other agents how to use Doc Review.
114
250
 
115
251
  Everything runs on your computer. Doc Review doesn’t require an account, cloud service, database, or API key.
116
- 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.
252
+ 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 leave the active feedback inventory; their round archive remains, and newer comments and corrections stay active.
117
253
 
118
254
  ## Upstream project
119
255
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@erdemtuna/doc-review",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
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",
@@ -53,6 +53,8 @@
53
53
  "lucide-static": "^1.34.0"
54
54
  },
55
55
  "dependencies": {
56
- "marked": "^18.0.7"
56
+ "diff": "^9.0.0",
57
+ "marked": "^18.0.7",
58
+ "parse5": "^8.0.1"
57
59
  }
58
60
  }
package/src/SKILL.md CHANGED
@@ -1,14 +1,36 @@
1
1
  ---
2
2
  name: doc-review
3
- description: Open an HTML file, Markdown file, or localhost page in a View-first browser review so the user can optionally edit, leave contextual comments, and send all feedback 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. It starts in View,
9
- where normal page controls work. They can switch to Edit, comment explicitly in either mode,
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
+ Native page controls work; supported self-contained HTML scripts run automatically.
23
+ They can switch to Edit, comment explicitly in either mode,
10
24
  and send the whole batch at once.
11
25
 
26
+ Plain HTML retains direct autosave. Scripted HTML edits are feedback-only:
27
+ apply them to the original source, never serialize the script-modified runtime
28
+ page over the file. Source changes do not require renewed approval.
29
+ Reload without scripts is a temporary recovery option, not permission to write
30
+ a scripted preview over its source. Reloads can wait for unresolved editing work.
31
+ Applications requiring external script dependencies should use localhost review;
32
+ automatic inline execution does not add support for application imports or workers.
33
+
12
34
  Markdown files open rendered. Their quotes and edits reference the rendered text,
13
35
  and the file itself is never touched — apply every change to the Markdown source,
14
36
  keeping its formatting syntax.
@@ -36,7 +58,8 @@ carried; newer comments and corrections survive.
36
58
 
37
59
  ## The loop
38
60
 
39
- 1. Write or update the HTML or Markdown file, or start the local page being reviewed.
61
+ 1. After the explicit review request, use the requested file or localhost route.
62
+ Create or update content, or start a local page, only as needed for that request.
40
63
  2. Open it for the user:
41
64
 
42
65
  ```sh
@@ -56,6 +79,12 @@ carried; newer comments and corrections survive.
56
79
  npx -y @erdemtuna/doc-review poll path/to/file.html --timeout 600
57
80
  ```
58
81
 
82
+ Without `--timeout`, the CLI stops after 12 hours. An explicit timeout covers
83
+ the entire operation, including server discovery, reconnects, and backoff.
84
+ Recoverable connection drops retry within that deadline; terminal errors
85
+ require action rather than another automatic poll. The 600-second command
86
+ above deliberately uses a shorter deadline.
87
+
59
88
  Keep this command in the foreground. Do not end your turn while it is waiting.
60
89
  If your shell returns a process or session handle, keep waiting on that handle
61
90
  until the command exits. If it prints `{"status":"timeout"}`, no feedback has
@@ -76,6 +105,17 @@ carried; newer comments and corrections survive.
76
105
  The response's `next_step` contains the complete acknowledgement command.
77
106
  Never acknowledge a different or guessed ID.
78
107
 
108
+ Acknowledgement means that you handled the feedback; it is separate from
109
+ browser result-capture readiness. Do not wait for history capture before
110
+ acknowledging completed work, and do not acknowledge merely because a page
111
+ looks stable. The browser captures round results automatically when possible.
112
+ Missing comparison data does not prevent the user from sending feedback.
113
+ Captured differences are observations, not proof that you alone authored them.
114
+ Content results may wait for the same identifiable tab as the baseline.
115
+ The user sees a return-to-tab prompt; do not automate tab clicking to force
116
+ capture. Source comparison remains independent. Unknown view identity is
117
+ labelled unverified, not asserted as a whole-application comparison.
118
+
79
119
  Repeat 3–4 until the user says they are done.
80
120
 
81
121
  Not sure whether feedback is already waiting — say, at the start of a new turn?
@@ -119,8 +159,13 @@ One batch covers every page the user visited, grouped by file or localhost URL.
119
159
 
120
160
  ## Rules
121
161
 
162
+ - Edits marked **`truncated`** contain incomplete fields listed in
163
+ `truncated_fields`. Each text/HTML field is limited to 200,000 Unicode code
164
+ points. Never apply a truncated field as a complete replacement or guess the
165
+ missing content. Obtain the complete edit from an authoritative source, or ask
166
+ the user for it. Do not acknowledge the batch until all feedback is handled.
122
167
  - **`edits` are changes the user already made.** `after` is their exact wording —
123
- carry it across verbatim and never revert it. If the HTML was generated from
168
+ unless marked truncated, carry it across verbatim and never revert it. If the HTML was generated from
124
169
  something else (MDX, Markdown, a template), apply `after` to the **source** too,
125
170
  or their fix disappears on the next build.
126
171
  - When `before_html`/`after_html` are present, the user changed formatting, not
@@ -148,8 +193,12 @@ One batch covers every page the user visited, grouped by file or localhost URL.
148
193
  - Find each comment by its `quote`; that exact string is in the file.
149
194
  - `kind: "element"` points at a whole block, so `quote` is its label, not body text.
150
195
  - Fix every page in `pages`, not just the first.
151
- - **Do not write a reply.** There is no chat. The user sees your work when the page
152
- reloads, which happens on its own the moment you save the file.
196
+ - **Do not write a reply.** There is no chat. The user sees the updated page and
197
+ can choose Changes for a fixed Send-to-result comparison. Reload may wait
198
+ for the user to handle unsaved review work. Live result capture may require
199
+ a contextual retry; never describe unavailable history as no changes.
200
+ - Review history uses local content/source snapshots, not screenshots or Git
201
+ commits. Do not create commits or alter repository state to populate history.
153
202
 
154
203
  ## Better edit labels (optional)
155
204
 
@@ -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();