@ansonlai/docx-redline-js 0.5.4 → 0.6.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.
Files changed (55) hide show
  1. package/AGENTS.md +82 -697
  2. package/ARCHITECTURE.md +13 -1
  3. package/CHANGELOG.md +8 -0
  4. package/README.md +177 -45
  5. package/core/paragraph-targeting.js +14 -2
  6. package/dist/docx-redline-js.esm.js +184 -51
  7. package/dist/docx-redline-js.esm.js.map +3 -3
  8. package/dist/docx-redline-js.esm.min.js +77 -77
  9. package/dist/docx-redline-js.esm.min.js.map +4 -4
  10. package/docs/AGENT_FAST_START.md +59 -0
  11. package/docs/AGENT_KNOWLEDGE_BASE.md +878 -0
  12. package/docs/SKILL_AUTHORING.md +126 -0
  13. package/docs/TESTING.md +35 -1
  14. package/docs/schemas/document-operations.schema.json +5 -1
  15. package/docs/validation-reports/2026-09-12-agent-cli-discovery-baseline.md +56 -0
  16. package/docs/validation-reports/2026-09-12-agent-protocol-rollout.md +86 -0
  17. package/docs/validation-reports/2026-09-13-agent-cli-efficiency-rollout.md +86 -0
  18. package/engine/oxml-engine.js +80 -13
  19. package/engine/run-builders.js +5 -15
  20. package/index.d.ts +28 -3
  21. package/node/cli-help.js +209 -0
  22. package/node/cli.js +323 -65
  23. package/node/docx-document.js +120 -69
  24. package/node/index.d.ts +6 -2
  25. package/package.json +15 -3
  26. package/scripts/generate-cross-author-slicing-fixtures.ps1 +25 -25
  27. package/services/batch-operation-orchestrator.js +215 -120
  28. package/services/document-inspection.js +89 -11
  29. package/services/document-operation-applier.js +52 -34
  30. package/services/document-operation-contract.js +10 -6
  31. package/services/document-operation-mutations.js +51 -5
  32. package/services/document-operation-session.js +4 -0
  33. package/services/error-recovery.js +174 -0
  34. package/services/operation-batch-compiler.js +394 -0
  35. package/services/operation-preflight.js +91 -72
  36. package/services/standalone-operation-runner.d.ts +17 -1
  37. package/docs/plans/2026-09-05-structural-revisions-and-fidelity-oracles.md +0 -1669
  38. package/docs/plans/2026-09-08-cross-author-revision-slicing.md +0 -1399
  39. package/docs/plans/completed/2026-03-01-release-0.1.4-design.md +0 -33
  40. package/docs/plans/completed/2026-03-01-release-0.1.4.md +0 -110
  41. package/docs/plans/completed/2026-05-31-architectural changes.md +0 -593
  42. package/docs/plans/completed/2026-08-02-reliability-improvements.md +0 -1155
  43. package/docs/plans/completed/2026-08-30-reliability-testing-improvements.md +0 -488
  44. package/docs/plans/completed/2026-09-01-performance-and-complexity-reduction.md +0 -669
  45. package/docs/plans/completed/2026-09-03-agent-friendly-document-workflows.md +0 -427
  46. package/docs/plans/completed/2026-09-04-comment-anchor-and-cli-reliability.md +0 -519
  47. package/docs/plans/completed/PERFORMANCE-CONSOLIDATION.md +0 -69
  48. package/docs/plans/completed/structural-revision-capability-matrix.md +0 -115
  49. package/docs/test-comparison-dashboard.html +0 -4338
  50. package/docs/validation-reports/2026-08-30-phase-1-word-visual-preflight.md +0 -22
  51. package/docs/validation-reports/2026-08-30-phase-2-word-visual-preflight.md +0 -24
  52. package/docs/validation-reports/2026-08-30-phase-3-coverage.md +0 -73
  53. package/docs/validation-reports/2026-09-02-multilevel-bullets-visual-review.md +0 -82
  54. package/docs/validation-reports/2026-09-02-multimodal-visual-samples.md +0 -114
  55. package/docs/validation-reports/2026-09-02-visual-failures-preflight.md +0 -79
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type OoxmlSourceType = 'package' | 'document' | 'fragment';
2
- export type RedlineStatus = 'ok' | 'no-op' | 'error';
2
+ export type RedlineStatus = 'ok' | 'no-op' | 'partial' | 'error';
3
3
  export type ExistingRevisionsPolicy = 'merge-same-author' | 'slice-cross-author' | 'reject-input' | 'accept-all-first' | 'accept-all-first-keep-normalized';
4
4
  export type RevisionView = 'accepted' | 'rejected';
5
5
 
@@ -68,6 +68,22 @@ export interface RedlineError {
68
68
  actual?: unknown;
69
69
  commentIds?: string[];
70
70
  comments?: Array<{ id: string; author: string; text: string }>;
71
+ recoveryVersion?: number;
72
+ category?: 'request_fixable' | 'target_refresh_required' | 'candidate_selection_required' | 'policy_choice_required' | 'user_authorization_required' | 'source_conflict' | 'library_or_builder_failure' | 'manual_document_resolution' | string;
73
+ context?: Record<string, unknown>;
74
+ recovery?: {
75
+ action: string;
76
+ sameArgumentsSafe: boolean;
77
+ requiresReinspection: boolean;
78
+ requiresUserAuthorization: boolean;
79
+ field?: string;
80
+ recommendedValue?: unknown;
81
+ };
82
+ expectedRevision?: RevisionToken;
83
+ currentRevision?: RevisionToken;
84
+ issueSummary?: { total: number; byCode: Array<{ code: string; count: number }> };
85
+ candidates?: unknown[];
86
+ [key: string]: unknown;
71
87
  }
72
88
 
73
89
  export interface RedlineOptions {
@@ -262,13 +278,15 @@ export function getTrackedChangeAuthors(xmlDocOrElement: Document | Element | nu
262
278
  export interface InspectedParagraph {
263
279
  index: number; ref: string; paragraphId: string | null; fingerprint: string | null;
264
280
  text: string; exactText: string; excerpt: string; inTable: boolean;
265
- humanReference: string; styleId: string | null;
281
+ humanReference: string; provision: string | null; styleId: string | null;
266
282
  table: { tableIndex: number; rowIndex: number; cellIndex: number } | null;
267
283
  structuralReferences: Array<{ type: 'footnote' | 'endnote' | 'comment'; id: string | null }>;
268
284
  headingLevel: number | null; nearestHeading: { level: number; text: string } | null;
269
285
  list: { numId: string; level: number; label: string | null; format: string | null } | null;
270
286
  hasRevisions: boolean; revisionAuthors: string[]; commentIds: string[];
271
287
  segments: RevisionTextSegment[];
288
+ selectionRole?: 'match' | 'context';
289
+ contextFor?: number[];
272
290
  }
273
291
  export interface RevisionToken {
274
292
  algorithm: 'sha256';
@@ -279,10 +297,17 @@ export interface RevisionToken {
279
297
  }
280
298
 
281
299
  export interface InspectedComment { id: string; author: string | null; date: string | null; text: string; paraId?: string | null; parentParaId?: string; parentCommentId?: string | null; done?: boolean; paragraphIndex?: number; targetRef?: string; anchoredText?: string; }
282
- export interface DocumentInspectionOptions { revisionView?: 'accepted' | 'rejected' | 'current'; excerptLength?: number; revisedOnly?: boolean; inTable?: boolean; skipEmpty?: boolean; search?: string; indexes?: number[]; range?: { start: number; end: number } | [number, number]; digestFn?: (bytes: Uint8Array) => string; }
300
+ export interface DocumentInspectionOptions { revisionView?: 'accepted' | 'rejected' | 'current'; excerptLength?: number; revisedOnly?: boolean; inTable?: boolean; skipEmpty?: boolean; search?: string; indexes?: number[]; range?: { start: number; end: number } | [number, number]; around?: number; limit?: number; after?: number; digestFn?: (bytes: Uint8Array) => string; }
301
+ export interface DocumentInspectionSelection {
302
+ search?: string; caseSensitive?: false; totalMatches: number; returnedMatches: number;
303
+ returnedParagraphs: number; truncated: boolean; nextAfter: number | null;
304
+ limit?: number; after?: number; around?: number; softByteLimit?: number;
305
+ oversizeItem?: boolean; contextTruncated?: boolean;
306
+ }
283
307
  export interface DocumentInspectionResult {
284
308
  status: 'ok' | 'error'; paragraphs: InspectedParagraph[]; comments: InspectedComment[];
285
309
  revisionAuthors?: string[]; commentAuthors?: string[]; counts?: { paragraphs: number; comments: number; revisedParagraphs: number };
310
+ selection?: DocumentInspectionSelection;
286
311
  revisionToken?: RevisionToken | null;
287
312
  coveredParts?: string[];
288
313
  warnings: string[]; error?: RedlineError;
@@ -0,0 +1,209 @@
1
+ const option = (key, name, description) => Object.freeze({ key, name, description });
2
+
3
+ const HELP = option('help', '--help, -h', 'Return machine-readable help without opening a document.');
4
+ const AUTHOR = option('author', '--author <name>, -a <name>', 'Reviewer name; falls back to DOCX_REDLINE_AUTHOR, then AI Redliner.');
5
+ const OUTPUT = option('output', '--output <file>, -o <file>', 'Write to this destination; the source is not overwritten.');
6
+ const IN_PLACE = option('inPlace', '--in-place, -i', 'Explicitly overwrite the source document.');
7
+ const FORCE = option('force', '--force, -f', 'Allow replacement of an existing destination.');
8
+ const NO_OVERWRITE = option('noOverwrite', '--no-overwrite, --no-clobber', 'Refuse replacement of an existing destination.');
9
+ const ALL_AUTHORS = option('allAuthors', '--all-authors', 'Resolve review content for every author; requires explicit user authorization.');
10
+ const COMPACT = option('compact', '--compact', 'Emit one-line JSON to reduce provider-visible output bytes.');
11
+
12
+ const INSPECTION_OPTIONS = Object.freeze([
13
+ HELP,
14
+ option('search', '--search <text>', 'Case-insensitive substring search.'),
15
+ option('revised', '--revised', 'Select paragraphs containing tracked revisions.'),
16
+ option('table', '--table', 'Select paragraphs inside tables.'),
17
+ option('body', '--body', 'Select paragraphs outside tables.'),
18
+ option('nonEmpty', '--non-empty', 'Exclude empty paragraphs; this is not a narrow document scope.'),
19
+ option('index', '--index <N>', 'Select one 1-based machine paragraph index.'),
20
+ option('indexes', '--indexes <N,N,...>', 'Select comma-separated 1-based machine paragraph indexes.'),
21
+ option('range', '--range <START:END>', 'Select an inclusive range of 1-based machine paragraph indexes.'),
22
+ option('view', '--view <accepted|rejected|current>', 'Select the revision view; restore discovery normally uses rejected.'),
23
+ option('around', '--around <N>, --context <N>, -C <N>', 'With --search, include 0-20 physical paragraphs around each returned match.'),
24
+ option('limit', '--limit <N>', 'Return at most N direct matches; surrounding context does not count.'),
25
+ option('after', '--after <INDEX>', 'Continue after this exclusive 1-based source paragraph index.'),
26
+ option('all', '--all', 'Explicitly bypass default result and soft output limits.')
27
+ ]);
28
+
29
+ const MUTATION_DESTINATION_OPTIONS = Object.freeze([
30
+ AUTHOR, OUTPUT, IN_PLACE, FORCE, NO_OVERWRITE
31
+ ]);
32
+
33
+ const APPLY_EXAMPLES = Object.freeze([
34
+ {
35
+ description: 'Ordinary tracked replacement; modified is the complete desired accepted-view paragraph.',
36
+ operation: {
37
+ type: 'redline',
38
+ target: { exactText: 'Original clause.', paragraphId: '1A2B3C4D' },
39
+ modified: 'Revised clause.'
40
+ }
41
+ },
42
+ {
43
+ description: 'Comment the complete target paragraph.',
44
+ operation: {
45
+ type: 'comment',
46
+ target: { exactText: 'Clause to review.', paragraphId: '2A2B3C4D' },
47
+ commentContent: 'Please confirm this language.'
48
+ }
49
+ },
50
+ {
51
+ description: 'Counterpropose a wholly foreign-deleted paragraph found through --view rejected.',
52
+ operation: {
53
+ type: 'restore',
54
+ target: {
55
+ exactText: 'Deleted source paragraph.',
56
+ paragraphId: '3A2B3C4D',
57
+ revisionView: 'rejected'
58
+ },
59
+ modified: 'Restored and revised paragraph.'
60
+ }
61
+ }
62
+ ]);
63
+
64
+ export const CLI_COMMAND_HELP = Object.freeze({
65
+ version: {
66
+ summary: 'Report the machine contract version and capabilities.',
67
+ usage: 'docx-redline version',
68
+ options: [HELP],
69
+ notes: ['Wrappers should require only the capabilities they use.'],
70
+ examples: [{ command: 'docx-redline version' }]
71
+ },
72
+ inspect: {
73
+ summary: 'Inspect detailed paragraph, revision, comment, and structure metadata.',
74
+ usage: 'docx-redline inspect <file.docx> [options]',
75
+ options: INSPECTION_OPTIONS,
76
+ notes: [
77
+ 'Prefer a focused --search, --index, or --range. Use extract when only exact edit targets are needed.',
78
+ 'P<number>, index, and paragraph ordinals are machine references, not user-facing Word locations.'
79
+ ],
80
+ examples: [
81
+ { command: 'docx-redline inspect contract.docx --search "force majeure" --around 3' },
82
+ { command: 'docx-redline inspect contract.docx --range 10:25 --view rejected' }
83
+ ]
84
+ },
85
+ extract: {
86
+ summary: 'Return compact exact targets and human-facing legal locations.',
87
+ usage: 'docx-redline extract <file.docx> [options]',
88
+ options: INSPECTION_OPTIONS,
89
+ notes: [
90
+ 'Search is always case-insensitive.',
91
+ 'Copy exactText plus paragraphId or fingerprint into operations; do not cite P<number> to users.',
92
+ 'Broad results are paginated; follow selection.nextAfter or pass --all deliberately.'
93
+ ],
94
+ examples: [
95
+ { command: 'docx-redline extract contract.docx --search "force majeure" --around 3' },
96
+ { command: 'docx-redline extract contract.docx --range 10:25' }
97
+ ]
98
+ },
99
+ preflight: {
100
+ summary: 'Check an operation batch without mutating or writing a document.',
101
+ usage: 'docx-redline preflight <file.docx> --operations <file.json|-> [options]',
102
+ options: [
103
+ HELP,
104
+ option('operations', '--operations <file.json|->, --operations-file <file>', 'Read an operation array/envelope from a UTF-8 file or stdin.'),
105
+ AUTHOR,
106
+ option('strictTargets', '--strict-targets', 'Require strict target descriptors.'),
107
+ option('target', '--target <text>', 'Inline one-operation target text.'),
108
+ option('modified', '--modified <text>', 'Complete desired accepted-view target content.'),
109
+ option('comment', '--comment <text>', 'Create an inline comment operation.'),
110
+ option('textToComment', '--text-to-comment <text>', 'Anchor an inline comment to an exact subspan.'),
111
+ option('targetRef', '--target-ref <N>', 'Disambiguate an inline target with a 1-based machine index.'),
112
+ option('existingRevisions', '--existing-revisions <policy>', 'Select the explicit existing-revision policy.')
113
+ ],
114
+ notes: ['Normal apply already performs validation; preflight is optional.'],
115
+ examples: [{ command: 'docx-redline preflight contract.docx --operations operations.json --author "Editor"' }]
116
+ },
117
+ apply: {
118
+ summary: 'Apply canonical document operations and write a derived DOCX.',
119
+ usage: 'docx-redline apply <file.docx> --operations <file.json|-> [options]',
120
+ options: [
121
+ HELP,
122
+ option('operations', '--operations <file.json|->, --operations-file <file>', 'Read an operation array/envelope from a UTF-8 file or serializer-backed stdin.'),
123
+ ...MUTATION_DESTINATION_OPTIONS,
124
+ option('noClobber', '--no-clobber', 'Alias of --no-overwrite.'),
125
+ option('expectedRevision', '--expected-revision <token|json>', 'Reject a stale package revision.'),
126
+ option('target', '--target <text>', 'Inline one-operation target text.'),
127
+ option('modified', '--modified <text>', 'Complete desired accepted-view target content.'),
128
+ option('comment', '--comment <text>', 'Create an inline comment operation.'),
129
+ option('textToComment', '--text-to-comment <text>', 'Anchor an inline comment to an exact subspan.'),
130
+ option('targetRef', '--target-ref <N>', 'Disambiguate an inline target with a 1-based machine index.'),
131
+ option('existingRevisions', '--existing-revisions <policy>', 'Select revision handling; cross-author slicing must be deliberate.'),
132
+ option('atomic', '--atomic[=true|false]', 'Choose all-or-nothing or progressive batch execution.'),
133
+ option('generateRedlines', '--generate-redlines[=true|false]', 'Control tracked-change generation.'),
134
+ option('noRedlines', '--no-redlines', 'Apply clean text without tracked-change markup.'),
135
+ option('requireComplete', '--require-complete', 'Return exit code 3 for progressive partial completion.'),
136
+ option('profile', '--profile agent', 'Require complete machine execution without selecting atomic/progressive or revision policy; explicit flags compose with it.'),
137
+ COMPACT
138
+ ],
139
+ notes: [
140
+ 'modified is complete desired accepted-view content, not only inserted words.',
141
+ 'Use a structured JSON file or serializer-backed stdin; never interpolate legal text through raw shell quoting.',
142
+ 'The source is never overwritten unless --in-place is explicit.',
143
+ 'The agent profile keeps progressive execution unless --atomic is explicit and does not change existing-revision policy.',
144
+ 'Do not accept/reject foreign revisions or remove comments without user authorization.',
145
+ 'Inspect completion, written, outputPath, every result, error.recovery, and retryPlan.'
146
+ ],
147
+ examples: APPLY_EXAMPLES
148
+ },
149
+ accept: {
150
+ summary: 'Accept tracked revisions by one author or all authors.',
151
+ usage: 'docx-redline accept <file.docx> (--author <name>|--all-authors) [options]',
152
+ options: [HELP, ...MUTATION_DESTINATION_OPTIONS, option('allAuthors', '--all-authors', ALL_AUTHORS.description), option('noClobber', '--no-clobber', 'Alias of --no-overwrite.'), COMPACT],
153
+ notes: ['Accepting foreign review content requires explicit user authorization.'],
154
+ examples: [{ command: 'docx-redline accept reviewed.docx --author "Editor"' }]
155
+ },
156
+ reject: {
157
+ summary: 'Reject tracked revisions by one author or all authors.',
158
+ usage: 'docx-redline reject <file.docx> (--author <name>|--all-authors) [options]',
159
+ options: [HELP, ...MUTATION_DESTINATION_OPTIONS, option('allAuthors', '--all-authors', ALL_AUTHORS.description), option('noClobber', '--no-clobber', 'Alias of --no-overwrite.'), COMPACT],
160
+ notes: ['Rejecting foreign review content requires explicit user authorization.'],
161
+ examples: [{ command: 'docx-redline reject reviewed.docx --author "Editor"' }]
162
+ },
163
+ 'delete-comments': {
164
+ summary: 'Delete comments by one author or all authors.',
165
+ usage: 'docx-redline delete-comments <file.docx> (--author <name>|--all-authors) [options]',
166
+ options: [HELP, ...MUTATION_DESTINATION_OPTIONS, option('allAuthors', '--all-authors', ALL_AUTHORS.description), option('noClobber', '--no-clobber', 'Alias of --no-overwrite.'), COMPACT],
167
+ notes: ['Removing reviewer comments requires explicit user authorization.'],
168
+ examples: [{ command: 'docx-redline delete-comments reviewed.docx --author "Reviewer"' }]
169
+ },
170
+ validate: {
171
+ summary: 'Validate revision markup and DOCX package wiring.',
172
+ usage: 'docx-redline validate <file.docx> [--baseline <file.docx>]',
173
+ options: [HELP, option('baseline', '--baseline <file.docx>', 'Report only validation issues introduced relative to a baseline package.')],
174
+ notes: ['Apply validates before writing; use this command for an explicit audit.'],
175
+ examples: [{ command: 'docx-redline validate reviewed.docx --baseline contract.docx' }]
176
+ }
177
+ });
178
+
179
+ export const CLI_COMMANDS = Object.freeze(Object.keys(CLI_COMMAND_HELP));
180
+
181
+ export function commandOptionKeys(command) {
182
+ return (CLI_COMMAND_HELP[command]?.options || []).map(item => item.key);
183
+ }
184
+
185
+ export function buildCliHelp(command = null) {
186
+ if (!command) {
187
+ return {
188
+ status: 'ok',
189
+ command: 'help',
190
+ usage: 'docx-redline <command> [file.docx] [options]',
191
+ commands: CLI_COMMANDS.map(name => ({ name, summary: CLI_COMMAND_HELP[name].summary })),
192
+ notes: ['Run docx-redline <command> --help for flags, semantics, and bounded examples.'],
193
+ documentation: ['AGENTS.md', 'docs/AGENT_FAST_START.md', 'docs/SKILL_AUTHORING.md', 'docs/schemas/document-operations.schema.json']
194
+ };
195
+ }
196
+ const entry = CLI_COMMAND_HELP[command];
197
+ if (!entry) return null;
198
+ return {
199
+ status: 'ok',
200
+ command: 'help',
201
+ forCommand: command,
202
+ summary: entry.summary,
203
+ usage: entry.usage,
204
+ options: entry.options.map(({ key: _key, ...publicOption }) => publicOption),
205
+ notes: entry.notes,
206
+ examples: entry.examples,
207
+ documentation: ['AGENTS.md', 'docs/AGENT_FAST_START.md', 'docs/SKILL_AUTHORING.md', 'docs/schemas/document-operations.schema.json']
208
+ };
209
+ }