@ansonlai/docx-redline-js 0.7.2 → 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/AGENTS.md +15 -10
- package/ARCHITECTURE.md +30 -10
- package/CHANGELOG.md +539 -431
- package/README.md +331 -309
- package/adapters/xml-adapter.js +84 -56
- package/core/sha256.js +120 -0
- package/dist/docx-redline-js.esm.js +8982 -693
- package/dist/docx-redline-js.esm.js.map +4 -4
- package/dist/docx-redline-js.esm.min.js +102 -89
- package/dist/docx-redline-js.esm.min.js.map +4 -4
- package/dist/docx-redline.bundle.cjs +28305 -0
- package/dist/docx-redline.bundle.cjs.map +7 -0
- package/dist/docx-redline.bundle.js +28296 -0
- package/dist/docx-redline.bundle.js.map +7 -0
- package/docs/AGENT_FAST_START.md +2 -1
- package/docs/AGENT_KNOWLEDGE_BASE.md +480 -393
- package/docs/SKILL_AUTHORING.md +5 -2
- package/docs/TESTING.md +2 -1
- package/docs/schemas/document-operations.schema.json +29 -0
- package/document/docx-document.d.ts +39 -0
- package/document/docx-document.js +1010 -0
- package/document/zip-archive.js +258 -0
- package/engine/reconstruction-writer.js +18 -0
- package/index.d.ts +598 -531
- package/index.js +349 -340
- package/node/cli-help.js +5 -5
- package/node/cli.js +760 -754
- package/node/docx-document.js +11 -355
- package/node/index.d.ts +1 -1
- package/node/zip-archive.js +28 -38
- package/package.json +14 -11
- package/scripts/build.mjs +40 -5
- package/scripts/generate-word-comment-edge-case-fixture.ps1 +58 -0
- package/scripts/generate-word-comment-thread-fixture.ps1 +36 -0
- package/scripts/generate-word-header-footer-fixture.ps1 +32 -0
- package/scripts/generate-word-multi-section-fixture.ps1 +27 -0
- package/scripts/generate-word-package-integrity-cases.mjs +141 -0
- package/scripts/generate-word-thread-state-fixtures.ps1 +50 -0
- package/scripts/word-com-package-integrity.ps1 +98 -0
- package/services/batch-operation-orchestrator.js +262 -259
- package/services/comment-replies.js +96 -95
- package/services/comment-thread-parts.js +280 -0
- package/services/document-inspection.js +366 -365
- package/services/document-operation-applier.js +230 -202
- package/services/document-operation-contract.js +17 -2
- package/services/error-recovery.js +6 -0
- package/services/headers-footers.js +206 -0
- package/services/operation-batch-compiler.js +1 -0
- package/services/operation-preflight.js +231 -218
- package/services/package-parts.js +80 -0
- package/services/revision-token.js +11 -7
- package/services/standalone-docx-plumbing.js +631 -569
- package/services/standalone-operation-runner.d.ts +19 -3
|
@@ -1,78 +1,81 @@
|
|
|
1
|
-
# Agent Knowledge Base
|
|
2
|
-
|
|
3
|
-
> Detailed reference for @ansonlai/docx-redline-js. All code and command paths
|
|
4
|
-
> are relative to the repository root. Start with `../AGENTS.md` for repository
|
|
5
|
-
> work or `AGENT_FAST_START.md` for ordinary document edits; open this file only
|
|
6
|
-
> when the quick guide points here or the task needs deeper behavior.
|
|
7
|
-
|
|
8
|
-
## Start Here: Repository Layout
|
|
9
|
-
|
|
10
|
-
This is one JavaScript package with three supported public surfaces. Choose the
|
|
11
|
-
surface before following imports:
|
|
12
|
-
|
|
13
|
-
| Use case | Public entry point | Primary implementation |
|
|
14
|
-
|---|---|---|
|
|
15
|
-
| Paragraph, range, list, table, comment, and OOXML transforms in any DOM-capable runtime | `index.js` (`@ansonlai/docx-redline-js`) | `engine/`, `pipeline/`, `core/`, and focused `services/` |
|
|
16
|
-
| Operations against complete `word/document.xml` strings | `services/standalone-operation-runner.js` (`@ansonlai/docx-redline-js/standalone-runner`) | `services/document-operation-*.js` and `services/batch-operation-orchestrator.js` |
|
|
17
|
-
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
`
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
|
64
|
-
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
1
|
+
# Agent Knowledge Base
|
|
2
|
+
|
|
3
|
+
> Detailed reference for @ansonlai/docx-redline-js. All code and command paths
|
|
4
|
+
> are relative to the repository root. Start with `../AGENTS.md` for repository
|
|
5
|
+
> work or `AGENT_FAST_START.md` for ordinary document edits; open this file only
|
|
6
|
+
> when the quick guide points here or the task needs deeper behavior.
|
|
7
|
+
|
|
8
|
+
## Start Here: Repository Layout
|
|
9
|
+
|
|
10
|
+
This is one JavaScript package with three supported public surfaces. Choose the
|
|
11
|
+
surface before following imports:
|
|
12
|
+
|
|
13
|
+
| Use case | Public entry point | Primary implementation |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| Paragraph, range, list, table, comment, and OOXML transforms in any DOM-capable runtime | `index.js` (`@ansonlai/docx-redline-js`) | `engine/`, `pipeline/`, `core/`, and focused `services/` |
|
|
16
|
+
| Operations against complete `word/document.xml` strings | `services/standalone-operation-runner.js` (`@ansonlai/docx-redline-js/standalone-runner`) | `services/document-operation-*.js` and `services/batch-operation-orchestrator.js` |
|
|
17
|
+
| Universal complete `.docx` packages (`Uint8Array`) across Node, browsers, edge, and sandboxes | `index.js` (`@ansonlai/docx-redline-js`) or `document/docx-document.js` | `document/docx-document.js`, `document/zip-archive.js`, `core/sha256.js` |
|
|
18
|
+
| Zero-dependency standalone sandbox bundles (n8n, Cloudflare Workers, edge) | `dist/docx-redline.bundle.js` / `.cjs` (`@ansonlai/docx-redline-js/bundle`) | Standalone inlined bundle (no Node built-ins) |
|
|
19
|
+
| Complete `.docx` buffers and the CLI in Node.js (backward compatibility) | `node/index.js` (`@ansonlai/docx-redline-js/node`) and `bin/docx-redline.js` | `node/docx-document.js`, `node/zip-archive.js`, and package-plumbing services |
|
|
20
|
+
|
|
21
|
+
The source tree is layered as follows:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
index.js root, host-independent public exports & universal facade
|
|
25
|
+
adapters/ XML (auto DOMParser fallback), config, and logging adapters
|
|
26
|
+
core/ shared OOXML primitives, text views, targeting, SHA-256, validation
|
|
27
|
+
document/ universal Uint8Array DOCX facade & fflate ZIP container
|
|
28
|
+
pipeline/ ingestion, diffing, markdown, lists, and serialization stages
|
|
29
|
+
engine/ paragraph/range reconciliation and surgical/reconstruction modes
|
|
30
|
+
orchestration/ route planning and structural list operation conversion
|
|
31
|
+
services/ document operations, comments, receipts, package artifacts
|
|
32
|
+
node/ Node CLI and backward-compatibility facade re-exports
|
|
33
|
+
bin/ CLI launcher; behavior belongs in node/ or services/
|
|
34
|
+
tests/*.mjs directly runnable suites discovered by scripts/run-tests.mjs
|
|
35
|
+
tests/helpers/ shared test utilities; not standalone suites
|
|
36
|
+
tests/fixtures/ checked-in synthetic/golden inputs and expected outputs
|
|
37
|
+
scripts/ build, fixture generation, benchmarks, and Word automation
|
|
38
|
+
docs/ schemas, testing guidance, plans, and generated reports
|
|
39
|
+
dist/ generated universal ESM and zero-dependency sandbox bundles
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`AGENTS.md` is the committed repository-wide agent guide. `.agent/` is ignored
|
|
43
|
+
local-only agent configuration and must not be treated as package source or as
|
|
44
|
+
instructions that will exist in another clone or in CI.
|
|
45
|
+
|
|
46
|
+
### Route Changes Without Exploring Everything
|
|
47
|
+
|
|
48
|
+
1. Identify the public surface in the table above.
|
|
49
|
+
2. Read its entry point, then follow only the symbol being changed.
|
|
50
|
+
3. Start from the closest existing test named for the behavior. Do not scan all
|
|
51
|
+
of `tests/` or unpack every fixture to understand one code path.
|
|
52
|
+
4. Use `rg` for symbols and filenames. Consult `ARCHITECTURE.md` for ownership
|
|
53
|
+
and `docs/TESTING.md` for test-lane selection before inventing a new route,
|
|
54
|
+
helper, test harness, or fixture generator.
|
|
55
|
+
5. Keep dependency direction inward: shared implementation modules must not
|
|
56
|
+
import `index.js`, and host-independent code must not import `node/`.
|
|
57
|
+
6. Do not inspect `dist/`, a vendored CLI bundle, or an installed plugin bundle
|
|
58
|
+
to discover public behavior. Use this file, `README.md`,
|
|
59
|
+
`docs/schemas/document-operations.schema.json`, and the unbundled source.
|
|
60
|
+
|
|
61
|
+
### Task-to-Module Shortcuts
|
|
62
|
+
|
|
63
|
+
| Task | Start here | Common focused tests |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| Basic text redline, formatting, or route choice | `engine/oxml-engine.js`, then the selected `engine/*-mode.js` | `tests/engine_reliability_tests.mjs`, `tests/formatting_tests.mjs` |
|
|
66
|
+
| Run splitting or edits inside revisions | `engine/surgical-mode.js`, `engine/surgical-run-splitting.js`, `engine/surgical-diff-application.js` | `tests/revision_split_injection_tests.mjs`, `tests/cross_author_slicing_synthetic_tests.mjs` |
|
|
67
|
+
| Accepted/rejected/current text or paragraph targeting | `core/paragraph-text.js`, `core/paragraph-targeting.js` | `tests/canonical_paragraph_text_tests.mjs`, `tests/revision_view_target_tests.mjs` |
|
|
68
|
+
| Lists, numbering, or markdown structure | `pipeline/list-generation.js`, `pipeline/structured-content.js`, `services/numbering-helpers.js` | `tests/list_tests.mjs`, `tests/list_replacement_structure_tests.mjs`, `tests/structured_content_planner_tests.mjs` |
|
|
69
|
+
| Tables | `engine/table-mode.js`, `core/table-targeting.js`, `services/table-reconciliation.js` | `tests/table_tests.mjs`, `tests/table_targeting_and_format_flags.mjs` |
|
|
70
|
+
| Comments and replies | `services/comment-engine.js`, `services/comment-replies.js`, `services/comment-package.js` | `tests/comment_tests.mjs`, `tests/comment_reply_tests.mjs` |
|
|
71
|
+
| Full-document operation scheduling or rollback | `services/document-operation-applier.js`, `services/batch-operation-orchestrator.js`, `services/document-operation-session.js` | `tests/standalone_operation_runner_tests.mjs`, `tests/performance_phase1_session_tests.mjs` |
|
|
72
|
+
| DOCX ZIP wiring or CLI behavior | `node/docx-document.js`, `node/zip-archive.js`, `node/cli.js` | `tests/docx_package_facade_tests.mjs`, `tests/node_zip_archive_tests.mjs`, `tests/agent_cli_tests.mjs` |
|
|
73
|
+
| Validation, receipts, and lifecycle oracles | `core/redline-validation.js`, `services/receipt-collector.js`, `services/revision-comment-management.js` | `tests/redline_validation_tests.mjs`, `tests/mutation_receipt_tests.mjs`, `tests/roundtrip_oracle_tests.mjs` |
|
|
74
|
+
|
|
75
|
+
Run one focused suite with `node tests/<name>.mjs`. Run `npm test` only when the
|
|
76
|
+
change crosses several subsystems or before release-level handoff. The Word COM,
|
|
77
|
+
visual, corpus, coverage, and fixture-export commands are separate lanes; use
|
|
78
|
+
them only when `docs/TESTING.md` says that lane proves the behavior in question.
|
|
76
79
|
|
|
77
80
|
## What This Package Does
|
|
78
81
|
|
|
@@ -97,21 +100,44 @@ back to `word/document.xml`.
|
|
|
97
100
|
## Entry Point
|
|
98
101
|
|
|
99
102
|
```js
|
|
100
|
-
import { applyRedlineToOxml, configureXmlProvider } from '@ansonlai/docx-redline-js';
|
|
103
|
+
import { applyRedlineToOxml, openDocx, configureXmlProvider } from '@ansonlai/docx-redline-js';
|
|
101
104
|
```
|
|
102
105
|
|
|
103
|
-
`index.js` is the primary host-independent entry point
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
`index.js` is the primary host-independent entry point, exposing both paragraph-level
|
|
107
|
+
reconciliation (`applyRedlineToOxml`) and universal complete-document operations
|
|
108
|
+
(`openDocx`, `DocxDocument`, `computePackageRevisionToken`).
|
|
109
|
+
|
|
110
|
+
## XML Provider Setup (Zero Configuration)
|
|
111
|
+
|
|
112
|
+
Zero configuration is required out of the box. The library automatically:
|
|
113
|
+
1. Uses `globalThis.DOMParser` and `globalThis.XMLSerializer` when available in standard web runtimes.
|
|
114
|
+
2. Transparently falls back to pure-JS `@xmldom/xmldom` in Node.js, edge workers, and sandboxes.
|
|
106
115
|
|
|
107
|
-
|
|
116
|
+
Consumers can optionally override these providers if a custom DOM implementation is desired:
|
|
108
117
|
|
|
109
118
|
```js
|
|
110
|
-
import {
|
|
111
|
-
configureXmlProvider({ DOMParser, XMLSerializer });
|
|
119
|
+
import { configureXmlProvider } from '@ansonlai/docx-redline-js';
|
|
120
|
+
configureXmlProvider({ DOMParser: MyDOMParser, XMLSerializer: MyXMLSerializer });
|
|
112
121
|
```
|
|
113
122
|
|
|
114
|
-
|
|
123
|
+
## Universal Complete-DOCX Operations (`openDocx`)
|
|
124
|
+
|
|
125
|
+
Complete `.docx` documents can be opened directly from a `Uint8Array` (or Node `Buffer`) across all environments without Node built-in dependencies:
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
import { openDocx } from '@ansonlai/docx-redline-js';
|
|
129
|
+
|
|
130
|
+
const doc = openDocx(docxUint8Array);
|
|
131
|
+
const inspection = doc.inspect();
|
|
132
|
+
const result = await doc.applyOperations([
|
|
133
|
+
{ type: 'replace', target: { paragraphId: '00000001' }, modified: 'Updated clause text' }
|
|
134
|
+
], { author: 'AI Editor' });
|
|
135
|
+
|
|
136
|
+
if (result.written) {
|
|
137
|
+
const outputBytes = doc.toUint8Array(); // canonical Uint8Array
|
|
138
|
+
// In Node.js: const outputBuf = doc.toBuffer();
|
|
139
|
+
}
|
|
140
|
+
```
|
|
115
141
|
|
|
116
142
|
## Key APIs by Use Case
|
|
117
143
|
|
|
@@ -217,57 +243,57 @@ const result = await applyOperationsToDocumentXml(documentXml, operations, 'Agen
|
|
|
217
243
|
The operation runner uses these field names:
|
|
218
244
|
|
|
219
245
|
```js
|
|
220
|
-
const operations = [
|
|
246
|
+
const operations = [
|
|
221
247
|
{ type: 'redline', target: 'Old paragraph text', modified: 'New paragraph text', targetRef: 12 },
|
|
222
248
|
{ type: 'comment', target: 'Paragraph text', textToComment: 'anchor text', commentContent: 'Comment body', targetRef: 18 },
|
|
223
249
|
{ type: 'highlight', target: 'Paragraph text', textToHighlight: 'anchor text', color: 'yellow', targetRef: 24 }
|
|
224
|
-
];
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
To counterpropose text for a paragraph wholly deleted by another reviewer,
|
|
228
|
-
use explicit restoration intent. A normal `redline` remains fail-closed with
|
|
229
|
-
`FOREIGN_PARAGRAPH_MARK_DELETION`:
|
|
230
|
-
|
|
231
|
-
```js
|
|
232
|
-
const restoration = {
|
|
233
|
-
type: 'restore',
|
|
234
|
-
target: { paragraphId: '1A2B3C4D', exactText: 'Original deleted paragraph text.' },
|
|
235
|
-
modified: 'Restored or adjusted paragraph text.',
|
|
236
|
-
author: 'Editor'
|
|
237
|
-
};
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
`restore` targets default to `revisionView: 'rejected'`, because that is where
|
|
241
|
-
the deleted source text is visible. Inspection/extraction fingerprints are
|
|
242
|
-
view-scoped and returned with a matching `revisionView`; copy the fingerprint,
|
|
243
|
-
exact text, and view together. Set `revisionView: 'accepted'` explicitly only
|
|
244
|
-
when intentionally using an accepted-view descriptor.
|
|
245
|
-
|
|
246
|
-
For a contiguous range, provide `targetEnd`/`targetEndRef` and one string per
|
|
247
|
-
source paragraph in `modified`. Restoration always uses tracked changes,
|
|
248
|
-
preserves the deleted source paragraph, and inserts the counterproposal after
|
|
249
|
-
the complete deleted source block with a fresh paragraph ID. Unchanged
|
|
250
|
-
pre-existing validation defects remain baseline diagnostics; a restore fails
|
|
251
|
-
with `GENERATED_OOXML_INVALID` only when it introduces a new validation error.
|
|
252
|
-
|
|
253
|
-
To insert run-level text inside content visible only in the rejected view, use
|
|
254
|
-
an explicit rejected-view `insert` operation:
|
|
255
|
-
|
|
256
|
-
```js
|
|
257
|
-
const insertion = {
|
|
258
|
-
type: 'insert',
|
|
259
|
-
target: { paragraphId: '1A2B3C4D', revisionView: 'rejected' },
|
|
260
|
-
anchor: { exactText: 'must pay', occurrence: 1, offset: 5 },
|
|
261
|
-
modified: '[clarification] ',
|
|
262
|
-
author: 'Editor',
|
|
263
|
-
existingRevisions: 'slice-cross-author'
|
|
264
|
-
};
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
The anchor offset is relative to `anchor.exactText`. Repeated anchors require an
|
|
268
|
-
explicit `occurrence`. The engine preserves the foreign deletion as sibling
|
|
269
|
-
`w:del` carriers around a top-level `w:ins`; unsupported comments, bookmarks,
|
|
270
|
-
fields, hyperlinks, moves, or non-text split boundaries fail closed.
|
|
250
|
+
];
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
To counterpropose text for a paragraph wholly deleted by another reviewer,
|
|
254
|
+
use explicit restoration intent. A normal `redline` remains fail-closed with
|
|
255
|
+
`FOREIGN_PARAGRAPH_MARK_DELETION`:
|
|
256
|
+
|
|
257
|
+
```js
|
|
258
|
+
const restoration = {
|
|
259
|
+
type: 'restore',
|
|
260
|
+
target: { paragraphId: '1A2B3C4D', exactText: 'Original deleted paragraph text.' },
|
|
261
|
+
modified: 'Restored or adjusted paragraph text.',
|
|
262
|
+
author: 'Editor'
|
|
263
|
+
};
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
`restore` targets default to `revisionView: 'rejected'`, because that is where
|
|
267
|
+
the deleted source text is visible. Inspection/extraction fingerprints are
|
|
268
|
+
view-scoped and returned with a matching `revisionView`; copy the fingerprint,
|
|
269
|
+
exact text, and view together. Set `revisionView: 'accepted'` explicitly only
|
|
270
|
+
when intentionally using an accepted-view descriptor.
|
|
271
|
+
|
|
272
|
+
For a contiguous range, provide `targetEnd`/`targetEndRef` and one string per
|
|
273
|
+
source paragraph in `modified`. Restoration always uses tracked changes,
|
|
274
|
+
preserves the deleted source paragraph, and inserts the counterproposal after
|
|
275
|
+
the complete deleted source block with a fresh paragraph ID. Unchanged
|
|
276
|
+
pre-existing validation defects remain baseline diagnostics; a restore fails
|
|
277
|
+
with `GENERATED_OOXML_INVALID` only when it introduces a new validation error.
|
|
278
|
+
|
|
279
|
+
To insert run-level text inside content visible only in the rejected view, use
|
|
280
|
+
an explicit rejected-view `insert` operation:
|
|
281
|
+
|
|
282
|
+
```js
|
|
283
|
+
const insertion = {
|
|
284
|
+
type: 'insert',
|
|
285
|
+
target: { paragraphId: '1A2B3C4D', revisionView: 'rejected' },
|
|
286
|
+
anchor: { exactText: 'must pay', occurrence: 1, offset: 5 },
|
|
287
|
+
modified: '[clarification] ',
|
|
288
|
+
author: 'Editor',
|
|
289
|
+
existingRevisions: 'slice-cross-author'
|
|
290
|
+
};
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
The anchor offset is relative to `anchor.exactText`. Repeated anchors require an
|
|
294
|
+
explicit `occurrence`. The engine preserves the foreign deletion as sibling
|
|
295
|
+
`w:del` carriers around a top-level `w:ins`; unsupported comments, bookmarks,
|
|
296
|
+
fields, hyperlinks, moves, or non-text split boundaries fail closed.
|
|
271
297
|
|
|
272
298
|
`targetRef` is an optional 1-based paragraph reference used to disambiguate
|
|
273
299
|
duplicate text. An operation-level `author` overrides the batch author; batch
|
|
@@ -296,9 +322,9 @@ read-only inspection of an agent-generated batch before applying it. Preflight i
|
|
|
296
322
|
read-only and strict by default: duplicate exact text returns `AMBIGUOUS_TARGET`,
|
|
297
323
|
approximate text is not selected, and the result reports candidate targets,
|
|
298
324
|
missing anchors, existing revisions, authors, required artifacts, and
|
|
299
|
-
same-paragraph conflicts. Direct execution is progressive by default
|
|
300
|
-
(`atomic: false`); pass `{ atomic: true }` when the batch must roll back as a
|
|
301
|
-
unit. When permissive resolution
|
|
325
|
+
same-paragraph conflicts. Direct execution is progressive by default
|
|
326
|
+
(`atomic: false`); pass `{ atomic: true }` when the batch must roll back as a
|
|
327
|
+
unit. When permissive resolution
|
|
302
328
|
encounters duplicate candidate paragraphs, it emits an
|
|
303
329
|
`AMBIGUOUS_TARGET_HEURISTIC_USED` warning; migrate to `{ strictTargets: true }`
|
|
304
330
|
with strict descriptors (`paragraphId`, `index`, `occurrence`, or `fingerprint`)
|
|
@@ -349,7 +375,7 @@ const inspection = inspectDocumentParts({ documentXml, commentsXml, numberingXml
|
|
|
349
375
|
Reuse `exactText` plus `paragraphId` or `fingerprint` in an operation. Computed
|
|
350
376
|
list labels and excerpts are for display, not replacements for exact targets.
|
|
351
377
|
|
|
352
|
-
### Safely edit a complete DOCX in Node
|
|
378
|
+
### Safely edit a complete DOCX in Node
|
|
353
379
|
|
|
354
380
|
```js
|
|
355
381
|
import { openDocx } from '@ansonlai/docx-redline-js/node';
|
|
@@ -361,197 +387,197 @@ if (!result.written) throw new Error(result.error?.message || 'No output written
|
|
|
361
387
|
const outputBuffer = result.toBuffer();
|
|
362
388
|
```
|
|
363
389
|
|
|
364
|
-
This facade defaults to strict targets, allocates package-safe comment IDs,
|
|
365
|
-
merges numbering, updates relationships/content types, and rolls back to the
|
|
366
|
-
original buffer when an atomic transaction fails.
|
|
367
|
-
|
|
368
|
-
### Designing a thin agent wrapper
|
|
369
|
-
|
|
370
|
-
New wrappers should delegate at a package boundary instead of copying internal
|
|
371
|
-
algorithms:
|
|
372
|
-
|
|
373
|
-
- Shell/file wrappers invoke the `docx-redline` CLI and preserve its JSON stdout
|
|
374
|
-
and exit code.
|
|
375
|
-
- Node byte-oriented wrappers use `openDocx`, `inspect`, `applyOperations`,
|
|
376
|
-
`resolveRevisions`, `deleteComments`, and `toBuffer`.
|
|
377
|
-
- XML-only hosts use the standalone runner and remain responsible for package
|
|
378
|
-
artifacts returned beside `documentXml`.
|
|
379
|
-
- Paragraph/range hosts use root exports and remain responsible for deciding how
|
|
380
|
-
the returned OOXML is inserted into a larger document.
|
|
381
|
-
|
|
382
|
-
A wrapper that vendors the CLI should perform a startup compatibility handshake
|
|
383
|
-
with `docx-redline version`. Pin the minimum CLI `contractVersion` and only the
|
|
384
|
-
capabilities that the wrapper's workflow actually requires. If the runtime is
|
|
385
|
-
too old, fail closed with an upgrade instruction; do not inspect the bundled
|
|
386
|
-
implementation or fall back to direct ZIP/XML mutation.
|
|
387
|
-
|
|
388
|
-
A wrapper may choose product defaults for author, atomic mode, output naming,
|
|
389
|
-
and accepted operation subsets. It must preserve strict targeting, exact text,
|
|
390
|
-
structured errors, per-operation results, receipts, warnings, validation, and
|
|
391
|
-
rollback behavior. It must not infer success from `hasChanges` alone or turn a
|
|
392
|
-
partial progressive result into an unconditional success.
|
|
393
|
-
|
|
394
|
-
Document those choices as wrapper policy and pass them explicitly. In
|
|
395
|
-
particular, do not describe a wrapper's preferred revision or atomicity policy
|
|
396
|
-
as though it were the underlying CLI or facade default.
|
|
397
|
-
|
|
398
|
-
Target handles are scoped to the exact package version that produced them. A
|
|
399
|
-
Node wrapper should return the package-scoped token from
|
|
400
|
-
`document.getRevisionToken()` with every inspection and pass it back as
|
|
401
|
-
`expectedRevision` when applying the planned operations. Do not substitute the
|
|
402
|
-
document-parts token included in `document.inspect()`; that token has a different
|
|
403
|
-
scope and the Node facade rejects it. A shell wrapper should ensure extraction
|
|
404
|
-
and application use the same unchanged path, and re-extract after switching to a
|
|
405
|
-
derived working copy.
|
|
406
|
-
|
|
407
|
-
For agent-facing function tools, prefer separate inspection, application, and
|
|
408
|
-
review-resolution tools. Describe each tool's use case, required inputs, side
|
|
409
|
-
effects, retry safety, success criteria, and common error codes. Convenience
|
|
410
|
-
tools should build operations defined by
|
|
411
|
-
`docs/schemas/document-operations.schema.json` and delegate to the same apply
|
|
412
|
-
path rather than implementing custom mutations.
|
|
413
|
-
|
|
414
|
-
For a `restore_deleted_paragraph` convenience tool, force inspection to
|
|
415
|
-
`revisionView: 'rejected'`, copy the exact target descriptor from that view, and
|
|
416
|
-
emit a canonical `restore` operation with the same explicit revision view.
|
|
417
|
-
Wholly foreign-deleted paragraphs appear empty in accepted/current inspection;
|
|
418
|
-
this is expected, not evidence that the paragraph is untargetable. Never reuse a
|
|
419
|
-
restore descriptor from a different source or earlier working-copy version. On
|
|
420
|
-
`TARGET_TEXT_MISMATCH`, re-extract from the exact package being applied rather
|
|
421
|
-
than inspecting implementation bundles or retrying the same operation.
|
|
422
|
-
|
|
423
|
-
### Agent Document Workflow (CLI)
|
|
424
|
-
|
|
425
|
-
Use the `docx-redline` CLI for complete `.docx` files. It emits JSON on stdout,
|
|
426
|
-
keeps exact text intact, and never overwrites the source unless `--in-place` is
|
|
427
|
-
explicitly supplied.
|
|
428
|
-
|
|
429
|
-
#### Operation Model: Choose by Output Shape
|
|
430
|
-
|
|
431
|
-
Every text-bearing operation targets existing content. Its `modified` field is
|
|
432
|
-
the complete desired accepted-view content for that target, not merely the new
|
|
433
|
-
fragment to insert. Do not choose a type from its English name alone.
|
|
434
|
-
|
|
435
|
-
| Requested result | Operation shape |
|
|
436
|
-
|---|---|
|
|
437
|
-
| Change text within one paragraph or replace its content | `{ type: 'redline', target, modified }` (`replace` is a compatibility alias) |
|
|
438
|
-
| Delete a whole paragraph | `{ type: 'delete', target }` (normalized to a redline with `modified: ''`) |
|
|
439
|
-
| Change native list structure | `{ type: 'list-change', target, modified: '<complete Markdown list>' }` |
|
|
440
|
-
| Reconcile a Word table | `{ type: 'table-reconciliation', target, modified: '<complete Markdown table>' }` |
|
|
441
|
-
| Comment or highlight existing text | `comment` with `commentContent`, or `highlight` with `textToHighlight` |
|
|
442
|
-
| Change character or paragraph formatting | `character-format`/`format` with `textToFormat` and `properties`, or `paragraph-format` with `properties` |
|
|
443
|
-
| Counterpropose a paragraph wholly deleted by another author | `restore` with a rejected-view target |
|
|
444
|
-
| Insert text inside another author's rejected-view content | `insert` with `target.revisionView: 'rejected'`, an exact `anchor`, and `existingRevisions: 'slice-cross-author'` |
|
|
445
|
-
|
|
446
|
-
`insert`, `list-change`, `table-reconciliation`, `replace`, and text-bearing
|
|
447
|
-
`format` are accepted compatibility types, but they normally normalize to the
|
|
448
|
-
same redline operation path. In particular, ordinary `{ type: 'insert' }` does
|
|
449
|
-
not mean “create a new sibling paragraph”; without a rejected-view target and
|
|
450
|
-
anchor, `modified` is still interpreted as the complete replacement text.
|
|
451
|
-
|
|
452
|
-
To append a native sibling item after an existing list item, prefer an explicit
|
|
453
|
-
`list-change` whose `modified` value contains the complete affected list block:
|
|
454
|
-
|
|
455
|
-
```json
|
|
456
|
-
{
|
|
457
|
-
"type": "list-change",
|
|
458
|
-
"target": {
|
|
459
|
-
"exactText": "Review the report.",
|
|
460
|
-
"paragraphId": "1A2B3C4D"
|
|
461
|
-
},
|
|
462
|
-
"modified": "1. Review the report.\n2. Record the approval decision."
|
|
463
|
-
}
|
|
464
|
-
```
|
|
465
|
-
|
|
466
|
-
The Markdown markers describe structure; visible labels are generated from the
|
|
467
|
-
document's numbering. Do not put `m)` or another computed label into target text.
|
|
468
|
-
For a single adjacent item in an existing list, the runner also accepts a
|
|
469
|
-
one-line `redline` where `modified` is the exact current item followed by the
|
|
470
|
-
new item's unnumbered text. Preserve the current item verbatim, omit the new
|
|
471
|
-
label, and make the new item at least six words so the adjacency form is
|
|
472
|
-
unambiguous. Use the explicit Markdown list form when adding multiple items,
|
|
473
|
-
nesting, or changing levels.
|
|
474
|
-
|
|
475
|
-
The canonical machine-readable contract is
|
|
476
|
-
`docs/schemas/document-operations.schema.json`. Read that schema or the examples
|
|
477
|
-
here instead of grepping bundled implementation code.
|
|
478
|
-
|
|
479
|
-
#### Standard Workflow (Extract Then Apply)
|
|
480
|
-
|
|
481
|
-
Use this for ordinary edits. `apply` validates the resulting package and
|
|
482
|
-
revision markup internally before writing, so do not add `preflight` or baseline
|
|
483
|
-
`validate` merely for reassurance.
|
|
484
|
-
|
|
485
|
-
1. Run one focused `extract`. Add `--around 3` when surrounding context is
|
|
486
|
-
needed. Retain `exactText` for drafting and target with `paragraphId` plus
|
|
487
|
-
`fingerprint`.
|
|
488
|
-
2. Build one final operation per target. Use complete `modified` text for broad
|
|
489
|
-
semantic revisions or localized `replacements` for small literal changes in
|
|
490
|
-
an inspected target. Consolidate multiple changes to one paragraph.
|
|
491
|
-
3. Run `apply` once per stable batch. Independent strong targets bind against
|
|
492
|
-
the batch-start document and do not need bottom-up sorting. Use captures only
|
|
493
|
-
for intentional created-content dependencies.
|
|
494
|
-
4. Require `completion: true` and a non-null `outputPath`. The CLI derives
|
|
495
|
-
completion from write status, operation outcomes, commit disposition, and
|
|
496
|
-
localized accepted-view verification.
|
|
497
|
-
5. Run a focused post-apply extraction only when placement or structural output
|
|
498
|
-
needs confirmation.
|
|
499
|
-
|
|
500
|
-
If `apply` returns an error, follow the recovery envelope below and make one
|
|
501
|
-
cause-specific correction. Do not probe behavior with disposable mutations or
|
|
502
|
-
read a vendor bundle.
|
|
503
|
-
|
|
504
|
-
```bash
|
|
505
|
-
# 1. Focused contextual discovery
|
|
506
|
-
docx-redline extract contract.docx --search "termination" --around 3
|
|
507
|
-
|
|
508
|
-
# 2. Serializer-backed apply against the inspected target
|
|
509
|
-
node emit-operations.mjs | docx-redline apply contract.docx --operations - --profile agent --compact --output reviewed.docx
|
|
510
|
-
|
|
511
|
-
# Optional clean edit without tracked changes
|
|
512
|
-
docx-redline apply contract.docx --target "Typo fix" --modified "Fixed typo" --no-redlines --output clean.docx
|
|
513
|
-
```
|
|
390
|
+
This facade defaults to strict targets, allocates package-safe comment IDs,
|
|
391
|
+
merges numbering, updates relationships/content types, and rolls back to the
|
|
392
|
+
original buffer when an atomic transaction fails.
|
|
393
|
+
|
|
394
|
+
### Designing a thin agent wrapper
|
|
395
|
+
|
|
396
|
+
New wrappers should delegate at a package boundary instead of copying internal
|
|
397
|
+
algorithms:
|
|
398
|
+
|
|
399
|
+
- Shell/file wrappers invoke the `docx-redline` CLI and preserve its JSON stdout
|
|
400
|
+
and exit code.
|
|
401
|
+
- Node byte-oriented wrappers use `openDocx`, `inspect`, `applyOperations`,
|
|
402
|
+
`resolveRevisions`, `deleteComments`, `resolveComment`, and `toBuffer`.
|
|
403
|
+
- XML-only hosts use the standalone runner and remain responsible for package
|
|
404
|
+
artifacts returned beside `documentXml`.
|
|
405
|
+
- Paragraph/range hosts use root exports and remain responsible for deciding how
|
|
406
|
+
the returned OOXML is inserted into a larger document.
|
|
407
|
+
|
|
408
|
+
A wrapper that vendors the CLI should perform a startup compatibility handshake
|
|
409
|
+
with `docx-redline version`. Pin the minimum CLI `contractVersion` and only the
|
|
410
|
+
capabilities that the wrapper's workflow actually requires. If the runtime is
|
|
411
|
+
too old, fail closed with an upgrade instruction; do not inspect the bundled
|
|
412
|
+
implementation or fall back to direct ZIP/XML mutation.
|
|
413
|
+
|
|
414
|
+
A wrapper may choose product defaults for author, atomic mode, output naming,
|
|
415
|
+
and accepted operation subsets. It must preserve strict targeting, exact text,
|
|
416
|
+
structured errors, per-operation results, receipts, warnings, validation, and
|
|
417
|
+
rollback behavior. It must not infer success from `hasChanges` alone or turn a
|
|
418
|
+
partial progressive result into an unconditional success.
|
|
419
|
+
|
|
420
|
+
Document those choices as wrapper policy and pass them explicitly. In
|
|
421
|
+
particular, do not describe a wrapper's preferred revision or atomicity policy
|
|
422
|
+
as though it were the underlying CLI or facade default.
|
|
423
|
+
|
|
424
|
+
Target handles are scoped to the exact package version that produced them. A
|
|
425
|
+
Node wrapper should return the package-scoped token from
|
|
426
|
+
`document.getRevisionToken()` with every inspection and pass it back as
|
|
427
|
+
`expectedRevision` when applying the planned operations. Do not substitute the
|
|
428
|
+
document-parts token included in `document.inspect()`; that token has a different
|
|
429
|
+
scope and the Node facade rejects it. A shell wrapper should ensure extraction
|
|
430
|
+
and application use the same unchanged path, and re-extract after switching to a
|
|
431
|
+
derived working copy.
|
|
432
|
+
|
|
433
|
+
For agent-facing function tools, prefer separate inspection, application, and
|
|
434
|
+
review-resolution tools. Describe each tool's use case, required inputs, side
|
|
435
|
+
effects, retry safety, success criteria, and common error codes. Convenience
|
|
436
|
+
tools should build operations defined by
|
|
437
|
+
`docs/schemas/document-operations.schema.json` and delegate to the same apply
|
|
438
|
+
path rather than implementing custom mutations.
|
|
439
|
+
|
|
440
|
+
For a `restore_deleted_paragraph` convenience tool, force inspection to
|
|
441
|
+
`revisionView: 'rejected'`, copy the exact target descriptor from that view, and
|
|
442
|
+
emit a canonical `restore` operation with the same explicit revision view.
|
|
443
|
+
Wholly foreign-deleted paragraphs appear empty in accepted/current inspection;
|
|
444
|
+
this is expected, not evidence that the paragraph is untargetable. Never reuse a
|
|
445
|
+
restore descriptor from a different source or earlier working-copy version. On
|
|
446
|
+
`TARGET_TEXT_MISMATCH`, re-extract from the exact package being applied rather
|
|
447
|
+
than inspecting implementation bundles or retrying the same operation.
|
|
448
|
+
|
|
449
|
+
### Agent Document Workflow (CLI)
|
|
450
|
+
|
|
451
|
+
Use the `docx-redline` CLI for complete `.docx` files. It emits JSON on stdout,
|
|
452
|
+
keeps exact text intact, and never overwrites the source unless `--in-place` is
|
|
453
|
+
explicitly supplied.
|
|
454
|
+
|
|
455
|
+
#### Operation Model: Choose by Output Shape
|
|
456
|
+
|
|
457
|
+
Every text-bearing operation targets existing content. Its `modified` field is
|
|
458
|
+
the complete desired accepted-view content for that target, not merely the new
|
|
459
|
+
fragment to insert. Do not choose a type from its English name alone.
|
|
460
|
+
|
|
461
|
+
| Requested result | Operation shape |
|
|
462
|
+
|---|---|
|
|
463
|
+
| Change text within one paragraph or replace its content | `{ type: 'redline', target, modified }` (`replace` is a compatibility alias) |
|
|
464
|
+
| Delete a whole paragraph | `{ type: 'delete', target }` (normalized to a redline with `modified: ''`) |
|
|
465
|
+
| Change native list structure | `{ type: 'list-change', target, modified: '<complete Markdown list>' }` |
|
|
466
|
+
| Reconcile a Word table | `{ type: 'table-reconciliation', target, modified: '<complete Markdown table>' }` |
|
|
467
|
+
| Comment or highlight existing text | `comment` with `commentContent`, or `highlight` with `textToHighlight` |
|
|
468
|
+
| Change character or paragraph formatting | `character-format`/`format` with `textToFormat` and `properties`, or `paragraph-format` with `properties` |
|
|
469
|
+
| Counterpropose a paragraph wholly deleted by another author | `restore` with a rejected-view target |
|
|
470
|
+
| Insert text inside another author's rejected-view content | `insert` with `target.revisionView: 'rejected'`, an exact `anchor`, and `existingRevisions: 'slice-cross-author'` |
|
|
471
|
+
|
|
472
|
+
`insert`, `list-change`, `table-reconciliation`, `replace`, and text-bearing
|
|
473
|
+
`format` are accepted compatibility types, but they normally normalize to the
|
|
474
|
+
same redline operation path. In particular, ordinary `{ type: 'insert' }` does
|
|
475
|
+
not mean “create a new sibling paragraph”; without a rejected-view target and
|
|
476
|
+
anchor, `modified` is still interpreted as the complete replacement text.
|
|
477
|
+
|
|
478
|
+
To append a native sibling item after an existing list item, prefer an explicit
|
|
479
|
+
`list-change` whose `modified` value contains the complete affected list block:
|
|
480
|
+
|
|
481
|
+
```json
|
|
482
|
+
{
|
|
483
|
+
"type": "list-change",
|
|
484
|
+
"target": {
|
|
485
|
+
"exactText": "Review the report.",
|
|
486
|
+
"paragraphId": "1A2B3C4D"
|
|
487
|
+
},
|
|
488
|
+
"modified": "1. Review the report.\n2. Record the approval decision."
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
The Markdown markers describe structure; visible labels are generated from the
|
|
493
|
+
document's numbering. Do not put `m)` or another computed label into target text.
|
|
494
|
+
For a single adjacent item in an existing list, the runner also accepts a
|
|
495
|
+
one-line `redline` where `modified` is the exact current item followed by the
|
|
496
|
+
new item's unnumbered text. Preserve the current item verbatim, omit the new
|
|
497
|
+
label, and make the new item at least six words so the adjacency form is
|
|
498
|
+
unambiguous. Use the explicit Markdown list form when adding multiple items,
|
|
499
|
+
nesting, or changing levels.
|
|
500
|
+
|
|
501
|
+
The canonical machine-readable contract is
|
|
502
|
+
`docs/schemas/document-operations.schema.json`. Read that schema or the examples
|
|
503
|
+
here instead of grepping bundled implementation code.
|
|
504
|
+
|
|
505
|
+
#### Standard Workflow (Extract Then Apply)
|
|
506
|
+
|
|
507
|
+
Use this for ordinary edits. `apply` validates the resulting package and
|
|
508
|
+
revision markup internally before writing, so do not add `preflight` or baseline
|
|
509
|
+
`validate` merely for reassurance.
|
|
510
|
+
|
|
511
|
+
1. Run one focused `extract`. Add `--around 3` when surrounding context is
|
|
512
|
+
needed. Retain `exactText` for drafting and target with `paragraphId` plus
|
|
513
|
+
`fingerprint`.
|
|
514
|
+
2. Build one final operation per target. Use complete `modified` text for broad
|
|
515
|
+
semantic revisions or localized `replacements` for small literal changes in
|
|
516
|
+
an inspected target. Consolidate multiple changes to one paragraph.
|
|
517
|
+
3. Run `apply` once per stable batch. Independent strong targets bind against
|
|
518
|
+
the batch-start document and do not need bottom-up sorting. Use captures only
|
|
519
|
+
for intentional created-content dependencies.
|
|
520
|
+
4. Require `completion: true` and a non-null `outputPath`. The CLI derives
|
|
521
|
+
completion from write status, operation outcomes, commit disposition, and
|
|
522
|
+
localized accepted-view verification.
|
|
523
|
+
5. Run a focused post-apply extraction only when placement or structural output
|
|
524
|
+
needs confirmation.
|
|
525
|
+
|
|
526
|
+
If `apply` returns an error, follow the recovery envelope below and make one
|
|
527
|
+
cause-specific correction. Do not probe behavior with disposable mutations or
|
|
528
|
+
read a vendor bundle.
|
|
529
|
+
|
|
530
|
+
```bash
|
|
531
|
+
# 1. Focused contextual discovery
|
|
532
|
+
docx-redline extract contract.docx --search "termination" --around 3
|
|
533
|
+
|
|
534
|
+
# 2. Serializer-backed apply against the inspected target
|
|
535
|
+
node emit-operations.mjs | docx-redline apply contract.docx --operations - --profile agent --compact --output reviewed.docx
|
|
536
|
+
|
|
537
|
+
# Optional clean edit without tracked changes
|
|
538
|
+
docx-redline apply contract.docx --target "Typo fix" --modified "Fixed typo" --no-redlines --output clean.docx
|
|
539
|
+
```
|
|
514
540
|
|
|
515
541
|
Key CLI defaults and behaviors:
|
|
516
542
|
- **Author**: Automatically defaults to `'AI Redliner'` (overridable via `--author` or `DOCX_REDLINE_AUTHOR` environment variable).
|
|
517
543
|
- **Existing revisions**: Defaults to `'merge-same-author'`. Pass `--existing-revisions slice-cross-author` to edit inside another reviewer's pending insertions with native carrier slicing.
|
|
518
544
|
- **Overwrite behavior**: Destination files provided via `--output` overwrite by default. To protect existing destination files, pass `--no-overwrite` or `--no-clobber`. The source document is never overwritten unless `--in-place` is specified.
|
|
519
545
|
- **Tracked changes**: Defaults to `generateRedlines: true`. When clean direct text is needed, pass `--no-redlines`.
|
|
520
|
-
- **Atomic rollback (optional)**: Operations apply progressively by default (`atomic: false`). For all-or-nothing transactional rollback where any error halts and reverts all changes, pass `--atomic`.
|
|
521
|
-
- **Complete-success exit (optional)**: Pass `--require-complete` when `partial` must exit nonzero (`3`). Errors exit `2`; the legacy zero exit for partial results remains when the flag is omitted.
|
|
522
|
-
- **Agent profile**: `--profile agent` enables complete-success exit behavior but preserves progressive execution and the ordinary revision policy. Compose it with `--atomic` or an explicit `--existing-revisions` choice when intended; resolved values appear in `effectiveOptions`.
|
|
523
|
-
- **Operation transport**: A UTF-8 operations file and serializer-backed `--operations -` are peers. Use whichever the host can construct without interpolating legal text in the shell.
|
|
524
|
-
- **Compact mutation JSON**: `apply`, `accept`, `reject`, and `delete-comments` omit document/package XML, full validation arrays, and duplicate nested receipts. Top-level `receipts` is authoritative. Pass `--compact` for one-line JSON; run `validate` for full issue records.
|
|
525
|
-
- Check `completion: true` and a non-null `outputPath` on stdout. Failed,
|
|
526
|
-
partial, rolled-back, unverified, and unwritten work cannot appear complete.
|
|
527
|
-
If an error occurs, follow `error.recovery.action` before correcting the cause.
|
|
528
|
-
|
|
529
|
-
For multi-clause or multi-page reviews, use focused batches rather than one
|
|
530
|
-
massive batch. This keeps context compact and simplifies error diagnosis.
|
|
531
|
-
|
|
532
|
-
#### Optional Speculative Localized Apply
|
|
533
|
-
|
|
534
|
-
Targetless localized apply remains available to expert users and harnesses with
|
|
535
|
-
a measured confidence policy. It is not the ordinary agent workflow:
|
|
536
|
-
|
|
537
|
-
```bash
|
|
538
|
-
# Globally unique literal
|
|
539
|
-
docx-redline apply contract.docx --find "thirty (30) days" --replace "sixty (60) days" --profile agent --output reviewed.docx
|
|
540
|
-
|
|
541
|
-
# Optional directional scope using a longer, fairly unique phrase
|
|
542
|
-
docx-redline apply contract.docx --search "distinctive nearby heading or phrase" --context-range 1:3 --find "thirty (30) days" --replace "sixty (60) days" --profile agent --output reviewed.docx
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
Omitting a target asks the CLI to resolve one eligible paragraph. Generic or
|
|
546
|
-
repeated anchors union their windows and may produce `AMBIGUOUS_TARGET`, costing
|
|
547
|
-
a recovery turn. `--around 3` is symmetric; `--occurrence` disambiguates only
|
|
548
|
-
inside an already selected paragraph. `anchorMatchCount > 1` signals a repeated
|
|
549
|
-
anchor even after a successful edit. Matching is exact and case-sensitive first,
|
|
550
|
-
then permits only ASCII-space/NBSP equivalence; ambiguity still fails closed.
|
|
551
|
-
|
|
552
|
-
Localized `change` evidence retains bounded before/after excerpts, commit
|
|
553
|
-
disposition, and accepted-view verification for audit consumers. Ordinary
|
|
554
|
-
agents rely on the computed top-level `completion` signal.
|
|
546
|
+
- **Atomic rollback (optional)**: Operations apply progressively by default (`atomic: false`). For all-or-nothing transactional rollback where any error halts and reverts all changes, pass `--atomic`.
|
|
547
|
+
- **Complete-success exit (optional)**: Pass `--require-complete` when `partial` must exit nonzero (`3`). Errors exit `2`; the legacy zero exit for partial results remains when the flag is omitted.
|
|
548
|
+
- **Agent profile**: `--profile agent` enables complete-success exit behavior but preserves progressive execution and the ordinary revision policy. Compose it with `--atomic` or an explicit `--existing-revisions` choice when intended; resolved values appear in `effectiveOptions`.
|
|
549
|
+
- **Operation transport**: A UTF-8 operations file and serializer-backed `--operations -` are peers. Use whichever the host can construct without interpolating legal text in the shell.
|
|
550
|
+
- **Compact mutation JSON**: `apply`, `accept`, `reject`, and `delete-comments` omit document/package XML, full validation arrays, and duplicate nested receipts. Top-level `receipts` is authoritative. Pass `--compact` for one-line JSON; run `validate` for full issue records.
|
|
551
|
+
- Check `completion: true` and a non-null `outputPath` on stdout. Failed,
|
|
552
|
+
partial, rolled-back, unverified, and unwritten work cannot appear complete.
|
|
553
|
+
If an error occurs, follow `error.recovery.action` before correcting the cause.
|
|
554
|
+
|
|
555
|
+
For multi-clause or multi-page reviews, use focused batches rather than one
|
|
556
|
+
massive batch. This keeps context compact and simplifies error diagnosis.
|
|
557
|
+
|
|
558
|
+
#### Optional Speculative Localized Apply
|
|
559
|
+
|
|
560
|
+
Targetless localized apply remains available to expert users and harnesses with
|
|
561
|
+
a measured confidence policy. It is not the ordinary agent workflow:
|
|
562
|
+
|
|
563
|
+
```bash
|
|
564
|
+
# Globally unique literal
|
|
565
|
+
docx-redline apply contract.docx --find "thirty (30) days" --replace "sixty (60) days" --profile agent --output reviewed.docx
|
|
566
|
+
|
|
567
|
+
# Optional directional scope using a longer, fairly unique phrase
|
|
568
|
+
docx-redline apply contract.docx --search "distinctive nearby heading or phrase" --context-range 1:3 --find "thirty (30) days" --replace "sixty (60) days" --profile agent --output reviewed.docx
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
Omitting a target asks the CLI to resolve one eligible paragraph. Generic or
|
|
572
|
+
repeated anchors union their windows and may produce `AMBIGUOUS_TARGET`, costing
|
|
573
|
+
a recovery turn. `--around 3` is symmetric; `--occurrence` disambiguates only
|
|
574
|
+
inside an already selected paragraph. `anchorMatchCount > 1` signals a repeated
|
|
575
|
+
anchor even after a successful edit. Matching is exact and case-sensitive first,
|
|
576
|
+
then permits only ASCII-space/NBSP equivalence; ambiguity still fails closed.
|
|
577
|
+
|
|
578
|
+
Localized `change` evidence retains bounded before/after excerpts, commit
|
|
579
|
+
disposition, and accepted-view verification for audit consumers. Ordinary
|
|
580
|
+
agents rely on the computed top-level `completion` signal.
|
|
555
581
|
|
|
556
582
|
#### High-Assurance / Staged Verification Workflow (Optional)
|
|
557
583
|
|
|
@@ -574,7 +600,7 @@ targets. For most unique clauses, `"target": "exact paragraph text"` is
|
|
|
574
600
|
sufficient; use discriminators (`paragraphId`, `fingerprint`, `index`, or
|
|
575
601
|
`occurrence`) when duplicate paragraph text appears in the document. Never
|
|
576
602
|
normalize or reconstruct `exactText`. Operation files follow
|
|
577
|
-
[`docs/schemas/document-operations.schema.json`](schemas/document-operations.schema.json).
|
|
603
|
+
[`docs/schemas/document-operations.schema.json`](schemas/document-operations.schema.json).
|
|
578
604
|
|
|
579
605
|
#### Commands
|
|
580
606
|
|
|
@@ -583,39 +609,41 @@ normalize or reconstruct `exactText`. Operation files follow
|
|
|
583
609
|
- `preflight` checks targets, anchors, revisions, conflicts, authors, and needed artifacts without mutation (read-only).
|
|
584
610
|
- `apply` applies an operation file transactionally with automatic rollback and internal markup validation.
|
|
585
611
|
- `accept` and `reject` resolve revisions selected by `--author` or `--all-authors`.
|
|
586
|
-
- `delete-comments` removes matching definitions and document anchors together
|
|
612
|
+
- `delete-comments` removes matching definitions and document anchors together, selected by `--author`,
|
|
613
|
+
`--all-authors`, or `--comment-id <id[,id]>` (ids from `inspect`). Deleting a thread root also deletes its
|
|
614
|
+
replies; unknown ids fail with `COMMENT_NOT_FOUND`.
|
|
587
615
|
- A whole-paragraph delete stops with `COMMENTED_CONTENT_DELETE` when the
|
|
588
616
|
paragraph has an existing comment. Surface the returned reviewer and comment
|
|
589
617
|
text for human follow-up; do not silently convert this into comment removal.
|
|
590
618
|
- `validate` audits revision markup and DOCX package wiring, optionally comparing against a `--baseline`.
|
|
591
619
|
|
|
592
|
-
Paragraph indexes are 1-based. Inspection filters are `--index 12`,
|
|
593
|
-
`--range 10:30`, `--indexes 2,5,8`, `--search text`, `--revised`, `--table`,
|
|
594
|
-
`--body`, `--non-empty`, and `--view accepted|rejected|current`. Search is
|
|
595
|
-
case-insensitive. When a search returns 0 matches in the active view but matches
|
|
596
|
-
exist in the alternate view (such as searching for a deleted clause to restore),
|
|
597
|
-
`selection.hint` provides immediate guidance to re-run with `--view rejected`.
|
|
598
|
-
Compact `extract --revised` results retain `hasRevisions`, `revisionAuthors`,
|
|
599
|
-
and `deleted`/`inserted` cues, so an empty `exactText` is identifiable as content
|
|
600
|
-
hidden by the selected revision view rather than a failed filter.
|
|
601
|
-
For one strongly identified rejected-view paragraph, `apply --restore
|
|
602
|
-
--target-id ID` restores the rejected text verbatim. One exact
|
|
603
|
-
`--find`/`--replace` pair may adjust it without restating the paragraph. The
|
|
604
|
-
shortcut refuses ranges, captures, weak targets, and inferred adjacent content.
|
|
605
|
-
Add `--around N` (`--context N` or `-C N`) to a search;
|
|
606
|
-
context records are labeled separately and do not consume the direct-hit
|
|
607
|
-
`--limit`. Continue a bounded result with `--after <paragraph-index>`. Ordinary
|
|
608
|
-
unscoped CLI inspection defaults to 20 direct records and a 48 KiB soft budget;
|
|
609
|
-
`--all` deliberately opts out. A malformed filter or unknown option is an error
|
|
610
|
-
rather than an unfiltered fallback. CLI help commands (`apply --help`,
|
|
611
|
-
`extract --help`, `--help`) report canonical GitHub repository URLs in their
|
|
612
|
-
`documentation` array.
|
|
613
|
-
|
|
614
|
-
Mutating commands use `--author`, operation-level authors, then
|
|
615
|
-
`DOCX_REDLINE_AUTHOR`, falling back visibly to `AI Redliner`; review-resolution
|
|
616
|
-
commands may use `--all-authors` where applicable. Without `--output`, a sibling
|
|
617
|
-
such as `contract.redlined.docx` is chosen. Existing outputs are refused unless
|
|
618
|
-
`--force` is present. `--in-place` is the only way to overwrite the input.
|
|
620
|
+
Paragraph indexes are 1-based. Inspection filters are `--index 12`,
|
|
621
|
+
`--range 10:30`, `--indexes 2,5,8`, `--search text`, `--revised`, `--table`,
|
|
622
|
+
`--body`, `--non-empty`, and `--view accepted|rejected|current`. Search is
|
|
623
|
+
case-insensitive. When a search returns 0 matches in the active view but matches
|
|
624
|
+
exist in the alternate view (such as searching for a deleted clause to restore),
|
|
625
|
+
`selection.hint` provides immediate guidance to re-run with `--view rejected`.
|
|
626
|
+
Compact `extract --revised` results retain `hasRevisions`, `revisionAuthors`,
|
|
627
|
+
and `deleted`/`inserted` cues, so an empty `exactText` is identifiable as content
|
|
628
|
+
hidden by the selected revision view rather than a failed filter.
|
|
629
|
+
For one strongly identified rejected-view paragraph, `apply --restore
|
|
630
|
+
--target-id ID` restores the rejected text verbatim. One exact
|
|
631
|
+
`--find`/`--replace` pair may adjust it without restating the paragraph. The
|
|
632
|
+
shortcut refuses ranges, captures, weak targets, and inferred adjacent content.
|
|
633
|
+
Add `--around N` (`--context N` or `-C N`) to a search;
|
|
634
|
+
context records are labeled separately and do not consume the direct-hit
|
|
635
|
+
`--limit`. Continue a bounded result with `--after <paragraph-index>`. Ordinary
|
|
636
|
+
unscoped CLI inspection defaults to 20 direct records and a 48 KiB soft budget;
|
|
637
|
+
`--all` deliberately opts out. A malformed filter or unknown option is an error
|
|
638
|
+
rather than an unfiltered fallback. CLI help commands (`apply --help`,
|
|
639
|
+
`extract --help`, `--help`) report canonical GitHub repository URLs in their
|
|
640
|
+
`documentation` array.
|
|
641
|
+
|
|
642
|
+
Mutating commands use `--author`, operation-level authors, then
|
|
643
|
+
`DOCX_REDLINE_AUTHOR`, falling back visibly to `AI Redliner`; review-resolution
|
|
644
|
+
commands may use `--all-authors` where applicable. Without `--output`, a sibling
|
|
645
|
+
such as `contract.redlined.docx` is chosen. Existing outputs are refused unless
|
|
646
|
+
`--force` is present. `--in-place` is the only way to overwrite the input.
|
|
619
647
|
|
|
620
648
|
Treat a nonzero exit code or JSON `status: "error"` as failure. A failed atomic
|
|
621
649
|
operation reports `written: false` and does not write an output file.
|
|
@@ -630,8 +658,57 @@ To reply inside an existing Word comment thread, use the comment ID returned by
|
|
|
630
658
|
{ "type": "comment_reply", "parentCommentId": "8", "commentContent": "Agreed; updated.", "author": "Editor" }
|
|
631
659
|
```
|
|
632
660
|
|
|
633
|
-
|
|
634
|
-
|
|
661
|
+
A reply is defined in `word/comments.xml` and linked to its thread in `word/commentsExtended.xml`
|
|
662
|
+
(`w15:paraIdParent`, keyed on the last paragraph of each comment, as Word does). Word only displays a
|
|
663
|
+
reply that also has its own `commentRangeStart`, `commentRangeEnd` and `commentReference` around the same
|
|
664
|
+
range as its thread, so the operation adds those markers next to the thread's existing ones. A reply to a
|
|
665
|
+
reply joins the root thread. If the parent has no anchor in the body the operation fails with
|
|
666
|
+
`PARENT_ANCHOR_NOT_FOUND`.
|
|
667
|
+
|
|
668
|
+
#### Headers and footers
|
|
669
|
+
|
|
670
|
+
`inspect().headersFooters` lists every header/footer part the document uses, with its paragraph text:
|
|
671
|
+
`path`, `kind`, `type` (`default`/`first`/`even`), `appliesToSections`, `active`, `hasFields`. Add `part` to an
|
|
672
|
+
operation to edit one instead of the body; everything else in the operation is unchanged:
|
|
673
|
+
|
|
674
|
+
```json
|
|
675
|
+
{ "type": "replace", "part": { "kind": "footer", "type": "default", "section": 0 },
|
|
676
|
+
"target": { "exactText": "Draft v1" }, "modified": "Draft v2" }
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
`part` is a selector (`kind`, optional `type` defaulting to `default`, optional zero-based `section`) or a part path such as
|
|
680
|
+
`word/footer2.xml`. Omitting `part` always targets the body; headers and footers are opt-in. Rules:
|
|
681
|
+
|
|
682
|
+
- A footer or header shared by several sections is one part; the result lists `partSections`. A section with no reference of its
|
|
683
|
+
own inherits the previous section's part (Word's "link to previous"), and `section` finds it.
|
|
684
|
+
- Several distinct parts matching a selector fail with `PART_AMBIGUOUS` (add `section` or use the path); none fail with
|
|
685
|
+
`PART_NOT_FOUND`. A bad selector rejects the whole request before anything is written.
|
|
686
|
+
- Fields are atomic. An edit that would change a `PAGE`/`NUMPAGES`/`DATE` field's text fails with `FIELD_EDIT_REFUSED`; edit the
|
|
687
|
+
text around the field instead.
|
|
688
|
+
- `comment`, `comment_reply` and `comment_resolve` with `part` fail with `COMMENT_IN_HEADER_FOOTER` (Word has no comments in headers or footers).
|
|
689
|
+
- Body and part operations can share a batch; results keep the original operation indexes and carry `part`. Atomic batches roll
|
|
690
|
+
back across body and parts. Revision ids stay unique across the whole package.
|
|
691
|
+
- `accept` / `reject` (API and CLI) also resolve tracked changes inside headers and footers.
|
|
692
|
+
- Creating a header/footer that does not exist is not supported.
|
|
693
|
+
- `exactText` in a header or footer is the whole paragraph text as `headersFooters[].paragraphs` shows it, including the
|
|
694
|
+
current result of any field (for example `Page 1`).
|
|
695
|
+
|
|
696
|
+
#### Resolving comment threads
|
|
697
|
+
|
|
698
|
+
Resolve (or reopen with `"resolved": false`) the thread containing a comment id from `inspect`:
|
|
699
|
+
|
|
700
|
+
```json
|
|
701
|
+
{ "type": "comment_resolve", "commentId": "8" }
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
The API form is `await doc.resolveComment(commentId, { resolved })`. Word resolves whole threads, so any id in
|
|
705
|
+
the thread updates the root and every reply together; a reply added to a resolved thread is written resolved,
|
|
706
|
+
as Word does. Only `word/commentsExtended.xml` changes. Resolving an already-resolved thread is `no_change`.
|
|
707
|
+
Unknown ids fail with `COMMENT_NOT_FOUND`, in `preflight` as well as `apply`. Operations run in batch order, so
|
|
708
|
+
`comment_reply` followed by `comment_resolve` closes the thread including the new reply. Like any comment
|
|
709
|
+
operation it is refused with `part`.
|
|
710
|
+
|
|
711
|
+
Resolving and deleting other reviewers' comments are review decisions: do them only when the user asked.
|
|
635
712
|
|
|
636
713
|
#### Legacy skill wrapper migration
|
|
637
714
|
|
|
@@ -653,7 +730,7 @@ must use the JSON status and process exit code; failed atomic work has
|
|
|
653
730
|
|
|
654
731
|
#### Safe Operations File Creation (JSON vs. Shell Heredocs)
|
|
655
732
|
|
|
656
|
-
When composing batch operation JSON, whether for a file or stdin:
|
|
733
|
+
When composing batch operation JSON, whether for a file or stdin:
|
|
657
734
|
|
|
658
735
|
- **Use structured file-writing tools or JSON serializers**: Write operations files via your environment's file-creation tools or a language JSON serializer (`JSON.stringify`).
|
|
659
736
|
- **Never compose operations in raw shell heredocs** (e.g., `cat << 'EOF'` in bash or PowerShell `@" ... "@`): Legal clauses routinely contain curly quotes (`“ ”`), smart apostrophes (`’`), em-dashes (`—`), section symbols (`§`), non-breaking spaces, and backslashes. Shell heredocs frequently mangle Unicode character encodings, quote escaping, and whitespace formatting, causing immediate `TARGET_NOT_FOUND` failures.
|
|
@@ -664,9 +741,9 @@ In default progressive mode (`atomic: false`), operations execute independently:
|
|
|
664
741
|
|
|
665
742
|
- **Do not rely solely on top-level `written: true` or `status !== "error"`**: A progressive batch can return `status: "partial"` with `written: true` when some operations succeed and others fail.
|
|
666
743
|
- **Walk every entry in `results`**: Check `results[i].status` and `results[i].error`. Any `status: "error"` entry in `results` represents an unapplied change that must be investigated and resolved.
|
|
667
|
-
- **`written: false`**: Indicates that zero operations were committed (or an atomic rollback occurred). Never treat or present an unwritten or partial output file as complete.
|
|
668
|
-
- **Follow `retryPlan`**: `base: "original"` means use the unchanged source and replay the corrected batch. `base: "output"` means retain committed progressive edits and submit only failed/unattempted indexes. `sameArgumentsSafe` is always false for failures.
|
|
669
|
-
- **Follow the recovery envelope**: Use `error.recovery.action`, `requiresReinspection`, and `requiresUserAuthorization` rather than deriving a retry from the prose message. The envelope is versioned by `recoveryVersion`.
|
|
744
|
+
- **`written: false`**: Indicates that zero operations were committed (or an atomic rollback occurred). Never treat or present an unwritten or partial output file as complete.
|
|
745
|
+
- **Follow `retryPlan`**: `base: "original"` means use the unchanged source and replay the corrected batch. `base: "output"` means retain committed progressive edits and submit only failed/unattempted indexes. `sameArgumentsSafe` is always false for failures.
|
|
746
|
+
- **Follow the recovery envelope**: Use `error.recovery.action`, `requiresReinspection`, and `requiresUserAuthorization` rather than deriving a retry from the prose message. The envelope is versioned by `recoveryVersion`.
|
|
670
747
|
|
|
671
748
|
#### Human-Readable References vs. Internal Machine Handles
|
|
672
749
|
|
|
@@ -687,30 +764,40 @@ When the CLI or runner returns an error code, follow these specific recovery act
|
|
|
687
764
|
|---|---|---|
|
|
688
765
|
| `TARGET_NOT_FOUND` | Target text did not match any paragraph. | **Do NOT retry with paraphrased text.** Re-run `extract`/`inspect`, copy `exactText` verbatim (including exact whitespace/punctuation), and add a discriminator (`paragraphId`, `fingerprint`, or `occurrence`). |
|
|
689
766
|
| `AMBIGUOUS_TARGET` | Multiple paragraphs match identical text. | Disambiguate by supplying `paragraphId`, `fingerprint`, `occurrence`, or `index` in the target descriptor. |
|
|
690
|
-
| `ANCHOR_NOT_FOUND` / `AMBIGUOUS_ANCHOR` | A comment or rejected-view insertion anchor was not uniquely matched. | For comments, narrow `textToComment` or omit it to anchor the whole paragraph. For rejected-view insertion, copy exact rejected text and provide `anchor.occurrence`. |
|
|
691
|
-
| `OVERLAPPING_SOURCE_TARGETS` / `OVERLAPPING_TEXT_EDITS` | Multiple complete text operations target the same batch-start source. | Consolidate all changes to that paragraph into one `redline` or `replace`; the library cannot choose between incompatible complete desired states. |
|
|
692
|
-
| `REVISION_ORDER_CONFLICT` | A text rewrite and formatting/highlight operation overlap the same source. | Consolidate or split the work at an intentional created-content dependency; do not try a different arbitrary order. |
|
|
693
|
-
| `CAPTURE_FANOUT_CONFLICT` | Multiple mutating consumers share one capture without distinct selectors. | Give consumers distinct selectors, chain them explicitly, or split the batch. |
|
|
694
|
-
| `EXISTING_REVISIONS` | Target paragraph contains tracked changes from another author. | Fails closed to protect third-party review marks. If editing inside that reviewer's pending insertion is intended, pass `--existing-revisions slice-cross-author` (or `existingRevisions: 'slice-cross-author'`). Do not pass `accept-all-first` without explicit user authorization. |
|
|
695
|
-
| `PATCH_ROUNDTRIP_MISMATCH` | A cross-author surgical edit did not reconstruct the requested modified text exactly. | Treat the operation as unapplied. Re-extract the exact paragraph text and split the edit into a narrower operation that does not cross the reported structural boundary. |
|
|
696
|
-
| `FOREIGN_PARAGRAPH_MARK_DELETION` | A normal edit attempted to write into a paragraph wholly deleted by another reviewer. | Use an explicit `restore` operation if the user intends to counterpropose that paragraph; otherwise leave the deletion unresolved. |
|
|
697
|
-
| `RESTORATION_STATE_REQUIRED` / `RESTORATION_COUNT_MISMATCH` | A `restore` target is not a wholly foreign-deleted paragraph, or its replacement count does not match the paragraph range. | Re-inspect the document and target the deleted paragraph by stable descriptor; provide exactly one replacement string per source paragraph. |
|
|
698
|
-
| `REJECTED_INSERTION_STATE_REQUIRED` / `UNSAFE_REVISION_BOUNDARY` | An explicit rejected-view insertion did not resolve to supported plain run text inside a wholly foreign-deleted paragraph. | Do not fall back to a generic edit. Narrow the exact anchor/offset, or handle comments, bookmarks, fields, hyperlinks, moves, or other structural boundaries manually. |
|
|
699
|
-
| `GENERATED_OOXML_INVALID` | The operation introduced a new validation error relative to its baseline. | Treat the operation as unapplied and inspect `generatedIssues`; correct the generating operation or builder rather than repairing or accepting the source document's unrelated baseline defects. |
|
|
700
|
-
| `UNSAFE_DELETED_TABLE_ROW` / `UNSUPPORTED_MOVE_REVISION` / `SECTION_BREAK_PARAGRAPH` / `UNSAFE_PARAGRAPH_PLACEMENT` | Paragraph restoration cannot preserve the source structural boundary safely. | Do not retry as an ordinary redline. Resolve the row/move/section/placement condition manually or narrow the restoration to a safe paragraph. |
|
|
767
|
+
| `ANCHOR_NOT_FOUND` / `AMBIGUOUS_ANCHOR` | A comment or rejected-view insertion anchor was not uniquely matched. | For comments, narrow `textToComment` or omit it to anchor the whole paragraph. For rejected-view insertion, copy exact rejected text and provide `anchor.occurrence`. |
|
|
768
|
+
| `OVERLAPPING_SOURCE_TARGETS` / `OVERLAPPING_TEXT_EDITS` | Multiple complete text operations target the same batch-start source. | Consolidate all changes to that paragraph into one `redline` or `replace`; the library cannot choose between incompatible complete desired states. |
|
|
769
|
+
| `REVISION_ORDER_CONFLICT` | A text rewrite and formatting/highlight operation overlap the same source. | Consolidate or split the work at an intentional created-content dependency; do not try a different arbitrary order. |
|
|
770
|
+
| `CAPTURE_FANOUT_CONFLICT` | Multiple mutating consumers share one capture without distinct selectors. | Give consumers distinct selectors, chain them explicitly, or split the batch. |
|
|
771
|
+
| `EXISTING_REVISIONS` | Target paragraph contains tracked changes from another author. | Fails closed to protect third-party review marks. If editing inside that reviewer's pending insertion is intended, pass `--existing-revisions slice-cross-author` (or `existingRevisions: 'slice-cross-author'`). Do not pass `accept-all-first` without explicit user authorization. |
|
|
772
|
+
| `PATCH_ROUNDTRIP_MISMATCH` | A cross-author surgical edit did not reconstruct the requested modified text exactly. | Treat the operation as unapplied. Re-extract the exact paragraph text and split the edit into a narrower operation that does not cross the reported structural boundary. |
|
|
773
|
+
| `FOREIGN_PARAGRAPH_MARK_DELETION` | A normal edit attempted to write into a paragraph wholly deleted by another reviewer. | Use an explicit `restore` operation if the user intends to counterpropose that paragraph; otherwise leave the deletion unresolved. |
|
|
774
|
+
| `RESTORATION_STATE_REQUIRED` / `RESTORATION_COUNT_MISMATCH` | A `restore` target is not a wholly foreign-deleted paragraph, or its replacement count does not match the paragraph range. | Re-inspect the document and target the deleted paragraph by stable descriptor; provide exactly one replacement string per source paragraph. |
|
|
775
|
+
| `REJECTED_INSERTION_STATE_REQUIRED` / `UNSAFE_REVISION_BOUNDARY` | An explicit rejected-view insertion did not resolve to supported plain run text inside a wholly foreign-deleted paragraph. | Do not fall back to a generic edit. Narrow the exact anchor/offset, or handle comments, bookmarks, fields, hyperlinks, moves, or other structural boundaries manually. |
|
|
776
|
+
| `GENERATED_OOXML_INVALID` | The operation introduced a new validation error relative to its baseline. | Treat the operation as unapplied and inspect `generatedIssues`; correct the generating operation or builder rather than repairing or accepting the source document's unrelated baseline defects. |
|
|
777
|
+
| `UNSAFE_DELETED_TABLE_ROW` / `UNSUPPORTED_MOVE_REVISION` / `SECTION_BREAK_PARAGRAPH` / `UNSAFE_PARAGRAPH_PLACEMENT` | Paragraph restoration cannot preserve the source structural boundary safely. | Do not retry as an ordinary redline. Resolve the row/move/section/placement condition manually or narrow the restoration to a safe paragraph. |
|
|
778
|
+
| `PART_NOT_FOUND` / `PART_AMBIGUOUS` | The `part` selector matched no header/footer, or several. | Re-run `inspect` and use `headersFooters[].path`, or add `section`. |
|
|
779
|
+
| `FIELD_EDIT_REFUSED` | The edit would change a field (PAGE, NUMPAGES, DATE) in a header or footer. | Edit the text around the field. |
|
|
780
|
+
| `COMMENT_IN_HEADER_FOOTER` | A comment operation carried `part`. | Comment on body text instead. |
|
|
781
|
+
| `COMMENT_NOT_FOUND` | `comment_resolve` or `delete-comments --comment-id` named an id that is not in the document. | Re-run `inspect` and use `comments[].id`. |
|
|
782
|
+
| `PARENT_COMMENT_NOT_FOUND` / `PARENT_ANCHOR_NOT_FOUND` | A reply's parent id is unknown, or the parent has no anchor in the body. | Re-run `inspect`; reply to a comment that is anchored in the body. |
|
|
701
783
|
| `COMMENTED_CONTENT_MERGE` / `COMMENTED_CONTENT_DELETE` | Operation would overwrite, revert, or delete content with comments. | Fails closed to prevent orphaned comment threads. Report the comment author and text to the user; resolve the comment before re-editing. |
|
|
702
|
-
| `INVALID_OPERATION` | Operation object violates schema or has incompatible fields. | Validate the JSON structure against [`document-operations.schema.json`](schemas/document-operations.schema.json) before targeting is attempted. |
|
|
784
|
+
| `INVALID_OPERATION` | Operation object violates schema or has incompatible fields. | Validate the JSON structure against [`document-operations.schema.json`](schemas/document-operations.schema.json) before targeting is attempted. |
|
|
703
785
|
| `STRUCTURED_CONTENT_INVALID` | Malformed Markdown table or structure in replacement text. | Ensure tables include a separator row (`\| --- \| --- \|`) and consistent column counts; do not downgrade to raw text. |
|
|
704
786
|
|
|
705
787
|
**Important Rule:** Never repeat the exact same failing command without correcting the reported cause. If an error persists after one correction attempt, stop and report the diagnostic code to the user.
|
|
706
788
|
|
|
707
789
|
#### Document Scope & Boundary Invariants
|
|
708
790
|
|
|
709
|
-
The `docx-redline` engine and CLI operate
|
|
791
|
+
The `docx-redline` engine and CLI operate on the **main document body** and, when an operation sets `part`, on
|
|
792
|
+
**existing header and footer parts**:
|
|
710
793
|
|
|
711
|
-
- **Supported Content**: Body paragraphs, numbered/bulleted lists, tables and table cells, comments,
|
|
712
|
-
|
|
713
|
-
-
|
|
794
|
+
- **Supported Content**: Body paragraphs, numbered/bulleted lists, tables and table cells, comments, comment replies,
|
|
795
|
+
resolving/reopening comment threads, and text in existing headers and footers (see *Headers and footers*).
|
|
796
|
+
- **Unsupported Content**: Footnotes, endnotes, floating text boxes, shape drawings, images, watermarks, embedded macros,
|
|
797
|
+
creating a header or footer that does not exist, editing field codes or results (`PAGE`, `NUMPAGES`, `DATE`), and
|
|
798
|
+
comments in headers or footers (Word has none).
|
|
799
|
+
- Do not attempt to target footnote citations or text boxes with `docx-redline`. Header/footer text is edited only with
|
|
800
|
+
an explicit `part`; body searches never match it.
|
|
714
801
|
|
|
715
802
|
### Convert paragraph text into a Word list
|
|
716
803
|
|
|
@@ -763,12 +850,12 @@ import { reconcileMarkdownTableOoxml } from '@ansonlai/docx-redline-js';
|
|
|
763
850
|
const result = await reconcileMarkdownTableOoxml(tableOoxml, originalText, markdownTable);
|
|
764
851
|
```
|
|
765
852
|
|
|
766
|
-
## Detailed Architecture
|
|
767
|
-
|
|
768
|
-
Use the layout and task shortcuts at the top of this file for normal work.
|
|
769
|
-
`ARCHITECTURE.md` is the maintained detailed module-ownership map and describes
|
|
770
|
-
the end-to-end dependency flow. Do not infer ownership from directory names or
|
|
771
|
-
reconstruct the architecture by repeatedly listing the repository.
|
|
853
|
+
## Detailed Architecture
|
|
854
|
+
|
|
855
|
+
Use the layout and task shortcuts at the top of this file for normal work.
|
|
856
|
+
`ARCHITECTURE.md` is the maintained detailed module-ownership map and describes
|
|
857
|
+
the end-to-end dependency flow. Do not infer ownership from directory names or
|
|
858
|
+
reconstruct the architecture by repeatedly listing the repository.
|
|
772
859
|
|
|
773
860
|
## Common Patterns
|
|
774
861
|
|
|
@@ -826,22 +913,22 @@ retain their string-only return type and return `''` for parse failures.
|
|
|
826
913
|
|
|
827
914
|
### Target text versus replacement text
|
|
828
915
|
|
|
829
|
-
Target resolution may normalize surrounding or repeated whitespace while
|
|
830
|
-
matching a paragraph. Replacement text is not normalized: tabs, line breaks,
|
|
831
|
-
non-breaking spaces, repeated spaces, and leading/trailing whitespace become
|
|
832
|
-
part of the requested edit. When editing extracted document text, copy the
|
|
833
|
-
exact paragraph text and modify it in place rather than round-tripping it
|
|
834
|
-
through a formatter that may change whitespace.
|
|
835
|
-
|
|
836
|
-
The normalized caller target is never used for mutation offsets in a text-bearing
|
|
837
|
-
edit. After target selection, the engine uses the resolved paragraph's byte-exact
|
|
838
|
-
JavaScript string as the source coordinate system. The legacy format-only
|
|
839
|
-
fallback for field-code paragraphs with no extractable accepted-view spans is
|
|
840
|
-
not a text-replacement path. If an ASCII-space target selected an NBSP
|
|
841
|
-
source, `resolvedTarget.targetTextMatch` reports `space_equivalent`, escaped
|
|
842
|
-
source/request excerpts, and differing code points. An NBSP-to-space request is
|
|
843
|
-
tracked as a replacement; it must not retain the NBSP and append another space.
|
|
844
|
-
The CLI keeps these bounded diagnostics but removes resolved clause text.
|
|
916
|
+
Target resolution may normalize surrounding or repeated whitespace while
|
|
917
|
+
matching a paragraph. Replacement text is not normalized: tabs, line breaks,
|
|
918
|
+
non-breaking spaces, repeated spaces, and leading/trailing whitespace become
|
|
919
|
+
part of the requested edit. When editing extracted document text, copy the
|
|
920
|
+
exact paragraph text and modify it in place rather than round-tripping it
|
|
921
|
+
through a formatter that may change whitespace.
|
|
922
|
+
|
|
923
|
+
The normalized caller target is never used for mutation offsets in a text-bearing
|
|
924
|
+
edit. After target selection, the engine uses the resolved paragraph's byte-exact
|
|
925
|
+
JavaScript string as the source coordinate system. The legacy format-only
|
|
926
|
+
fallback for field-code paragraphs with no extractable accepted-view spans is
|
|
927
|
+
not a text-replacement path. If an ASCII-space target selected an NBSP
|
|
928
|
+
source, `resolvedTarget.targetTextMatch` reports `space_equivalent`, escaped
|
|
929
|
+
source/request excerpts, and differing code points. An NBSP-to-space request is
|
|
930
|
+
tracked as a replacement; it must not retain the NBSP and append another space.
|
|
931
|
+
The CLI keeps these bounded diagnostics but removes resolved clause text.
|
|
845
932
|
|
|
846
933
|
For ordinary insertions and deletions, target the visible accepted view:
|
|
847
934
|
inserted `w:t` text is visible and deleted `w:delText` is not. Move revisions
|