Agent Framework

DocxReviewer API

Word-like API for AI document review — add comments, suggest replacements, accept/reject changes. Auto-generated from source.

@eigenpal/docx-editor-agents — headless, Word-like API for AI document review.

npm install @eigenpal/docx-editor-agents

Quick Start

import { DocxReviewer } from '@eigenpal/docx-editor-agents';
 
const reviewer = await DocxReviewer.fromBuffer(buffer, 'AI Reviewer');
 
// Read — plain text with paragraph indices for LLM prompts
const text = reviewer.getContentAsText();
// [0] (h1) Introduction
// [1] The liability cap is $50k per incident.
// [2] (table, row 1, col 1) Revenue ...
 
// Comment on a paragraph (anchors to whole paragraph)
reviewer.addComment(1, 'This cap seems too low.');
 
// Replace text (creates tracked change)
reviewer.replace(1, '$50k', '$500k');
 
// Or batch from an LLM JSON response
reviewer.applyReview({
  comments: [{ paragraphIndex: 1, text: 'Too low.' }],
  proposals: [{ paragraphIndex: 1, search: '$50k', replaceWith: '$500k' }],
});
 
// Export
const output = await reviewer.toBuffer();

DocxReviewer Methods

Author is set once via DocxReviewer.fromBuffer(buffer, 'Author Name') — no need to repeat it on every call.

Read

MethodDescription
DocxReviewer.fromBuffer(buffer: ArrayBuffer, author?: string | undefined): Promise<DocxReviewer>Create a reviewer from a DOCX file buffer.
getContent(options?: GetContentOptions | undefined): ContentBlock[]Get document content as structured blocks (headings, paragraphs, tables, lists).
getContentAsText(options?: GetContentOptions | undefined): stringGet document content as plain text for LLM prompts. Each paragraph is prefixed with its index: [0] text, [1] text, etc. Table cells include position: [5] (table, row 1, col 2) cell text. Avoids JSON quote-escaping issues — LLMs can copy text verbatim.

Discover

MethodDescription
getChanges(filter?: ChangeFilter | undefined): ReviewChange[]Get all tracked changes in the document.
getComments(filter?: CommentFilter | undefined): ReviewComment[]Get all comments with their replies.

Comment

MethodDescription
addComment(paragraphIndex: number, text: string): numberAdd a comment on a paragraph.
addComment(options: AddCommentOptions): number(overload)
replyTo(commentId: number, text: string): numberReply to an existing comment.
replyTo(commentId: number, options: ReplyOptions): number(overload)

Propose Changes

MethodDescription
replace(paragraphIndex: number, search: string, replaceWith: string): voidReplace text in a paragraph. Creates a tracked change (deletion + insertion).
replace(options: ProposeReplacementOptions): void(overload)
proposeInsertion(options: ProposeInsertionOptions): voidInsert text as a tracked change.
proposeDeletion(options: ProposeDeletionOptions): voidDelete text as a tracked change.

Resolve

MethodDescription
acceptChange(id: number): voidAccept a tracked change by its revision ID.
rejectChange(id: number): voidReject a tracked change by its revision ID.
acceptAll(): numberAccept all tracked changes. Returns count accepted.
rejectAll(): numberReject all tracked changes. Returns count rejected.

Batch

MethodDescription
applyReview(ops: BatchReviewOptions): BatchResultApply multiple review operations in one call. Uses the reviewer's default author. Individual failures are collected, not thrown.

Export

MethodDescription
toDocument(): DocumentGet the modified Document model.
toBuffer(): Promise<ArrayBuffer>Serialize back to a DOCX buffer. Requires the original buffer.

Types

GetContentOptions

FieldTypeDescription
fromIndex?number
toIndex?number
includeTrackedChanges?booleanAnnotate tracked changes inline. Default: true
includeCommentAnchors?booleanAnnotate comments inline. Default: true

BatchReviewOptions

FieldTypeDescription
accept?number[]
reject?number[]
comments?AddCommentOptions[]
replies?(ReplyOptions & \{ commentId: number; \})[]
proposals?ProposeReplacementOptions[]

BatchResult

FieldTypeDescription
acceptednumber
rejectednumber
commentsAddednumber
repliesAddednumber
proposalsAddednumber
errorsBatchError[]

BatchError

FieldTypeDescription
operationstring
id?number
search?string
errorstring

ReviewChange

FieldTypeDescription
idnumber
type'insertion' | 'deletion' | 'moveFrom' | 'moveTo'
authorstring
datestring | null
textstring
contextstring
paragraphIndexnumber

ReviewComment

FieldTypeDescription
idnumber
authorstring
datestring | null
textstring
anchoredTextstring
paragraphIndexnumber
repliesReviewCommentReply[]
doneboolean

AddCommentOptions

FieldTypeDescription
paragraphIndexnumber
textstring
author?string
search?stringOptional: anchor to specific text. Omit to anchor whole paragraph.

ProposeReplacementOptions

FieldTypeDescription
paragraphIndexnumber
searchstring
replaceWithstring
author?string

Examples

Next.js API Route — AI Document Review

Upload a DOCX, send it to an LLM for review, apply comments and tracked changes, return the modified file.

// app/api/review/route.ts
import { NextRequest, NextResponse } from 'next/server';
import OpenAI from 'openai';
import { DocxReviewer } from '@eigenpal/docx-editor-agents';
 
const openai = new OpenAI();
 
export async function POST(request: NextRequest) {
  const formData = await request.formData();
  const file = formData.get('file') as File;
  if (!file) return NextResponse.json({ error: 'No file' }, { status: 400 });
 
  // 1. Parse DOCX and read as plain text
  const reviewer = await DocxReviewer.fromBuffer(await file.arrayBuffer(), 'AI Reviewer');
  const text = reviewer.getContentAsText();
 
  // 2. Send to LLM
  const response = await openai.chat.completions.create({
    model: 'gpt-4o',
    response_format: { type: 'json_object' },
    messages: [
      {
        role: 'system',
        content: `Review this document. Return JSON:
{
  "comments": [{ "paragraphIndex": <number>, "text": "<feedback>" }],
  "replacements": [{ "paragraphIndex": <number>, "search": "<phrase>", "replaceWith": "<better>" }]
}
paragraphIndex must match a [number] from the document.`
      },
      { role: 'user', content: text }
    ],
  });
 
  const actions = JSON.parse(response.choices[0]?.message?.content || '{}');
 
  // 3. Apply review — author is set once on the reviewer
  reviewer.applyReview({
    comments: actions.comments,
    proposals: actions.replacements,
  });
 
  // 4. Return modified DOCX
  const output = await reviewer.toBuffer();
  return new NextResponse(output, {
    headers: {
      'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    },
  });
}

Node.js Script — Batch Review

Review multiple DOCX files from the command line.

import { readFileSync, writeFileSync } from 'fs';
import { DocxReviewer } from '@eigenpal/docx-editor-agents';
 
const buffer = readFileSync('contract.docx');
const reviewer = await DocxReviewer.fromBuffer(buffer.buffer, 'Legal Bot');
 
// Read content
const content = reviewer.getContentAsText();
console.log(content);
// [0] (h1) Service Agreement
// [1] The liability cap is $50k per incident.
// [2] (table, row 1, col 1) Party A ...
 
// Add comments and changes directly
reviewer.addComment(1, 'Liability cap is below industry standard.');
reviewer.replace(1, '$50k', '$500k');
 
// Export
const output = await reviewer.toBuffer();
writeFileSync('contract-reviewed.docx', Buffer.from(output));

Using with Claude (Anthropic)

import Anthropic from '@anthropic-ai/sdk';
import { DocxReviewer } from '@eigenpal/docx-editor-agents';
 
const client = new Anthropic();
const reviewer = await DocxReviewer.fromBuffer(buffer, 'Claude Reviewer');
 
const response = await client.messages.create({
  model: 'claude-sonnet-4-20250514',
  max_tokens: 4096,
  messages: [{
    role: 'user',
    content: `Review this document and return JSON with comments and replacements:\n\n${reviewer.getContentAsText()}`
  }],
});
 
const actions = JSON.parse(response.content[0].text);
reviewer.applyReview({ comments: actions.comments, proposals: actions.replacements });

Full working example: examples/agent-use-demo


Source: @eigenpal/docx-editor-agents