@ansonlai/docx-redline-js 0.6.0 → 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.
package/AGENTS.md CHANGED
@@ -9,7 +9,7 @@ or an installed plugin bundle to infer public behavior.
9
9
  | Task | Start here |
10
10
  |---|---|
11
11
  | Edit or review a complete `.docx` | [Agent Fast Start](docs/AGENT_FAST_START.md) and the `docx-redline` CLI |
12
- | Build an agent/tool wrapper | [README wrapper example](README.md#example-agent-session-wrapper-development-only), then [knowledge base](docs/AGENT_KNOWLEDGE_BASE.md#designing-a-thin-agent-wrapper) |
12
+ | Build or update an agent skill/tool wrapper | [Skill Authoring Contract](docs/SKILL_AUTHORING.md), then [README wrapper example](README.md#example-agent-session-wrapper-development-only) |
13
13
  | Change paragraph/range reconciliation | `index.js` → `engine/oxml-engine.js` → selected `engine/*-mode.js` |
14
14
  | Change complete-document operations | `services/standalone-operation-runner.js` → `services/document-operation-*.js` |
15
15
  | Change DOCX ZIP or CLI behavior | `node/index.js`, `node/docx-document.js`, `node/cli.js` |
@@ -22,12 +22,12 @@ specific advanced operation, API, or recovery topic you need.
22
22
  ## Ordinary document edits
23
23
 
24
24
  Use one focused extraction and one apply call. With a structured wrapper, use
25
- its revision-bound target handles. For shell-only work, serialize operations to
26
- stdin and use the explicit agent profile:
25
+ its revision-bound target handles. For shell-only work, use a UTF-8 operations
26
+ file or serializer-backed stdin and the explicit agent profile:
27
27
 
28
28
  ```bash
29
- docx-redline extract contract.docx --range 10:30
30
- node emit-operations.mjs | docx-redline apply contract.docx --operations - --profile agent --output reviewed.docx
29
+ docx-redline extract contract.docx --search "termination" --around 3
30
+ node emit-operations.mjs | docx-redline apply contract.docx --operations - --profile agent --compact --output reviewed.docx
31
31
  ```
32
32
 
33
33
  For every ordinary text operation, `modified` is the complete desired
@@ -43,6 +43,10 @@ unchanged failed arguments. Do not accept/reject another reviewer's work or
43
43
  remove comments without explicit user authorization. The source is never
44
44
  overwritten unless `--in-place` is explicit.
45
45
 
46
+ The agent profile preserves progressive execution and the ordinary revision
47
+ policy. Add `--atomic` or `--existing-revisions slice-cross-author` only when
48
+ that policy is intended, and confirm the resolved `effectiveOptions`.
49
+
46
50
  Advanced restore, rejected-view insertion, list, table, formatting, comments,
47
51
  revision policies, and failure examples live in the
48
52
  [knowledge base](docs/AGENT_KNOWLEDGE_BASE.md#agent-document-workflow-cli) and
package/CHANGELOG.md CHANGED
@@ -13,6 +13,9 @@
13
13
 
14
14
  ### New Features
15
15
 
16
+ - **Self-describing and bounded agent discovery (WP-00–WP-02)**: CLI contract version 6 adds command-specific JSON help, canonical redline/comment/restore examples, case-insensitive contextual search with `--around`/`--context`/`-C`, direct-hit pagination through `--limit` and `--after`, and deliberate `--all` opt-out. Unscoped inspection is capped at 20 direct records and a 48 KiB soft budget without truncating any returned `exactText`; extraction now distinguishes matches from context and prioritizes human-facing provision/heading references over machine paragraph ordinals.
17
+ - **Composable agent safety and compact output (WP-03–WP-04)**: CLI contract version 7 replaces the atomic-coupled agent profile capability with `agent-safety-profile-v2`. `--profile agent` now enforces complete-success exits while preserving progressive execution and the ordinary revision policy; `--atomic` and `--existing-revisions` compose explicitly. Compact CLI JSON keeps receipts only in the authoritative top-level array, omits exact-match diagnostics, summarizes successful equivalent-whitespace matches, and supports one-line `--compact` output without changing Node or standalone-runner results.
18
+ - **Packaged skill-authoring contract (WP-05)**: `docs/SKILL_AUTHORING.md` separates automation invariants from transaction/revision policy, file-versus-stdin transport, reviewer preferences, recovery-envelope behavior, and human-facing presentation. It requires contract/capability negotiation, focused contextual extraction, and library delegation while retaining the visible `AI Redliner` fallback without a new author guard.
16
19
  - **Batch-start source binding (WP-04)**: Strong document-operation targets are compiled once against the immutable source DOM and carried through structural index/fingerprint drift with rollback-aware session identities. Independent edits no longer need manual bottom-up sorting; same-source writes, incompatible revision/format combinations, and unsafe capture fan-out fail before mutation with causal conflict codes. Unique references to paragraph text created elsewhere in the same batch become explicit internal capture dependencies.
17
20
  - **Machine-actionable recovery (WP-05)**: Operation, package-facade, example-session, and compact CLI failures now carry recovery envelope version 1 with stable stage/category/action metadata, bounded corrective context, and authorization flags. Failed and partial mutation results include an explicit original-vs-output retry plan and operation indexes. `apply --require-complete` exits with code 3 for partial work while preserving legacy behavior without the flag.
18
21
  - **Compact shell-agent protocol (WP-06)**: CLI contract version 5 adds `operations-stdin` and `agent-profile-v1`. `--operations -` accepts UTF-8 operation arrays/envelopes without a temporary operations file, while explicit `--profile agent` combines atomic rollback and complete-success exit behavior and reports its effective settings. `AGENTS.md` is now a 527-word routing card, and the 349-word `docs/AGENT_FAST_START.md` carries the ordinary edit contract. Ambient project configuration was deliberately deferred because it would save only the profile flag while adding hidden state.
package/README.md CHANGED
@@ -144,10 +144,13 @@ import { inspectDocumentParts } from '@ansonlai/docx-redline-js';
144
144
  const inventory = inspectDocumentParts({ documentXml, commentsXml, numberingXml });
145
145
  ```
146
146
 
147
- Inspection returns exact paragraph text, target IDs/fingerprints, headings,
148
- table/list context, revision authors, and joined comment anchors. Filters such
149
- as `search`, `indexes`, `range`, `revisedOnly`, `inTable`, and `skipEmpty`
150
- limit output. `revisionView` accepts `accepted`, `rejected`, or `current`.
147
+ Inspection returns exact paragraph text, target IDs/fingerprints, headings,
148
+ table/list context, revision authors, and joined comment anchors. Filters such
149
+ as `search`, `indexes`, `range`, `revisedOnly`, `inTable`, and `skipEmpty`
150
+ limit output. Search is a case-insensitive substring match; `around` adds nearby
151
+ drafting context, while `limit` and `after` page direct hits. `revisionView`
152
+ accepts `accepted`, `rejected`, or `current`. Returned paragraphs identify
153
+ matches versus context and include `humanReference` for user-facing summaries.
151
154
 
152
155
  For complete `.docx` buffers in Node:
153
156
 
@@ -211,11 +214,11 @@ latency. Checked comparative results are in the
211
214
 
212
215
  ### Agent CLI
213
216
 
214
- ```bash
215
- docx-redline extract contract.docx --range 10:30
216
- docx-redline preflight contract.docx --operations operations.json --author "Editor"
217
- docx-redline apply contract.docx --operations operations.json --author "Editor" --output reviewed.docx
218
- docx-redline validate reviewed.docx
217
+ ```bash
218
+ docx-redline extract contract.docx --search "termination" --around 3
219
+ docx-redline preflight contract.docx --operations operations.json --author "Editor"
220
+ docx-redline apply contract.docx --operations operations.json --author "Editor" --output reviewed.docx
221
+ docx-redline validate reviewed.docx
219
222
  ```
220
223
 
221
224
  ```bash
@@ -232,7 +235,7 @@ docx-redline apply contract.docx --target "Another author's clause" --modified "
232
235
  docx-redline apply contract.docx --operations operations.json --atomic --require-complete --output reviewed.docx
233
236
 
234
237
  # Agent shell path: JSON is emitted by a serializer, not interpolated by the shell
235
- node emit-operations.mjs | docx-redline apply contract.docx --operations - --profile agent --output reviewed.docx
238
+ node emit-operations.mjs | docx-redline apply contract.docx --operations - --profile agent --compact --output reviewed.docx
236
239
  ```
237
240
 
238
241
  All commands emit JSON on stdout. `apply` defaults:
@@ -241,18 +244,33 @@ All commands emit JSON on stdout. `apply` defaults:
241
244
  - **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.
242
245
  - **Transactionality**: Defaults to `atomic: false` (applies valid operations and reports any failures). Pass `--atomic` for all-or-nothing rollback on any operation error.
243
246
  - **Complete-success exit**: Pass `--require-complete` when a progressive `partial` result must exit with code `3`; errors exit with code `2`. Without the flag, partial results retain the legacy zero exit code, so always inspect `completion`.
244
- - **Agent profile**: `--profile agent` explicitly enables atomic rollback and complete-success exit behavior while retaining strict targeting, validation, tracked changes, and `merge-same-author`. It reports the resolved `effectiveOptions`; explicit flags take precedence.
247
+ - **Agent profile**: `--profile agent` enables complete-success exit behavior while retaining the ordinary progressive transaction and existing-revision defaults. Compose it with `--atomic` or an explicit `--existing-revisions` policy when intended, and verify the resolved `effectiveOptions`.
245
248
  - **Operations from stdin**: `--operations -` reads the same array or `{ operations, expectedRevision }` envelope accepted from a file. Feed it from a JSON serializer or structured process API, not shell-interpolated legal text.
249
+ - **Compact stdout**: `--compact` emits one-line mutation JSON. It changes serialization only, not operation semantics.
246
250
  - **Tracked changes**: Defaults to `generateRedlines: true`. Pass `--no-redlines` when clean direct text edits are desired.
247
251
  - **Inline edits**: Use `--target <text>` with `--modified <text>` or `--comment <text>` for quick one-liners without creating a JSON file.
248
- - **Compact mutation results**: `apply`, `accept`, `reject`, and `delete-comments` omit full OOXML/package payloads and inspection text from stdout. They report `written`, `outputPath`, per-operation results and receipts, compact validation counts, and a derived `completion` boolean. Use `validate` when full issue arrays are needed.
252
+ - **Compact mutation results**: `apply`, `accept`, `reject`, and `delete-comments` omit full OOXML/package payloads and inspection text from stdout. CLI operation results omit duplicate nested receipt bodies; the ordered top-level `receipts` array is authoritative. Successful exact target-match diagnostics are omitted and equivalent-whitespace matches retain only mode/count. Node and standalone-runner results remain unchanged. Use `validate` when full issue arrays are needed.
249
253
 
250
- `docx-redline version` reports contract version 5 and the additive
251
- `batch-start-source-binding`, `recovery-envelope-v1`, and
252
- `require-complete-exit`, `operations-stdin`, and `agent-profile-v1`
253
- capabilities. Wrappers should negotiate only the capabilities they use.
254
-
255
- See the [compact agent fast start](./docs/AGENT_FAST_START.md) and the
254
+ Inspection commands default to 20 direct matches and a 48 KiB soft response
255
+ budget when no explicit positional scope is supplied. Use `--limit` with
256
+ `--after <paragraph-index>` to continue, `--around N` (aliases `--context` and
257
+ `-C`) to include nearby paragraphs, and `--all` only for deliberate unbounded
258
+ inspection. Every retained target is complete; an individually oversized
259
+ paragraph is returned whole with an `oversizeItem` marker. Machine `index` and
260
+ `ref` fields are for targeting and pagination, not user-facing Word locations;
261
+ use `humanReference`, `provision`, or `nearestHeading` in reports.
262
+
263
+ `docx-redline version` reports contract version 7 and the additive
264
+ `command-help-v1`, `inspection-context-v1`, `bounded-inspection-v1`,
265
+ `human-document-references-v1`, `batch-start-source-binding`,
266
+ `recovery-envelope-v1`, `require-complete-exit`, `operations-stdin`,
267
+ `agent-safety-profile-v2`, `deduplicated-cli-receipts`, and
268
+ `compact-cli-json-v1` capabilities. Wrappers should negotiate only the
269
+ capabilities they use. Run `docx-redline <command> --help` for that command's
270
+ machine-readable options, behavior, exit codes, and compact examples.
271
+
272
+ See the [compact agent fast start](./docs/AGENT_FAST_START.md), the
273
+ [skill/harness authoring contract](./docs/SKILL_AUTHORING.md), and the
256
274
  [operation JSON Schema](docs/schemas/document-operations.schema.json).
257
275
 
258
276
  ### Configuration (call once at startup)
@@ -554,10 +572,14 @@ guidance. Callers should pass `{ strictTargets: true }` and use strict descripto
554
572
 
555
573
  ### Mutation Receipts
556
574
 
557
- Both single-operation (`applyOperationToDocumentXml`) and batch
558
- (`applyOperationsToDocumentXml`) results expose commit-aware **Mutation Receipts**
559
- (`result.receipt` on single results and per-item `results[i].receipt`, plus `result.receipts`
560
- for the full batch).
575
+ Both single-operation (`applyOperationToDocumentXml`) and batch
576
+ (`applyOperationsToDocumentXml`) results expose commit-aware **Mutation Receipts**
577
+ (`result.receipt` on single results and per-item `results[i].receipt`, plus `result.receipts`
578
+ for the full batch).
579
+
580
+ This full receipt shape is retained by the Node facade and standalone runner.
581
+ Only compact CLI JSON removes duplicate `results[i].receipt` bodies and keeps
582
+ the ordered top-level `receipts` array.
561
583
 
562
584
  ```js
563
585
  const result = await applyOperationsToDocumentXml(documentXml, operations, 'Agent');
@@ -1,4 +1,4 @@
1
- // @ansonlai/docx-redline-js v0.6.0 — https://github.com/AnsonLai/docx-redline-js
1
+ // @ansonlai/docx-redline-js v0.6.1 — https://github.com/AnsonLai/docx-redline-js
2
2
  var __create = Object.create;
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -11466,6 +11466,70 @@ function collectDocumentCommentAnchors(paragraphNodes, revisionView) {
11466
11466
  });
11467
11467
  return anchors;
11468
11468
  }
11469
+ function selectInspectionParagraphs(allParagraphs, options) {
11470
+ let matches = allParagraphs;
11471
+ if (options.revisedOnly) matches = matches.filter((item) => item.hasRevisions);
11472
+ if (options.inTable != null) matches = matches.filter((item) => item.inTable === !!options.inTable);
11473
+ if (options.skipEmpty) matches = matches.filter((item) => item.text.length > 0);
11474
+ if (options.search) {
11475
+ const needle = String(options.search).toLowerCase();
11476
+ matches = matches.filter((item) => item.text.toLowerCase().includes(needle));
11477
+ }
11478
+ if (Array.isArray(options.indexes)) {
11479
+ const indexes = new Set(options.indexes);
11480
+ matches = matches.filter((item) => indexes.has(item.index));
11481
+ }
11482
+ let rangeStart = 1;
11483
+ let rangeEnd = allParagraphs.length;
11484
+ if (options.range) {
11485
+ rangeStart = Number(options.range.start ?? options.range[0]);
11486
+ rangeEnd = Number(options.range.end ?? options.range[1]);
11487
+ matches = matches.filter((item) => item.index >= rangeStart && item.index <= rangeEnd);
11488
+ }
11489
+ const totalMatches = matches.length;
11490
+ const after = Number.isInteger(options.after) && options.after > 0 ? options.after : null;
11491
+ const remaining = after == null ? matches : matches.filter((item) => item.index > after);
11492
+ const limit = Number.isInteger(options.limit) && options.limit > 0 ? options.limit : null;
11493
+ const selectedMatches = limit == null ? remaining : remaining.slice(0, limit);
11494
+ const truncated = selectedMatches.length < remaining.length;
11495
+ const around = Number.isInteger(options.around) && options.around > 0 ? options.around : 0;
11496
+ const exposeSelection = !!options.search || around > 0 || limit != null || after != null;
11497
+ let paragraphs = selectedMatches;
11498
+ if (around > 0 && options.search) {
11499
+ const directIndexes = new Set(selectedMatches.map((item) => item.index));
11500
+ const contextFor = /* @__PURE__ */ new Map();
11501
+ for (const match of selectedMatches) {
11502
+ const start = Math.max(rangeStart, match.index - around);
11503
+ const end = Math.min(rangeEnd, match.index + around);
11504
+ for (let index = start; index <= end; index++) {
11505
+ if (directIndexes.has(index)) continue;
11506
+ const owners = contextFor.get(index) || [];
11507
+ owners.push(match.index);
11508
+ contextFor.set(index, owners);
11509
+ }
11510
+ }
11511
+ const returnedIndexes = /* @__PURE__ */ new Set([...directIndexes, ...contextFor.keys()]);
11512
+ paragraphs = allParagraphs.filter((item) => returnedIndexes.has(item.index)).map((item) => directIndexes.has(item.index) ? { ...item, selectionRole: "match" } : { ...item, selectionRole: "context", contextFor: contextFor.get(item.index) || [] });
11513
+ } else if (exposeSelection) {
11514
+ paragraphs = selectedMatches.map((item) => ({ ...item, selectionRole: "match" }));
11515
+ }
11516
+ return {
11517
+ paragraphs,
11518
+ ...exposeSelection ? {
11519
+ selection: {
11520
+ ...options.search ? { search: String(options.search), caseSensitive: false } : {},
11521
+ totalMatches,
11522
+ returnedMatches: selectedMatches.length,
11523
+ returnedParagraphs: paragraphs.length,
11524
+ truncated,
11525
+ nextAfter: truncated && selectedMatches.length > 0 ? selectedMatches[selectedMatches.length - 1].index : null,
11526
+ ...limit != null ? { limit } : {},
11527
+ ...after != null ? { after } : {},
11528
+ ...around > 0 ? { around } : {}
11529
+ }
11530
+ } : {}
11531
+ };
11532
+ }
11469
11533
  function inspectDocumentParts(parts, options = {}) {
11470
11534
  const documentPart = parseXml2(parts?.documentXml, "word/document.xml", true);
11471
11535
  if (documentPart.error) return { status: "error", error: documentPart.error, paragraphs: [], comments: [], warnings: [] };
@@ -11495,7 +11559,8 @@ function inspectDocumentParts(parts, options = {}) {
11495
11559
  const index = zeroIndex + 1;
11496
11560
  const provision = list?.label && list.format !== "bullet" ? list.label : null;
11497
11561
  const headingText = nearestHeading?.text || null;
11498
- const humanReference = [provision, headingText, text.slice(0, options.excerptLength || 120)].filter(Boolean).join(" \u2014 ");
11562
+ const excerpt = text.slice(0, options.excerptLength || 120);
11563
+ const humanReference = level ? text : provision ? [provision, headingText].filter(Boolean).join(" \u2014 ") : headingText ? [headingText, excerpt].filter(Boolean).join(" \u2014 ") : excerpt;
11499
11564
  const segments = extractParagraphRevisionSegments(paragraph);
11500
11565
  return {
11501
11566
  index,
@@ -11505,8 +11570,9 @@ function inspectDocumentParts(parts, options = {}) {
11505
11570
  revisionView,
11506
11571
  text,
11507
11572
  exactText: text,
11508
- excerpt: text.slice(0, options.excerptLength || 120),
11573
+ excerpt,
11509
11574
  humanReference,
11575
+ provision,
11510
11576
  inTable: hasAncestor(paragraph, "tc"),
11511
11577
  table: structure.table,
11512
11578
  styleId,
@@ -11527,22 +11593,8 @@ function inspectDocumentParts(parts, options = {}) {
11527
11593
  definition.anchoredText ?? (definition.anchoredText = commentAnchors.get(id) || paragraph.text);
11528
11594
  comments.set(id, definition);
11529
11595
  }
11530
- if (options.revisedOnly) paragraphs = paragraphs.filter((item) => item.hasRevisions);
11531
- if (options.inTable != null) paragraphs = paragraphs.filter((item) => item.inTable === !!options.inTable);
11532
- if (options.skipEmpty) paragraphs = paragraphs.filter((item) => item.text.length > 0);
11533
- if (options.search) {
11534
- const needle = String(options.search).toLowerCase();
11535
- paragraphs = paragraphs.filter((item) => item.text.toLowerCase().includes(needle));
11536
- }
11537
- if (Array.isArray(options.indexes)) {
11538
- const indexes = new Set(options.indexes);
11539
- paragraphs = paragraphs.filter((item) => indexes.has(item.index));
11540
- }
11541
- if (options.range) {
11542
- const start = Number(options.range.start ?? options.range[0]);
11543
- const end = Number(options.range.end ?? options.range[1]);
11544
- paragraphs = paragraphs.filter((item) => item.index >= start && item.index <= end);
11545
- }
11596
+ const selected = selectInspectionParagraphs(paragraphs, options);
11597
+ paragraphs = selected.paragraphs;
11546
11598
  const allRevisionAuthors = [...new Set(paragraphs.flatMap((item) => item.revisionAuthors))].sort();
11547
11599
  const coveredEntries = extractDocumentPartsEntries(parts);
11548
11600
  const coveredParts = coveredEntries.map((e) => e.name).sort();
@@ -11559,6 +11611,7 @@ function inspectDocumentParts(parts, options = {}) {
11559
11611
  revisionToken,
11560
11612
  coveredParts,
11561
11613
  paragraphs,
11614
+ ...selected.selection ? { selection: selected.selection } : {},
11562
11615
  comments: [...comments.values()],
11563
11616
  revisionAuthors: allRevisionAuthors,
11564
11617
  commentAuthors: [...new Set([...comments.values()].map((item) => item.author).filter(Boolean))].sort(),