@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 +9 -5
- package/CHANGELOG.md +3 -0
- package/README.md +44 -22
- package/dist/docx-redline-js.esm.js +72 -19
- package/dist/docx-redline-js.esm.js.map +2 -2
- package/dist/docx-redline-js.esm.min.js +44 -44
- package/dist/docx-redline-js.esm.min.js.map +3 -3
- package/docs/AGENT_FAST_START.md +7 -7
- package/docs/AGENT_KNOWLEDGE_BASE.md +33 -23
- package/docs/SKILL_AUTHORING.md +126 -0
- package/docs/TESTING.md +17 -2
- package/docs/validation-reports/2026-09-12-agent-cli-discovery-baseline.md +56 -0
- package/docs/validation-reports/2026-09-12-agent-protocol-rollout.md +7 -3
- package/docs/validation-reports/2026-09-13-agent-cli-efficiency-rollout.md +86 -0
- package/index.d.ts +11 -2
- package/node/cli-help.js +209 -0
- package/node/cli.js +221 -47
- package/package.json +6 -1
- package/services/document-inspection.js +84 -8
|
@@ -183,6 +183,80 @@ function collectDocumentCommentAnchors(paragraphNodes, revisionView) {
|
|
|
183
183
|
return anchors;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
function selectInspectionParagraphs(allParagraphs, options) {
|
|
187
|
+
let matches = allParagraphs;
|
|
188
|
+
if (options.revisedOnly) matches = matches.filter(item => item.hasRevisions);
|
|
189
|
+
if (options.inTable != null) matches = matches.filter(item => item.inTable === !!options.inTable);
|
|
190
|
+
if (options.skipEmpty) matches = matches.filter(item => item.text.length > 0);
|
|
191
|
+
if (options.search) {
|
|
192
|
+
const needle = String(options.search).toLowerCase();
|
|
193
|
+
matches = matches.filter(item => item.text.toLowerCase().includes(needle));
|
|
194
|
+
}
|
|
195
|
+
if (Array.isArray(options.indexes)) {
|
|
196
|
+
const indexes = new Set(options.indexes);
|
|
197
|
+
matches = matches.filter(item => indexes.has(item.index));
|
|
198
|
+
}
|
|
199
|
+
let rangeStart = 1;
|
|
200
|
+
let rangeEnd = allParagraphs.length;
|
|
201
|
+
if (options.range) {
|
|
202
|
+
rangeStart = Number(options.range.start ?? options.range[0]);
|
|
203
|
+
rangeEnd = Number(options.range.end ?? options.range[1]);
|
|
204
|
+
matches = matches.filter(item => item.index >= rangeStart && item.index <= rangeEnd);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const totalMatches = matches.length;
|
|
208
|
+
const after = Number.isInteger(options.after) && options.after > 0 ? options.after : null;
|
|
209
|
+
const remaining = after == null ? matches : matches.filter(item => item.index > after);
|
|
210
|
+
const limit = Number.isInteger(options.limit) && options.limit > 0 ? options.limit : null;
|
|
211
|
+
const selectedMatches = limit == null ? remaining : remaining.slice(0, limit);
|
|
212
|
+
const truncated = selectedMatches.length < remaining.length;
|
|
213
|
+
const around = Number.isInteger(options.around) && options.around > 0 ? options.around : 0;
|
|
214
|
+
const exposeSelection = !!options.search || around > 0 || limit != null || after != null;
|
|
215
|
+
|
|
216
|
+
let paragraphs = selectedMatches;
|
|
217
|
+
if (around > 0 && options.search) {
|
|
218
|
+
const directIndexes = new Set(selectedMatches.map(item => item.index));
|
|
219
|
+
const contextFor = new Map();
|
|
220
|
+
for (const match of selectedMatches) {
|
|
221
|
+
const start = Math.max(rangeStart, match.index - around);
|
|
222
|
+
const end = Math.min(rangeEnd, match.index + around);
|
|
223
|
+
for (let index = start; index <= end; index++) {
|
|
224
|
+
if (directIndexes.has(index)) continue;
|
|
225
|
+
const owners = contextFor.get(index) || [];
|
|
226
|
+
owners.push(match.index);
|
|
227
|
+
contextFor.set(index, owners);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const returnedIndexes = new Set([...directIndexes, ...contextFor.keys()]);
|
|
231
|
+
paragraphs = allParagraphs
|
|
232
|
+
.filter(item => returnedIndexes.has(item.index))
|
|
233
|
+
.map(item => directIndexes.has(item.index)
|
|
234
|
+
? { ...item, selectionRole: 'match' }
|
|
235
|
+
: { ...item, selectionRole: 'context', contextFor: contextFor.get(item.index) || [] });
|
|
236
|
+
} else if (exposeSelection) {
|
|
237
|
+
paragraphs = selectedMatches.map(item => ({ ...item, selectionRole: 'match' }));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
paragraphs,
|
|
242
|
+
...(exposeSelection ? {
|
|
243
|
+
selection: {
|
|
244
|
+
...(options.search ? { search: String(options.search), caseSensitive: false } : {}),
|
|
245
|
+
totalMatches,
|
|
246
|
+
returnedMatches: selectedMatches.length,
|
|
247
|
+
returnedParagraphs: paragraphs.length,
|
|
248
|
+
truncated,
|
|
249
|
+
nextAfter: truncated && selectedMatches.length > 0
|
|
250
|
+
? selectedMatches[selectedMatches.length - 1].index
|
|
251
|
+
: null,
|
|
252
|
+
...(limit != null ? { limit } : {}),
|
|
253
|
+
...(after != null ? { after } : {}),
|
|
254
|
+
...(around > 0 ? { around } : {})
|
|
255
|
+
}
|
|
256
|
+
} : {})
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
186
260
|
/** Read-only, stable document-parts inspection for agents and package adapters. */
|
|
187
261
|
export function inspectDocumentParts(parts, options = {}) {
|
|
188
262
|
const documentPart = parseXml(parts?.documentXml, 'word/document.xml', true);
|
|
@@ -213,12 +287,17 @@ export function inspectDocumentParts(parts, options = {}) {
|
|
|
213
287
|
const index = zeroIndex + 1;
|
|
214
288
|
const provision = list?.label && list.format !== 'bullet' ? list.label : null;
|
|
215
289
|
const headingText = nearestHeading?.text || null;
|
|
216
|
-
const
|
|
290
|
+
const excerpt = text.slice(0, options.excerptLength || 120);
|
|
291
|
+
const humanReference = level
|
|
292
|
+
? text
|
|
293
|
+
: (provision
|
|
294
|
+
? [provision, headingText].filter(Boolean).join(' — ')
|
|
295
|
+
: (headingText ? [headingText, excerpt].filter(Boolean).join(' — ') : excerpt));
|
|
217
296
|
const segments = extractParagraphRevisionSegments(paragraph);
|
|
218
297
|
return {
|
|
219
298
|
index, ref: `P${index}`, paragraphId: getParagraphId(paragraph),
|
|
220
299
|
fingerprint: createParagraphFingerprint(paragraph, { text, index, revisionView }), revisionView,
|
|
221
|
-
text, exactText: text, excerpt
|
|
300
|
+
text, exactText: text, excerpt, humanReference, provision, inTable: hasAncestor(paragraph, 'tc'), table: structure.table,
|
|
222
301
|
styleId, headingLevel: level, nearestHeading, list, structuralReferences: structure.references, hasRevisions: authors.length > 0, revisionAuthors: authors, commentIds: ids,
|
|
223
302
|
segments
|
|
224
303
|
};
|
|
@@ -229,12 +308,8 @@ export function inspectDocumentParts(parts, options = {}) {
|
|
|
229
308
|
definition.anchoredText ??= commentAnchors.get(id) || paragraph.text;
|
|
230
309
|
comments.set(id, definition);
|
|
231
310
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (options.skipEmpty) paragraphs = paragraphs.filter(item => item.text.length > 0);
|
|
235
|
-
if (options.search) { const needle = String(options.search).toLowerCase(); paragraphs = paragraphs.filter(item => item.text.toLowerCase().includes(needle)); }
|
|
236
|
-
if (Array.isArray(options.indexes)) { const indexes = new Set(options.indexes); paragraphs = paragraphs.filter(item => indexes.has(item.index)); }
|
|
237
|
-
if (options.range) { const start = Number(options.range.start ?? options.range[0]); const end = Number(options.range.end ?? options.range[1]); paragraphs = paragraphs.filter(item => item.index >= start && item.index <= end); }
|
|
311
|
+
const selected = selectInspectionParagraphs(paragraphs, options);
|
|
312
|
+
paragraphs = selected.paragraphs;
|
|
238
313
|
const allRevisionAuthors = [...new Set(paragraphs.flatMap(item => item.revisionAuthors))].sort();
|
|
239
314
|
const coveredEntries = extractDocumentPartsEntries(parts);
|
|
240
315
|
const coveredParts = coveredEntries.map(e => e.name).sort();
|
|
@@ -251,6 +326,7 @@ export function inspectDocumentParts(parts, options = {}) {
|
|
|
251
326
|
revisionToken,
|
|
252
327
|
coveredParts,
|
|
253
328
|
paragraphs,
|
|
329
|
+
...(selected.selection ? { selection: selected.selection } : {}),
|
|
254
330
|
comments: [...comments.values()],
|
|
255
331
|
revisionAuthors: allRevisionAuthors,
|
|
256
332
|
commentAuthors: [...new Set([...comments.values()].map(item => item.author).filter(Boolean))].sort(),
|