@reneturcios/docxredline 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +148 -2
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +120 -0
- package/dist/cli.js.map +1 -0
- package/dist/docx.d.ts +19 -0
- package/dist/docx.js +592 -0
- package/dist/docx.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +178 -0
- package/dist/mcp.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/package.json +1 -6
- package/server.json +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rene Turcios
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,149 @@
|
|
|
1
|
-
#
|
|
1
|
+
# docxredline
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
docxredline works on Word `.docx` files the way an agent needs to:
|
|
4
|
+
read one as plain text you can reason over, edit one so Word itself
|
|
5
|
+
shows the changes as tracked redlines, and scrub one clean before it
|
|
6
|
+
leaves the building. Everything is local — documents never leave the
|
|
7
|
+
machine, and there is no server component beyond the stdio MCP face.
|
|
8
|
+
|
|
9
|
+
This is a labelled core subset: it covers the three document tools
|
|
10
|
+
(`read_docx`, `process_document_batch`, `finalize_document`) and nothing
|
|
11
|
+
else. It does not drive a live Word instance, render any UI, or handle
|
|
12
|
+
document comments (see the notes per tool below).
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @reneturcios/docxredline read agreement.docx
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or run the container image:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
docker run -i ghcr.io/cloudcorprecords/docxredline
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The container defaults to the MCP server (see below). Pass CLI
|
|
27
|
+
arguments to use it as a CLI instead. Note the CLI reads and writes
|
|
28
|
+
files by path, so give the container a volume mount for your documents.
|
|
29
|
+
|
|
30
|
+
## CLI
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Read a document (tracked changes shown as CriticMarkup)
|
|
34
|
+
docxredline read agreement.docx
|
|
35
|
+
|
|
36
|
+
# Read the final clean version instead
|
|
37
|
+
docxredline read agreement.docx --clean
|
|
38
|
+
|
|
39
|
+
# Only paragraphs mentioning payment terms; synthetic page 2
|
|
40
|
+
docxredline read agreement.docx --search "payment"
|
|
41
|
+
docxredline read agreement.docx --page 2
|
|
42
|
+
|
|
43
|
+
# Apply a batch of edits from a JSON file
|
|
44
|
+
docxredline batch agreement.docx changes.json --author "Review Bot"
|
|
45
|
+
|
|
46
|
+
# Pre-send scrub, locked read-only
|
|
47
|
+
docxredline finalize agreement.docx --protection read_only
|
|
48
|
+
|
|
49
|
+
docxredline --version
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Exit codes: `0` on success, `1` when a file cannot be read or no change
|
|
53
|
+
in a batch applies, `2` for usage errors. All commands accept `--json`.
|
|
54
|
+
|
|
55
|
+
A changes file is a JSON array of change objects:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
[
|
|
59
|
+
{ "type": "modify", "target_text": "thirty days", "new_text": "sixty days" },
|
|
60
|
+
{ "type": "modify", "target_text": "old clause", "new_text": "new clause", "match_mode": "all" },
|
|
61
|
+
{ "type": "accept", "target_text": "12" },
|
|
62
|
+
{ "type": "reject", "target_text": "13" }
|
|
63
|
+
]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## MCP server
|
|
67
|
+
|
|
68
|
+
One bin serves both faces. The `mcp` subcommand starts a stdio MCP
|
|
69
|
+
server:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx -y @reneturcios/docxredline mcp
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
MCP client configuration:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"mcpServers": {
|
|
80
|
+
"docxredline": {
|
|
81
|
+
"command": "npx",
|
|
82
|
+
"args": ["-y", "@reneturcios/docxredline", "mcp"]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Tools
|
|
89
|
+
|
|
90
|
+
| Tool | Input | What it does |
|
|
91
|
+
| --- | --- | --- |
|
|
92
|
+
| `read_docx` | `path` (string, required); `clean_view` (boolean), `search_query` (string), `page` (number) optional | Returns the document as Markdown-like text, paragraph by paragraph. Existing tracked changes render as CriticMarkup — `{++inserted++}` and `{--deleted--}` — unless `clean_view` resolves them away. `search_query` keeps only paragraphs containing the text. |
|
|
93
|
+
| `process_document_batch` | `path` (string, required), `changes` (array, required); `output_path`, `author` optional | Applies a batch of changes and writes the result (default: `<name>.redlined.docx` beside the input). Returns how many changes applied and a per-change error list for the rest. The output is written only when at least one change applied. |
|
|
94
|
+
| `finalize_document` | `path` (string, required); `output_path`, `protection_mode` optional | Writes a distribution-ready copy (default: `<name>.scrubbed.docx`): author fields in `docProps/core.xml` emptied, `w:rsid*` attributes stripped from the body, and — with `protection_mode: "read_only"` — a `documentProtection` lock added. |
|
|
95
|
+
|
|
96
|
+
### How edits and IDs work in this subset
|
|
97
|
+
|
|
98
|
+
- A `modify` change finds `target_text` inside a single paragraph's
|
|
99
|
+
text and rewrites the containing run: the old text is wrapped in a
|
|
100
|
+
`<w:del>` element and the new text in a `<w:ins>` element, both
|
|
101
|
+
stamped with the batch `author` and fresh `w:id`s, so Word displays
|
|
102
|
+
a genuine tracked change.
|
|
103
|
+
- Safety gate: a non-regex `modify` with the default `match_mode`
|
|
104
|
+
`"first"` whose target matches **more than one paragraph** is
|
|
105
|
+
refused with an ambiguity error rather than applied to a guessed
|
|
106
|
+
paragraph. Use a longer target, or `match_mode: "all"` to edit every
|
|
107
|
+
occurrence in every matching paragraph. `regex: true` treats
|
|
108
|
+
`target_text` as a regular expression.
|
|
109
|
+
- A target that spans multiple runs inside its paragraph cannot be
|
|
110
|
+
rewritten by this subset and is reported as a per-change failure.
|
|
111
|
+
- `accept` / `reject` take the tracked change's `w:id` string as
|
|
112
|
+
`target_text`. Accept keeps inserted text and drops deletions;
|
|
113
|
+
reject does the inverse. (The original tool's richer ID model for
|
|
114
|
+
comments and ranges is not reproduced here.)
|
|
115
|
+
- `reply` is not supported in this subset: it comes back as a
|
|
116
|
+
per-change failure, `"comments not supported in this subset"`, and
|
|
117
|
+
is never silently counted as applied.
|
|
118
|
+
|
|
119
|
+
### Synthetic paging
|
|
120
|
+
|
|
121
|
+
`.docx` has no page concept, so `page` is a local contract: paragraphs
|
|
122
|
+
are grouped 40 to a page, numbered from 1. `read_docx` reports the
|
|
123
|
+
document's total paragraph count and synthetic page count with every
|
|
124
|
+
result so callers can page deliberately.
|
|
125
|
+
|
|
126
|
+
### finalize notes
|
|
127
|
+
|
|
128
|
+
When the document has no `word/settings.xml` part and protection is
|
|
129
|
+
requested, docxredline creates a minimal one carrying only the
|
|
130
|
+
protection element. A complete package would also register that part
|
|
131
|
+
in `[Content_Types].xml` and the package relationships; this subset
|
|
132
|
+
records that as a known gap rather than rewriting those parts.
|
|
133
|
+
|
|
134
|
+
## Development
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
npm install
|
|
138
|
+
npm run build # tsc -> dist/
|
|
139
|
+
npm test # node test runner over test/*.test.ts, no network
|
|
140
|
+
npm run dev # run the CLI from source via tsx
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Tests generate their own minimal `.docx` fixtures in a temp directory
|
|
144
|
+
with the same stored-ZIP writer the tool ships, so the suite exercises
|
|
145
|
+
the real archive reader end to end.
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
MIT — see `LICENSE`.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* docxredline CLI.
|
|
4
|
+
*
|
|
5
|
+
* docxredline read <path.docx> [--clean] [--search <text>] [--page <n>] [--json]
|
|
6
|
+
* docxredline batch <path.docx> <changes.json> [--output <path>] [--author <name>] [--json]
|
|
7
|
+
* docxredline finalize <path.docx> [--output <path>] [--protection read_only] [--json]
|
|
8
|
+
* docxredline mcp
|
|
9
|
+
*
|
|
10
|
+
* `batch` reads its changes from a JSON file holding an array of change
|
|
11
|
+
* objects ({type, target_text, new_text, ...}).
|
|
12
|
+
*
|
|
13
|
+
* Exit codes: 0 success, 1 file/edit error, 2 usage error.
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync } from "node:fs";
|
|
16
|
+
import { finalizeDocument, processDocumentBatch, readDocx, VERSION } from "./docx.js";
|
|
17
|
+
function usage() {
|
|
18
|
+
console.error(`Usage:
|
|
19
|
+
docxredline read <path.docx> [--clean] [--search <text>] [--page <n>] [--json]
|
|
20
|
+
docxredline batch <path.docx> <changes.json> [--output <path>] [--author <name>] [--json]
|
|
21
|
+
docxredline finalize <path.docx> [--output <path>] [--protection <mode>] [--json]
|
|
22
|
+
docxredline mcp
|
|
23
|
+
docxredline --version`);
|
|
24
|
+
process.exit(2);
|
|
25
|
+
}
|
|
26
|
+
function flagValue(args, flag) {
|
|
27
|
+
const i = args.indexOf(flag);
|
|
28
|
+
return i !== -1 && i + 1 < args.length ? args[i + 1] : undefined;
|
|
29
|
+
}
|
|
30
|
+
function positional(args, flagsWithValues) {
|
|
31
|
+
const out = [];
|
|
32
|
+
for (let i = 0; i < args.length; i++) {
|
|
33
|
+
if (flagsWithValues.includes(args[i]))
|
|
34
|
+
i++;
|
|
35
|
+
else if (!args[i].startsWith("--"))
|
|
36
|
+
out.push(args[i]);
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
async function main() {
|
|
41
|
+
const [cmd, ...rest] = process.argv.slice(2);
|
|
42
|
+
if (cmd === "--version" || cmd === "-v") {
|
|
43
|
+
console.log(VERSION);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (cmd === "mcp") {
|
|
47
|
+
await import("./mcp.js");
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (!cmd)
|
|
51
|
+
usage();
|
|
52
|
+
const asJson = rest.includes("--json");
|
|
53
|
+
try {
|
|
54
|
+
if (cmd === "read") {
|
|
55
|
+
const [input] = positional(rest, ["--search", "--page"]);
|
|
56
|
+
if (!input)
|
|
57
|
+
usage();
|
|
58
|
+
const pageRaw = flagValue(rest, "--page");
|
|
59
|
+
const result = readDocx(input, {
|
|
60
|
+
cleanView: rest.includes("--clean"),
|
|
61
|
+
searchQuery: flagValue(rest, "--search"),
|
|
62
|
+
page: pageRaw !== undefined ? Number(pageRaw) : undefined,
|
|
63
|
+
});
|
|
64
|
+
if (asJson)
|
|
65
|
+
console.log(JSON.stringify(result, null, 2));
|
|
66
|
+
else
|
|
67
|
+
console.log(result.text);
|
|
68
|
+
}
|
|
69
|
+
else if (cmd === "batch") {
|
|
70
|
+
const [input, changesFile] = positional(rest, ["--output", "--author"]);
|
|
71
|
+
if (!input || !changesFile)
|
|
72
|
+
usage();
|
|
73
|
+
const changes = JSON.parse(readFileSync(changesFile, "utf8"));
|
|
74
|
+
if (!Array.isArray(changes)) {
|
|
75
|
+
console.error("error: changes file must hold a JSON array of changes");
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
const result = processDocumentBatch({
|
|
79
|
+
path: input,
|
|
80
|
+
outputPath: flagValue(rest, "--output"),
|
|
81
|
+
author: flagValue(rest, "--author"),
|
|
82
|
+
changes,
|
|
83
|
+
});
|
|
84
|
+
if (asJson)
|
|
85
|
+
console.log(JSON.stringify(result, null, 2));
|
|
86
|
+
else {
|
|
87
|
+
console.log(`applied ${result.applied}, failed ${result.failed.length}` +
|
|
88
|
+
(result.output_path ? ` -> ${result.output_path}` : ""));
|
|
89
|
+
for (const f of result.failed)
|
|
90
|
+
console.log(` change ${f.index}: ${f.error}`);
|
|
91
|
+
}
|
|
92
|
+
if (result.applied === 0 && result.failed.length > 0)
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
else if (cmd === "finalize") {
|
|
96
|
+
const [input] = positional(rest, ["--output", "--protection"]);
|
|
97
|
+
if (!input)
|
|
98
|
+
usage();
|
|
99
|
+
const result = finalizeDocument({
|
|
100
|
+
path: input,
|
|
101
|
+
outputPath: flagValue(rest, "--output"),
|
|
102
|
+
protectionMode: flagValue(rest, "--protection"),
|
|
103
|
+
});
|
|
104
|
+
if (asJson)
|
|
105
|
+
console.log(JSON.stringify(result, null, 2));
|
|
106
|
+
else
|
|
107
|
+
console.log(`scrubbed -> ${result.output_path} (metadata_stripped=${result.metadata_stripped}, ` +
|
|
108
|
+
`rsid_removed=${result.rsid_attributes_removed}, protection=${result.protection})`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
usage();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
console.error(`error: ${err instanceof Error ? err.message : String(err)}`);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
void main();
|
|
120
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/docx.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { BatchInput, BatchResult, FinalizeInput, FinalizeResult, ReadDocxOptions, ReadDocxResult, ZipEntry } from "./types.js";
|
|
2
|
+
export declare const VERSION = "0.1.0";
|
|
3
|
+
/** Paragraphs per synthetic page for read_docx's `page` argument. */
|
|
4
|
+
export declare const PARAGRAPHS_PER_PAGE = 40;
|
|
5
|
+
/** Standard CRC-32 (IEEE 802.3 polynomial) of a byte string. */
|
|
6
|
+
export declare function crc32(data: Uint8Array): number;
|
|
7
|
+
/** Build a ZIP archive of stored (uncompressed) entries. */
|
|
8
|
+
export declare function writeZip(entries: ZipEntry[]): Buffer;
|
|
9
|
+
/** Read a ZIP archive (stored or deflated entries) into ordered entries. */
|
|
10
|
+
export declare function readZip(buf: Buffer): ZipEntry[];
|
|
11
|
+
/** Decode XML entities (& < A A ...) in a text node. */
|
|
12
|
+
export declare function decodeXml(text: string): string;
|
|
13
|
+
/** Escape a string for use as XML text content. */
|
|
14
|
+
export declare function encodeXml(text: string): string;
|
|
15
|
+
/** Paragraph XML spans of a document.xml string, in order. */
|
|
16
|
+
export declare function splitParagraphs(documentXml: string): string[];
|
|
17
|
+
export declare function readDocx(inputPath: string, options?: ReadDocxOptions): ReadDocxResult;
|
|
18
|
+
export declare function processDocumentBatch(input: BatchInput): BatchResult;
|
|
19
|
+
export declare function finalizeDocument(input: FinalizeInput): FinalizeResult;
|
package/dist/docx.js
ADDED
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* docxredline core: read, redline, and finalize .docx files, locally.
|
|
3
|
+
*
|
|
4
|
+
* A .docx is a ZIP of XML parts. This module carries its own minimal ZIP
|
|
5
|
+
* reader (stored + deflated entries, inflated with node:zlib) and ZIP
|
|
6
|
+
* writer (stored entries with locally computed CRC32), then works on
|
|
7
|
+
* word/document.xml as text:
|
|
8
|
+
*
|
|
9
|
+
* - Reading splits the body into <w:p> paragraphs, concatenates the
|
|
10
|
+
* <w:t> text of each, and renders existing <w:ins> / <w:del> tracked
|
|
11
|
+
* changes as CriticMarkup ({++ ++} / {-- --}). The clean view resolves
|
|
12
|
+
* them instead: inserted text stays, deleted text goes.
|
|
13
|
+
* - A modify edit finds its target inside one paragraph's concatenated
|
|
14
|
+
* <w:t> text and rewrites the containing run, wrapping the old text in
|
|
15
|
+
* a <w:del> and the new text in a <w:ins> so Word shows a native
|
|
16
|
+
* tracked change. Safety gate, mirroring the original tool: a
|
|
17
|
+
* non-regex target with match_mode "first" that matches more than one
|
|
18
|
+
* paragraph is refused as ambiguous rather than guessed at.
|
|
19
|
+
* - Accept / reject address one tracked-change element by its w:id
|
|
20
|
+
* (carried in the change's target_text): accept unwraps <w:ins> and
|
|
21
|
+
* drops <w:del>; reject does the inverse.
|
|
22
|
+
* - finalize empties the author fields of docProps/core.xml, strips
|
|
23
|
+
* w:rsid* attributes from the body, and can add a readOnly
|
|
24
|
+
* documentProtection element to word/settings.xml.
|
|
25
|
+
*
|
|
26
|
+
* Paragraphs are paged synthetically at 40 per page for read_docx's
|
|
27
|
+
* `page` argument; .docx has no page concept of its own.
|
|
28
|
+
*/
|
|
29
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
30
|
+
import path from "node:path";
|
|
31
|
+
import { inflateRawSync } from "node:zlib";
|
|
32
|
+
export const VERSION = "0.1.0";
|
|
33
|
+
/** Paragraphs per synthetic page for read_docx's `page` argument. */
|
|
34
|
+
export const PARAGRAPHS_PER_PAGE = 40;
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// CRC32 + ZIP (stored writer, stored/deflated reader)
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
const CRC_TABLE = (() => {
|
|
39
|
+
const table = [];
|
|
40
|
+
for (let n = 0; n < 256; n++) {
|
|
41
|
+
let c = n;
|
|
42
|
+
for (let k = 0; k < 8; k++) {
|
|
43
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
44
|
+
}
|
|
45
|
+
table.push(c >>> 0);
|
|
46
|
+
}
|
|
47
|
+
return table;
|
|
48
|
+
})();
|
|
49
|
+
/** Standard CRC-32 (IEEE 802.3 polynomial) of a byte string. */
|
|
50
|
+
export function crc32(data) {
|
|
51
|
+
let crc = 0xffffffff;
|
|
52
|
+
for (let i = 0; i < data.length; i++) {
|
|
53
|
+
crc = CRC_TABLE[(crc ^ data[i]) & 0xff] ^ (crc >>> 8);
|
|
54
|
+
}
|
|
55
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
56
|
+
}
|
|
57
|
+
function u16le(n) {
|
|
58
|
+
return [n & 0xff, (n >>> 8) & 0xff];
|
|
59
|
+
}
|
|
60
|
+
function u32le(n) {
|
|
61
|
+
return [n & 0xff, (n >>> 8) & 0xff, (n >>> 16) & 0xff, (n >>> 24) & 0xff];
|
|
62
|
+
}
|
|
63
|
+
// DOS date pinned to 2026-10-26 so written archives are deterministic.
|
|
64
|
+
const DOS_TIME = 0;
|
|
65
|
+
const DOS_DATE = ((2026 - 1980) << 9) | (10 << 5) | 26;
|
|
66
|
+
/** Build a ZIP archive of stored (uncompressed) entries. */
|
|
67
|
+
export function writeZip(entries) {
|
|
68
|
+
const encoder = new TextEncoder();
|
|
69
|
+
const chunks = [];
|
|
70
|
+
const central = [];
|
|
71
|
+
let offset = 0;
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
const nameBytes = encoder.encode(entry.name);
|
|
74
|
+
const crc = crc32(entry.data);
|
|
75
|
+
const localHeader = [
|
|
76
|
+
...u32le(0x04034b50), // local file header signature
|
|
77
|
+
...u16le(20), // version needed
|
|
78
|
+
...u16le(0), // flags
|
|
79
|
+
...u16le(0), // method 0 = stored
|
|
80
|
+
...u16le(DOS_TIME),
|
|
81
|
+
...u16le(DOS_DATE),
|
|
82
|
+
...u32le(crc),
|
|
83
|
+
...u32le(entry.data.length), // compressed size
|
|
84
|
+
...u32le(entry.data.length), // uncompressed size
|
|
85
|
+
...u16le(nameBytes.length),
|
|
86
|
+
...u16le(0), // extra length
|
|
87
|
+
];
|
|
88
|
+
chunks.push(...localHeader, ...nameBytes, ...entry.data);
|
|
89
|
+
central.push(...u32le(0x02014b50), // central directory signature
|
|
90
|
+
...u16le(20), // version made by
|
|
91
|
+
...u16le(20), // version needed
|
|
92
|
+
...u16le(0), // flags
|
|
93
|
+
...u16le(0), // method: stored
|
|
94
|
+
...u16le(DOS_TIME), ...u16le(DOS_DATE), ...u32le(crc), ...u32le(entry.data.length), ...u32le(entry.data.length), ...u16le(nameBytes.length), ...u16le(0), // extra length
|
|
95
|
+
...u16le(0), // comment length
|
|
96
|
+
...u16le(0), // disk number start
|
|
97
|
+
...u16le(0), // internal attributes
|
|
98
|
+
...u32le(0), // external attributes
|
|
99
|
+
...u32le(offset), // local header offset
|
|
100
|
+
...nameBytes);
|
|
101
|
+
offset += localHeader.length + nameBytes.length + entry.data.length;
|
|
102
|
+
}
|
|
103
|
+
const endRecord = [
|
|
104
|
+
...u32le(0x06054b50), // end of central directory signature
|
|
105
|
+
...u16le(0), // disk number
|
|
106
|
+
...u16le(0), // disk with central directory
|
|
107
|
+
...u16le(entries.length), // entries on this disk
|
|
108
|
+
...u16le(entries.length), // total entries
|
|
109
|
+
...u32le(central.length), // central directory size
|
|
110
|
+
...u32le(offset), // central directory offset
|
|
111
|
+
...u16le(0), // comment length
|
|
112
|
+
];
|
|
113
|
+
return Buffer.from([...chunks, ...central, ...endRecord]);
|
|
114
|
+
}
|
|
115
|
+
/** Read a ZIP archive (stored or deflated entries) into ordered entries. */
|
|
116
|
+
export function readZip(buf) {
|
|
117
|
+
let eocd = -1;
|
|
118
|
+
const earliest = Math.max(0, buf.length - 22 - 0xffff);
|
|
119
|
+
for (let i = buf.length - 22; i >= earliest; i--) {
|
|
120
|
+
if (buf.readUInt32LE(i) === 0x06054b50) {
|
|
121
|
+
eocd = i;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (eocd === -1) {
|
|
126
|
+
throw new Error("not a ZIP archive (end of central directory not found)");
|
|
127
|
+
}
|
|
128
|
+
const count = buf.readUInt16LE(eocd + 10);
|
|
129
|
+
let pos = buf.readUInt32LE(eocd + 16);
|
|
130
|
+
const entries = [];
|
|
131
|
+
for (let n = 0; n < count; n++) {
|
|
132
|
+
if (pos + 46 > buf.length || buf.readUInt32LE(pos) !== 0x02014b50) {
|
|
133
|
+
throw new Error("corrupt ZIP archive (bad central directory entry)");
|
|
134
|
+
}
|
|
135
|
+
const method = buf.readUInt16LE(pos + 10);
|
|
136
|
+
const compSize = buf.readUInt32LE(pos + 20);
|
|
137
|
+
const nameLen = buf.readUInt16LE(pos + 28);
|
|
138
|
+
const extraLen = buf.readUInt16LE(pos + 30);
|
|
139
|
+
const commentLen = buf.readUInt16LE(pos + 32);
|
|
140
|
+
const localOffset = buf.readUInt32LE(pos + 42);
|
|
141
|
+
const name = buf.toString("utf8", pos + 46, pos + 46 + nameLen);
|
|
142
|
+
if (localOffset + 30 > buf.length || buf.readUInt32LE(localOffset) !== 0x04034b50) {
|
|
143
|
+
throw new Error(`corrupt ZIP archive (bad local header for ${name})`);
|
|
144
|
+
}
|
|
145
|
+
const lNameLen = buf.readUInt16LE(localOffset + 26);
|
|
146
|
+
const lExtraLen = buf.readUInt16LE(localOffset + 28);
|
|
147
|
+
const dataStart = localOffset + 30 + lNameLen + lExtraLen;
|
|
148
|
+
const raw = buf.subarray(dataStart, dataStart + compSize);
|
|
149
|
+
let data;
|
|
150
|
+
if (method === 0) {
|
|
151
|
+
data = Buffer.from(raw);
|
|
152
|
+
}
|
|
153
|
+
else if (method === 8) {
|
|
154
|
+
data = inflateRawSync(raw);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
throw new Error(`unsupported ZIP compression method ${method} for ${name}`);
|
|
158
|
+
}
|
|
159
|
+
entries.push({ name, data });
|
|
160
|
+
pos += 46 + nameLen + extraLen + commentLen;
|
|
161
|
+
}
|
|
162
|
+
return entries;
|
|
163
|
+
}
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// XML helpers
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
const XML_ENTITIES = {
|
|
168
|
+
amp: "&",
|
|
169
|
+
lt: "<",
|
|
170
|
+
gt: ">",
|
|
171
|
+
quot: '"',
|
|
172
|
+
apos: "'",
|
|
173
|
+
};
|
|
174
|
+
/** Decode XML entities (& < A A ...) in a text node. */
|
|
175
|
+
export function decodeXml(text) {
|
|
176
|
+
return text.replace(/&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g, (whole, body) => {
|
|
177
|
+
if (body.startsWith("#x") || body.startsWith("#X")) {
|
|
178
|
+
return String.fromCodePoint(parseInt(body.slice(2), 16));
|
|
179
|
+
}
|
|
180
|
+
if (body.startsWith("#")) {
|
|
181
|
+
return String.fromCodePoint(parseInt(body.slice(1), 10));
|
|
182
|
+
}
|
|
183
|
+
return XML_ENTITIES[body] ?? whole;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/** Escape a string for use as XML text content. */
|
|
187
|
+
export function encodeXml(text) {
|
|
188
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
189
|
+
}
|
|
190
|
+
/** Escape a string for use inside a double-quoted XML attribute. */
|
|
191
|
+
function encodeAttr(text) {
|
|
192
|
+
return encodeXml(text).replace(/"/g, """);
|
|
193
|
+
}
|
|
194
|
+
/** Paragraph XML spans of a document.xml string, in order. */
|
|
195
|
+
export function splitParagraphs(documentXml) {
|
|
196
|
+
const spans = [];
|
|
197
|
+
const re = /<w:p\b[^>]*\/>|<w:p\b[^>]*>[\s\S]*?<\/w:p>/g;
|
|
198
|
+
let m;
|
|
199
|
+
while ((m = re.exec(documentXml)) !== null)
|
|
200
|
+
spans.push(m[0]);
|
|
201
|
+
return spans;
|
|
202
|
+
}
|
|
203
|
+
/** Replace the i-th paragraph span of document.xml (paragraph count is stable). */
|
|
204
|
+
function replaceParagraph(documentXml, index, replacement) {
|
|
205
|
+
const re = /<w:p\b[^>]*\/>|<w:p\b[^>]*>[\s\S]*?<\/w:p>/g;
|
|
206
|
+
let i = 0;
|
|
207
|
+
return documentXml.replace(re, (span) => (i++ === index ? replacement : span));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* The text segments of one paragraph, in order, each tagged by whether it
|
|
211
|
+
* sits inside a <w:ins>, inside a <w:del> (its text lives in <w:delText>),
|
|
212
|
+
* or in ordinary body text.
|
|
213
|
+
*/
|
|
214
|
+
function paragraphSegments(paragraphXml) {
|
|
215
|
+
const segments = [];
|
|
216
|
+
const tokenRe = /<[^>]+>|[^<]+/g;
|
|
217
|
+
const context = [];
|
|
218
|
+
let capture = null;
|
|
219
|
+
let buffer = "";
|
|
220
|
+
const kind = () => context.includes("del") ? "del" : context.includes("ins") ? "ins" : "normal";
|
|
221
|
+
const flush = () => {
|
|
222
|
+
if (capture !== null && buffer.length > 0) {
|
|
223
|
+
segments.push({ kind: capture === "delText" ? "del" : kind(), text: decodeXml(buffer) });
|
|
224
|
+
}
|
|
225
|
+
buffer = "";
|
|
226
|
+
};
|
|
227
|
+
let tok;
|
|
228
|
+
while ((tok = tokenRe.exec(paragraphXml)) !== null) {
|
|
229
|
+
const t = tok[0];
|
|
230
|
+
if (t.startsWith("<")) {
|
|
231
|
+
if (/^<w:ins\b/.test(t) && !t.endsWith("/>"))
|
|
232
|
+
context.push("ins");
|
|
233
|
+
else if (/^<\/w:ins\s*>/.test(t)) {
|
|
234
|
+
const i = context.lastIndexOf("ins");
|
|
235
|
+
if (i !== -1)
|
|
236
|
+
context.splice(i, 1);
|
|
237
|
+
}
|
|
238
|
+
else if (/^<w:del\b/.test(t) && !t.endsWith("/>"))
|
|
239
|
+
context.push("del");
|
|
240
|
+
else if (/^<\/w:del\s*>/.test(t)) {
|
|
241
|
+
const i = context.lastIndexOf("del");
|
|
242
|
+
if (i !== -1)
|
|
243
|
+
context.splice(i, 1);
|
|
244
|
+
}
|
|
245
|
+
else if (/^<w:t\b/.test(t) && !t.endsWith("/>")) {
|
|
246
|
+
flush();
|
|
247
|
+
capture = "t";
|
|
248
|
+
}
|
|
249
|
+
else if (/^<w:delText\b/.test(t) && !t.endsWith("/>")) {
|
|
250
|
+
flush();
|
|
251
|
+
capture = "delText";
|
|
252
|
+
}
|
|
253
|
+
else if (/^<\/w:(t|delText)\s*>/.test(t)) {
|
|
254
|
+
flush();
|
|
255
|
+
capture = null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
else if (capture !== null) {
|
|
259
|
+
buffer += t;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
flush();
|
|
263
|
+
return segments;
|
|
264
|
+
}
|
|
265
|
+
/** Concatenated <w:t> text of a paragraph (inserted text in, deleted text out). */
|
|
266
|
+
function paragraphPlainText(paragraphXml) {
|
|
267
|
+
return paragraphSegments(paragraphXml)
|
|
268
|
+
.filter((s) => s.kind !== "del")
|
|
269
|
+
.map((s) => s.text)
|
|
270
|
+
.join("");
|
|
271
|
+
}
|
|
272
|
+
/** Render one paragraph in markup (CriticMarkup) or clean view. */
|
|
273
|
+
function renderParagraph(paragraphXml, cleanView) {
|
|
274
|
+
const out = [];
|
|
275
|
+
for (const seg of paragraphSegments(paragraphXml)) {
|
|
276
|
+
if (seg.kind === "ins")
|
|
277
|
+
out.push(cleanView ? seg.text : `{++${seg.text}++}`);
|
|
278
|
+
else if (seg.kind === "del") {
|
|
279
|
+
if (!cleanView)
|
|
280
|
+
out.push(`{--${seg.text}--}`);
|
|
281
|
+
}
|
|
282
|
+
else
|
|
283
|
+
out.push(seg.text);
|
|
284
|
+
}
|
|
285
|
+
return out.join("");
|
|
286
|
+
}
|
|
287
|
+
function loadDocx(inputPath) {
|
|
288
|
+
const resolvedPath = path.resolve(inputPath);
|
|
289
|
+
const buf = readFileSync(resolvedPath); // ENOENT etc. propagate as the real error
|
|
290
|
+
const entries = readZip(buf);
|
|
291
|
+
const doc = entries.find((e) => e.name === "word/document.xml");
|
|
292
|
+
if (!doc) {
|
|
293
|
+
throw new Error("not a .docx file (word/document.xml missing)");
|
|
294
|
+
}
|
|
295
|
+
return { resolvedPath, entries, documentXml: doc.data.toString("utf8") };
|
|
296
|
+
}
|
|
297
|
+
function saveDocx(entries, documentXml, outputPath) {
|
|
298
|
+
const next = entries.map((e) => e.name === "word/document.xml" ? { name: e.name, data: Buffer.from(documentXml, "utf8") } : e);
|
|
299
|
+
writeFileSync(outputPath, writeZip(next));
|
|
300
|
+
}
|
|
301
|
+
function siblingOutput(inputPath, suffix) {
|
|
302
|
+
const dir = path.dirname(inputPath);
|
|
303
|
+
const base = path.basename(inputPath).replace(/\.docx$/i, "");
|
|
304
|
+
return path.join(dir, `${base}.${suffix}.docx`);
|
|
305
|
+
}
|
|
306
|
+
// ---------------------------------------------------------------------------
|
|
307
|
+
// read_docx
|
|
308
|
+
// ---------------------------------------------------------------------------
|
|
309
|
+
export function readDocx(inputPath, options = {}) {
|
|
310
|
+
const { resolvedPath, documentXml } = loadDocx(inputPath);
|
|
311
|
+
const cleanView = options.cleanView ?? false;
|
|
312
|
+
let paragraphs = splitParagraphs(documentXml).map((p) => renderParagraph(p, cleanView));
|
|
313
|
+
const total = paragraphs.length;
|
|
314
|
+
if (options.searchQuery !== undefined) {
|
|
315
|
+
const q = options.searchQuery;
|
|
316
|
+
paragraphs = paragraphs.filter((p) => p.includes(q));
|
|
317
|
+
}
|
|
318
|
+
const pageCount = Math.max(1, Math.ceil(total / PARAGRAPHS_PER_PAGE));
|
|
319
|
+
const result = {
|
|
320
|
+
path: resolvedPath,
|
|
321
|
+
text: "",
|
|
322
|
+
paragraphs,
|
|
323
|
+
paragraph_count: paragraphs.length,
|
|
324
|
+
total_paragraphs: total,
|
|
325
|
+
page_count: pageCount,
|
|
326
|
+
};
|
|
327
|
+
if (options.page !== undefined) {
|
|
328
|
+
const start = (options.page - 1) * PARAGRAPHS_PER_PAGE;
|
|
329
|
+
result.paragraphs = paragraphs.slice(start, start + PARAGRAPHS_PER_PAGE);
|
|
330
|
+
result.paragraph_count = result.paragraphs.length;
|
|
331
|
+
result.page = options.page;
|
|
332
|
+
}
|
|
333
|
+
result.text = result.paragraphs.join("\n\n");
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
function textRunSpans(paragraphXml) {
|
|
337
|
+
const spans = [];
|
|
338
|
+
const re = /<w:t\b[^>]*>([\s\S]*?)<\/w:t>/g;
|
|
339
|
+
let m;
|
|
340
|
+
while ((m = re.exec(paragraphXml)) !== null) {
|
|
341
|
+
const openTag = m[0].slice(0, m[0].indexOf(">") + 1);
|
|
342
|
+
spans.push({ index: m.index, element: m[0], openTag, text: decodeXml(m[1]) });
|
|
343
|
+
}
|
|
344
|
+
return spans;
|
|
345
|
+
}
|
|
346
|
+
function findMatches(plain, change) {
|
|
347
|
+
const target = change.target_text ?? "";
|
|
348
|
+
if (change.regex) {
|
|
349
|
+
const re = new RegExp(target, "g");
|
|
350
|
+
const out = [];
|
|
351
|
+
let m;
|
|
352
|
+
while ((m = re.exec(plain)) !== null) {
|
|
353
|
+
out.push({ start: m.index, end: m.index + m[0].length, matched: m[0] });
|
|
354
|
+
if (m[0].length === 0)
|
|
355
|
+
re.lastIndex++;
|
|
356
|
+
}
|
|
357
|
+
return out;
|
|
358
|
+
}
|
|
359
|
+
const out = [];
|
|
360
|
+
let from = 0;
|
|
361
|
+
for (;;) {
|
|
362
|
+
const i = plain.indexOf(target, from);
|
|
363
|
+
if (i === -1)
|
|
364
|
+
break;
|
|
365
|
+
out.push({ start: i, end: i + target.length, matched: target });
|
|
366
|
+
from = i + Math.max(1, target.length);
|
|
367
|
+
}
|
|
368
|
+
return out;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Rewrite one paragraph so the plain-text range [start, end) becomes a
|
|
372
|
+
* tracked change: the old text wrapped in <w:del>, the new text in a
|
|
373
|
+
* following <w:ins>. The range must lie inside a single <w:t> element.
|
|
374
|
+
*/
|
|
375
|
+
function applyModifyToParagraph(paragraphXml, range, newText, author, id, date) {
|
|
376
|
+
const spans = textRunSpans(paragraphXml);
|
|
377
|
+
let plainStart = 0;
|
|
378
|
+
for (const span of spans) {
|
|
379
|
+
const spanEnd = plainStart + span.text.length;
|
|
380
|
+
if (range.start >= plainStart && range.end <= spanEnd) {
|
|
381
|
+
const localStart = range.start - plainStart;
|
|
382
|
+
const localEnd = range.end - plainStart;
|
|
383
|
+
const prefix = span.text.slice(0, localStart);
|
|
384
|
+
const old = span.text.slice(localStart, localEnd);
|
|
385
|
+
const suffix = span.text.slice(localEnd);
|
|
386
|
+
const replacement = span.openTag +
|
|
387
|
+
encodeXml(prefix) +
|
|
388
|
+
`</w:t></w:r>` +
|
|
389
|
+
`<w:del w:id="${id}" w:author="${encodeAttr(author)}" w:date="${date}">` +
|
|
390
|
+
`<w:r><w:delText xml:space="preserve">${encodeXml(old)}</w:delText></w:r></w:del>` +
|
|
391
|
+
`<w:ins w:id="${id + 1}" w:author="${encodeAttr(author)}" w:date="${date}">` +
|
|
392
|
+
`<w:r><w:t xml:space="preserve">${encodeXml(newText)}</w:t></w:r></w:ins>` +
|
|
393
|
+
`<w:r><w:t xml:space="preserve">${encodeXml(suffix)}</w:t>`;
|
|
394
|
+
return (paragraphXml.slice(0, span.index) +
|
|
395
|
+
replacement +
|
|
396
|
+
paragraphXml.slice(span.index + span.element.length));
|
|
397
|
+
}
|
|
398
|
+
plainStart = spanEnd;
|
|
399
|
+
}
|
|
400
|
+
throw new Error("target spans multiple runs; not supported in this subset");
|
|
401
|
+
}
|
|
402
|
+
function nextTrackedId(documentXml) {
|
|
403
|
+
let max = 0;
|
|
404
|
+
const re = /w:id="(\d+)"/g;
|
|
405
|
+
let m;
|
|
406
|
+
while ((m = re.exec(documentXml)) !== null)
|
|
407
|
+
max = Math.max(max, parseInt(m[1], 10));
|
|
408
|
+
return max + 1;
|
|
409
|
+
}
|
|
410
|
+
function attrValue(attrs, name) {
|
|
411
|
+
const m = new RegExp(`(?:^|\\s)${name}="([^"]*)"`).exec(attrs);
|
|
412
|
+
return m ? m[1] : null;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Accept or reject the tracked-change element carrying w:id === id.
|
|
416
|
+
* Accept: unwrap <w:ins> (content stays), drop <w:del> (content goes).
|
|
417
|
+
* Reject: drop <w:ins>, unwrap <w:del> restoring its text as <w:t>.
|
|
418
|
+
* Returns the new XML, or null when no element carries that id.
|
|
419
|
+
*/
|
|
420
|
+
function applyAcceptReject(documentXml, id, accept) {
|
|
421
|
+
const insRe = /<w:ins\b([^>]*)>([\s\S]*?)<\/w:ins>/g;
|
|
422
|
+
let m;
|
|
423
|
+
while ((m = insRe.exec(documentXml)) !== null) {
|
|
424
|
+
if (attrValue(m[1], "w:id") === id) {
|
|
425
|
+
const replacement = accept ? m[2] : "";
|
|
426
|
+
return documentXml.slice(0, m.index) + replacement + documentXml.slice(m.index + m[0].length);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
const delRe = /<w:del\b([^>]*)>([\s\S]*?)<\/w:del>/g;
|
|
430
|
+
while ((m = delRe.exec(documentXml)) !== null) {
|
|
431
|
+
if (attrValue(m[1], "w:id") === id) {
|
|
432
|
+
const restored = m[2]
|
|
433
|
+
.replace(/<w:delText\b([^>]*)>/g, "<w:t$1>")
|
|
434
|
+
.replace(/<\/w:delText\s*>/g, "</w:t>");
|
|
435
|
+
const replacement = accept ? "" : restored;
|
|
436
|
+
return documentXml.slice(0, m.index) + replacement + documentXml.slice(m.index + m[0].length);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
export function processDocumentBatch(input) {
|
|
442
|
+
const loaded = loadDocx(input.path);
|
|
443
|
+
let documentXml = loaded.documentXml;
|
|
444
|
+
const author = input.author ?? "docxredline";
|
|
445
|
+
const failed = [];
|
|
446
|
+
let applied = 0;
|
|
447
|
+
let idCounter = nextTrackedId(documentXml);
|
|
448
|
+
input.changes.forEach((change, index) => {
|
|
449
|
+
try {
|
|
450
|
+
if (change.type === "modify") {
|
|
451
|
+
if (!change.target_text)
|
|
452
|
+
throw new Error("modify requires target_text");
|
|
453
|
+
const mode = change.match_mode ?? "first";
|
|
454
|
+
if (mode !== "first" && mode !== "all") {
|
|
455
|
+
throw new Error(`unknown match_mode: ${mode}`);
|
|
456
|
+
}
|
|
457
|
+
const paragraphs = splitParagraphs(documentXml);
|
|
458
|
+
const plains = paragraphs.map(paragraphPlainText);
|
|
459
|
+
const matchesPerPara = plains.map((p) => findMatches(p, change));
|
|
460
|
+
const matchingParas = matchesPerPara
|
|
461
|
+
.map((matches, i) => (matches.length > 0 ? i : -1))
|
|
462
|
+
.filter((i) => i !== -1);
|
|
463
|
+
if (matchingParas.length === 0) {
|
|
464
|
+
throw new Error(`target not found: ${JSON.stringify(change.target_text)}`);
|
|
465
|
+
}
|
|
466
|
+
// Safety gate: an unanchored "first" edit must never guess between
|
|
467
|
+
// paragraphs. (Regex targets and match_mode "all" are exempt: the
|
|
468
|
+
// caller has opted into the broader behaviour explicitly.)
|
|
469
|
+
if (!change.regex && mode === "first" && matchingParas.length > 1) {
|
|
470
|
+
throw new Error(`ambiguous target: ${JSON.stringify(change.target_text)} matches ` +
|
|
471
|
+
`${matchingParas.length} paragraphs; refusing a "first" edit ` +
|
|
472
|
+
`(use match_mode "all" or a longer target)`);
|
|
473
|
+
}
|
|
474
|
+
const date = new Date().toISOString();
|
|
475
|
+
const newText = change.new_text ?? "";
|
|
476
|
+
const targets = mode === "all"
|
|
477
|
+
? matchingParas.flatMap((pi) => matchesPerPara[pi].map((range) => ({ pi, range })))
|
|
478
|
+
: [{ pi: matchingParas[0], range: matchesPerPara[matchingParas[0]][0] }];
|
|
479
|
+
// Apply in reverse document order so earlier plain-text offsets
|
|
480
|
+
// stay valid while the paragraph XML grows around later edits.
|
|
481
|
+
targets.sort((a, b) => b.pi - a.pi || b.range.start - a.range.start);
|
|
482
|
+
for (const { pi, range } of targets) {
|
|
483
|
+
const current = splitParagraphs(documentXml)[pi];
|
|
484
|
+
const updated = applyModifyToParagraph(current, range, newText, author, idCounter, date);
|
|
485
|
+
idCounter += 2;
|
|
486
|
+
documentXml = replaceParagraph(documentXml, pi, updated);
|
|
487
|
+
}
|
|
488
|
+
applied++;
|
|
489
|
+
}
|
|
490
|
+
else if (change.type === "accept" || change.type === "reject") {
|
|
491
|
+
if (!change.target_text) {
|
|
492
|
+
throw new Error(`${change.type} requires target_text (the w:id of the tracked change)`);
|
|
493
|
+
}
|
|
494
|
+
const updated = applyAcceptReject(documentXml, change.target_text, change.type === "accept");
|
|
495
|
+
if (updated === null) {
|
|
496
|
+
throw new Error(`no tracked change with w:id ${JSON.stringify(change.target_text)}`);
|
|
497
|
+
}
|
|
498
|
+
documentXml = updated;
|
|
499
|
+
applied++;
|
|
500
|
+
}
|
|
501
|
+
else if (change.type === "reply") {
|
|
502
|
+
throw new Error("comments not supported in this subset");
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
throw new Error(`unknown change type: ${JSON.stringify(change.type)}`);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
catch (err) {
|
|
509
|
+
failed.push({ index, error: err instanceof Error ? err.message : String(err) });
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
const result = { applied, failed };
|
|
513
|
+
if (applied >= 1) {
|
|
514
|
+
const outputPath = path.resolve(input.outputPath ?? siblingOutput(loaded.resolvedPath, "redlined"));
|
|
515
|
+
saveDocx(loaded.entries, documentXml, outputPath);
|
|
516
|
+
result.output_path = outputPath;
|
|
517
|
+
}
|
|
518
|
+
return result;
|
|
519
|
+
}
|
|
520
|
+
// ---------------------------------------------------------------------------
|
|
521
|
+
// finalize_document
|
|
522
|
+
// ---------------------------------------------------------------------------
|
|
523
|
+
export function finalizeDocument(input) {
|
|
524
|
+
const loaded = loadDocx(input.path);
|
|
525
|
+
const entries = loaded.entries.map((e) => ({ name: e.name, data: e.data }));
|
|
526
|
+
// Strip w:rsid* attributes (Word's internal revision-save IDs) from the body.
|
|
527
|
+
let documentXml = loaded.documentXml;
|
|
528
|
+
const rsidMatches = documentXml.match(/\s+w:rsid[A-Za-z]*="[^"]*"/g);
|
|
529
|
+
const rsidRemoved = rsidMatches ? rsidMatches.length : 0;
|
|
530
|
+
if (rsidRemoved > 0) {
|
|
531
|
+
documentXml = documentXml.replace(/\s+w:rsid[A-Za-z]*="[^"]*"/g, "");
|
|
532
|
+
}
|
|
533
|
+
const docEntry = entries.find((e) => e.name === "word/document.xml");
|
|
534
|
+
if (docEntry)
|
|
535
|
+
docEntry.data = Buffer.from(documentXml, "utf8");
|
|
536
|
+
// Empty the author fields of docProps/core.xml.
|
|
537
|
+
let metadataStripped = false;
|
|
538
|
+
const core = entries.find((e) => e.name === "docProps/core.xml");
|
|
539
|
+
if (core) {
|
|
540
|
+
const scrubbed = core.data
|
|
541
|
+
.toString("utf8")
|
|
542
|
+
.replace(/(<dc:creator\b[^>]*>)[\s\S]*?(<\/dc:creator>)/g, "$1$2")
|
|
543
|
+
.replace(/(<cp:lastModifiedBy\b[^>]*>)[\s\S]*?(<\/cp:lastModifiedBy>)/g, "$1$2");
|
|
544
|
+
core.data = Buffer.from(scrubbed, "utf8");
|
|
545
|
+
metadataStripped = true;
|
|
546
|
+
}
|
|
547
|
+
// Optional document protection.
|
|
548
|
+
let protection = null;
|
|
549
|
+
if (input.protectionMode !== undefined) {
|
|
550
|
+
if (input.protectionMode !== "read_only") {
|
|
551
|
+
throw new Error(`unsupported protection_mode: ${JSON.stringify(input.protectionMode)}`);
|
|
552
|
+
}
|
|
553
|
+
const element = `<w:documentProtection w:edit="readOnly" w:enforcement="1"/>`;
|
|
554
|
+
const settings = entries.find((e) => e.name === "word/settings.xml");
|
|
555
|
+
if (settings) {
|
|
556
|
+
let xml = settings.data.toString("utf8");
|
|
557
|
+
if (/<w:documentProtection\b[^>]*\/>/.test(xml)) {
|
|
558
|
+
xml = xml.replace(/<w:documentProtection\b[^>]*\/>/, element);
|
|
559
|
+
}
|
|
560
|
+
else if (/<w:documentProtection\b[^>]*>[\s\S]*?<\/w:documentProtection>/.test(xml)) {
|
|
561
|
+
xml = xml.replace(/<w:documentProtection\b[^>]*>[\s\S]*?<\/w:documentProtection>/, element);
|
|
562
|
+
}
|
|
563
|
+
else if (/<w:settings\b[^>]*>/.test(xml)) {
|
|
564
|
+
xml = xml.replace(/(<w:settings\b[^>]*>)/, `$1${element}`);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
throw new Error("word/settings.xml has no <w:settings> root element");
|
|
568
|
+
}
|
|
569
|
+
settings.data = Buffer.from(xml, "utf8");
|
|
570
|
+
}
|
|
571
|
+
else {
|
|
572
|
+
// Minimal settings part. Note: a full .docx also needs a
|
|
573
|
+
// [Content_Types].xml override and a relationship entry for this
|
|
574
|
+
// part; see BUILD-REPORT gaps.
|
|
575
|
+
const minimal = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n` +
|
|
576
|
+
`<w:settings xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">` +
|
|
577
|
+
`${element}</w:settings>`;
|
|
578
|
+
entries.push({ name: "word/settings.xml", data: Buffer.from(minimal, "utf8") });
|
|
579
|
+
}
|
|
580
|
+
protection = "read_only";
|
|
581
|
+
}
|
|
582
|
+
const outputPath = path.resolve(input.outputPath ?? siblingOutput(loaded.resolvedPath, "scrubbed"));
|
|
583
|
+
writeFileSync(outputPath, writeZip(entries));
|
|
584
|
+
return {
|
|
585
|
+
path: loaded.resolvedPath,
|
|
586
|
+
output_path: outputPath,
|
|
587
|
+
metadata_stripped: metadataStripped,
|
|
588
|
+
rsid_attributes_removed: rsidRemoved,
|
|
589
|
+
protection,
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
//# sourceMappingURL=docx.js.map
|
package/dist/docx.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docx.js","sourceRoot":"","sources":["../src/docx.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* docxredline MCP server. Tools: read_docx, process_document_batch,
|
|
3
|
+
* finalize_document — a labelled core subset of the public spec.
|
|
4
|
+
*
|
|
5
|
+
* Why the low-level Server instead of McpServer: the high-level helper
|
|
6
|
+
* converts zod schemas to JSON Schema and injects two keys that are not
|
|
7
|
+
* in this tool's public spec ("$schema" and "additionalProperties"),
|
|
8
|
+
* which breaks exact tool-list/schema parity with the spec. This file
|
|
9
|
+
* therefore serves tools/list with the spec's exact input schemas,
|
|
10
|
+
* while zod still validates every tools/call argument set. Tool names,
|
|
11
|
+
* parameters, and parameter descriptions are taken from the public spec.
|
|
12
|
+
*
|
|
13
|
+
* All logic is local: the tools read and write .docx files on disk.
|
|
14
|
+
*/
|
|
15
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
16
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
17
|
+
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { finalizeDocument, processDocumentBatch, readDocx, VERSION } from "./docx.js";
|
|
20
|
+
const ReadDocxArgs = z.object({
|
|
21
|
+
path: z.string().describe("Path to the .docx file."),
|
|
22
|
+
clean_view: z
|
|
23
|
+
.boolean()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Read the final clean version (accept all tracked changes)."),
|
|
26
|
+
search_query: z
|
|
27
|
+
.string()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe("Only return paragraphs containing this text."),
|
|
30
|
+
page: z
|
|
31
|
+
.number()
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Synthetic page number to return (paragraphs are grouped into pages)."),
|
|
34
|
+
});
|
|
35
|
+
const ChangeArgs = z.object({
|
|
36
|
+
type: z.string().describe("modify, accept, reject, or reply."),
|
|
37
|
+
target_text: z.string().optional(),
|
|
38
|
+
new_text: z.string().optional(),
|
|
39
|
+
comment: z.string().optional(),
|
|
40
|
+
match_mode: z.string().optional().describe("first (default) or all."),
|
|
41
|
+
regex: z.boolean().optional(),
|
|
42
|
+
});
|
|
43
|
+
const ProcessDocumentBatchArgs = z.object({
|
|
44
|
+
path: z.string(),
|
|
45
|
+
output_path: z
|
|
46
|
+
.string()
|
|
47
|
+
.optional()
|
|
48
|
+
.describe("Where to write the edited .docx. Defaults to <name>.redlined.docx beside the input."),
|
|
49
|
+
author: z.string().optional().describe("Author name recorded on the tracked changes."),
|
|
50
|
+
changes: z.array(ChangeArgs).describe("The list of changes to apply atomically."),
|
|
51
|
+
});
|
|
52
|
+
const FinalizeDocumentArgs = z.object({
|
|
53
|
+
path: z.string(),
|
|
54
|
+
output_path: z.string().optional(),
|
|
55
|
+
protection_mode: z.string().optional().describe("e.g. read_only to lock the document."),
|
|
56
|
+
});
|
|
57
|
+
const TOOLS = [
|
|
58
|
+
{
|
|
59
|
+
name: "read_docx",
|
|
60
|
+
description: "Read a .docx file as clean Markdown-like text (CriticMarkup view of existing tracked changes), optionally filtered.",
|
|
61
|
+
inputSchema: {
|
|
62
|
+
type: "object",
|
|
63
|
+
properties: {
|
|
64
|
+
path: { type: "string", description: "Path to the .docx file." },
|
|
65
|
+
clean_view: {
|
|
66
|
+
type: "boolean",
|
|
67
|
+
description: "Read the final clean version (accept all tracked changes).",
|
|
68
|
+
},
|
|
69
|
+
search_query: {
|
|
70
|
+
type: "string",
|
|
71
|
+
description: "Only return paragraphs containing this text.",
|
|
72
|
+
},
|
|
73
|
+
page: {
|
|
74
|
+
type: "number",
|
|
75
|
+
description: "Synthetic page number to return (paragraphs are grouped into pages).",
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
required: ["path"],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "process_document_batch",
|
|
83
|
+
description: "Apply a batch of edits to a .docx as native Word tracked changes (redlines), or accept/reject/reply to existing tracked changes and comments by ID.",
|
|
84
|
+
inputSchema: {
|
|
85
|
+
type: "object",
|
|
86
|
+
properties: {
|
|
87
|
+
path: { type: "string" },
|
|
88
|
+
output_path: {
|
|
89
|
+
type: "string",
|
|
90
|
+
description: "Where to write the edited .docx. Defaults to <name>.redlined.docx beside the input.",
|
|
91
|
+
},
|
|
92
|
+
author: { type: "string", description: "Author name recorded on the tracked changes." },
|
|
93
|
+
changes: {
|
|
94
|
+
type: "array",
|
|
95
|
+
items: {
|
|
96
|
+
type: "object",
|
|
97
|
+
properties: {
|
|
98
|
+
type: { type: "string", description: "modify, accept, reject, or reply." },
|
|
99
|
+
target_text: { type: "string" },
|
|
100
|
+
new_text: { type: "string" },
|
|
101
|
+
comment: { type: "string" },
|
|
102
|
+
match_mode: { type: "string", description: "first (default) or all." },
|
|
103
|
+
regex: { type: "boolean" },
|
|
104
|
+
},
|
|
105
|
+
required: ["type"],
|
|
106
|
+
},
|
|
107
|
+
description: "The list of changes to apply atomically.",
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
required: ["path", "changes"],
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "finalize_document",
|
|
115
|
+
description: "Pre-send scrub: strip author metadata and internal tracking IDs from a .docx, optionally lock it, and prepare it for distribution.",
|
|
116
|
+
inputSchema: {
|
|
117
|
+
type: "object",
|
|
118
|
+
properties: {
|
|
119
|
+
path: { type: "string" },
|
|
120
|
+
output_path: { type: "string" },
|
|
121
|
+
protection_mode: { type: "string", description: "e.g. read_only to lock the document." },
|
|
122
|
+
},
|
|
123
|
+
required: ["path"],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
function textResult(value) {
|
|
128
|
+
return { content: [{ type: "text", text: JSON.stringify(value, null, 2) }] };
|
|
129
|
+
}
|
|
130
|
+
function errorResult(err) {
|
|
131
|
+
return {
|
|
132
|
+
content: [
|
|
133
|
+
{ type: "text", text: err instanceof Error ? err.message : String(err) },
|
|
134
|
+
],
|
|
135
|
+
isError: true,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const server = new Server({ name: "docxredline", version: VERSION }, { capabilities: { tools: {} } });
|
|
139
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
140
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
141
|
+
const { name, arguments: args } = request.params;
|
|
142
|
+
try {
|
|
143
|
+
if (name === "read_docx") {
|
|
144
|
+
const parsed = ReadDocxArgs.parse(args ?? {});
|
|
145
|
+
return textResult(readDocx(parsed.path, {
|
|
146
|
+
cleanView: parsed.clean_view,
|
|
147
|
+
searchQuery: parsed.search_query,
|
|
148
|
+
page: parsed.page,
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
if (name === "process_document_batch") {
|
|
152
|
+
const parsed = ProcessDocumentBatchArgs.parse(args ?? {});
|
|
153
|
+
return textResult(processDocumentBatch({
|
|
154
|
+
path: parsed.path,
|
|
155
|
+
outputPath: parsed.output_path,
|
|
156
|
+
author: parsed.author,
|
|
157
|
+
changes: parsed.changes,
|
|
158
|
+
}));
|
|
159
|
+
}
|
|
160
|
+
if (name === "finalize_document") {
|
|
161
|
+
const parsed = FinalizeDocumentArgs.parse(args ?? {});
|
|
162
|
+
return textResult(finalizeDocument({
|
|
163
|
+
path: parsed.path,
|
|
164
|
+
outputPath: parsed.output_path,
|
|
165
|
+
protectionMode: parsed.protection_mode,
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
return errorResult(err);
|
|
171
|
+
}
|
|
172
|
+
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
173
|
+
});
|
|
174
|
+
async function main() {
|
|
175
|
+
await server.connect(new StdioServerTransport());
|
|
176
|
+
}
|
|
177
|
+
void main();
|
|
178
|
+
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* docxredline types.
|
|
3
|
+
*
|
|
4
|
+
* Everything here is a local file contract: inputs name .docx files on
|
|
5
|
+
* disk, outputs name the file that was written plus what happened inside
|
|
6
|
+
* it. There is no network surface and no injectable dependency.
|
|
7
|
+
*/
|
|
8
|
+
export interface ReadDocxOptions {
|
|
9
|
+
/** Resolve existing tracked changes away (accept-all view). */
|
|
10
|
+
cleanView?: boolean;
|
|
11
|
+
/** Only return paragraphs whose rendered text contains this string. */
|
|
12
|
+
searchQuery?: string;
|
|
13
|
+
/** Synthetic 1-based page to return; paragraphs are grouped 40 per page. */
|
|
14
|
+
page?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ReadDocxResult {
|
|
17
|
+
/** The resolved input path that was read. */
|
|
18
|
+
path: string;
|
|
19
|
+
/** The returned paragraphs joined with blank lines. */
|
|
20
|
+
text: string;
|
|
21
|
+
/** The returned paragraphs, in document order. */
|
|
22
|
+
paragraphs: string[];
|
|
23
|
+
/** How many paragraphs are in `paragraphs` (after filter / paging). */
|
|
24
|
+
paragraph_count: number;
|
|
25
|
+
/** How many paragraphs the document holds in total (before filter / paging). */
|
|
26
|
+
total_paragraphs: number;
|
|
27
|
+
/** Total synthetic pages at 40 paragraphs per page. */
|
|
28
|
+
page_count: number;
|
|
29
|
+
/** The page that was returned, when a page was requested. */
|
|
30
|
+
page?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface DocChange {
|
|
33
|
+
/** modify, accept, reject, or reply. */
|
|
34
|
+
type: string;
|
|
35
|
+
/**
|
|
36
|
+
* For modify: the text (or regex, when `regex` is set) to find.
|
|
37
|
+
* For accept / reject: the `w:id` string of the tracked-change element.
|
|
38
|
+
*/
|
|
39
|
+
target_text?: string;
|
|
40
|
+
/** Replacement text for modify. */
|
|
41
|
+
new_text?: string;
|
|
42
|
+
/** Comment text; carried for shape compatibility, unused in this subset. */
|
|
43
|
+
comment?: string;
|
|
44
|
+
/** "first" (default) or "all". */
|
|
45
|
+
match_mode?: string;
|
|
46
|
+
/** Treat target_text as a regular expression for modify. */
|
|
47
|
+
regex?: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface BatchInput {
|
|
50
|
+
path: string;
|
|
51
|
+
outputPath?: string;
|
|
52
|
+
author?: string;
|
|
53
|
+
changes: DocChange[];
|
|
54
|
+
}
|
|
55
|
+
export interface BatchFailure {
|
|
56
|
+
/** Index of the change in the submitted batch. */
|
|
57
|
+
index: number;
|
|
58
|
+
error: string;
|
|
59
|
+
}
|
|
60
|
+
export interface BatchResult {
|
|
61
|
+
/** How many changes were applied. */
|
|
62
|
+
applied: number;
|
|
63
|
+
/** One entry per change that could not be applied, with the reason. */
|
|
64
|
+
failed: BatchFailure[];
|
|
65
|
+
/** Where the edited document was written; present only when applied >= 1. */
|
|
66
|
+
output_path?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface FinalizeInput {
|
|
69
|
+
path: string;
|
|
70
|
+
outputPath?: string;
|
|
71
|
+
/** e.g. "read_only" to lock the document. */
|
|
72
|
+
protectionMode?: string;
|
|
73
|
+
}
|
|
74
|
+
export interface FinalizeResult {
|
|
75
|
+
/** The resolved input path. */
|
|
76
|
+
path: string;
|
|
77
|
+
/** Where the scrubbed document was written. */
|
|
78
|
+
output_path: string;
|
|
79
|
+
/** True when docProps/core.xml was present and its author fields emptied. */
|
|
80
|
+
metadata_stripped: boolean;
|
|
81
|
+
/** How many w:rsid* attributes were removed from word/document.xml. */
|
|
82
|
+
rsid_attributes_removed: number;
|
|
83
|
+
/** The protection mode that was applied, or null when none was requested. */
|
|
84
|
+
protection: string | null;
|
|
85
|
+
}
|
|
86
|
+
/** One entry of a ZIP archive, in archive order. */
|
|
87
|
+
export interface ZipEntry {
|
|
88
|
+
name: string;
|
|
89
|
+
data: Buffer;
|
|
90
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* docxredline types.
|
|
3
|
+
*
|
|
4
|
+
* Everything here is a local file contract: inputs name .docx files on
|
|
5
|
+
* disk, outputs name the file that was written plus what happened inside
|
|
6
|
+
* it. There is no network surface and no injectable dependency.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@reneturcios/docxredline",
|
|
3
|
-
"version": "0.0.0-stage",
|
|
4
|
-
"stub": true,
|
|
5
|
-
"description": "Temporary package placeholder for staged publishing"
|
|
6
|
-
}
|
|
1
|
+
{"bin":{"docxredline":"dist/cli.js"},"dependencies":{"@modelcontextprotocol/sdk":"^1","zod":"^4"},"description":"Read .docx files as Markdown-like text with a CriticMarkup view of tracked changes, apply edits as native Word redlines, and scrub documents before sending. One bin serves a small CLI and a stdio MCP server.","devDependencies":{"@types/node":"^20","tsx":"^4","typescript":"^5"},"engines":{"node":">=20"},"files":["dist","server.json"],"homepage":"https://github.com/CloudCorpRecords/docxredline#readme","license":"MIT","mcpName":"io.github.CloudCorpRecords/docxredline","name":"@reneturcios/docxredline","private":false,"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/CloudCorpRecords/docxredline.git"},"scripts":{"build":"tsc","dev":"tsx src/cli.ts","test":"tsx --test test/*.test.ts"},"type":"module","version":"0.1.0"}
|
package/server.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"$schema":"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json","description":"Read, redline, and finalize .docx files with native Word tracked changes.","name":"io.github.CloudCorpRecords/docxredline","packages":[{"identifier":"@reneturcios/docxredline","packageArguments":[{"type":"positional","value":"mcp"}],"registryBaseUrl":"https://registry.npmjs.org","registryType":"npm","runtimeHint":"npx","transport":{"type":"stdio"},"version":"0.1.0"},{"identifier":"ghcr.io/cloudcorprecords/docxredline:0.1.0","packageArguments":[{"type":"positional","value":"mcp"}],"registryType":"oci","runtimeArguments":[{"name":"--rm","type":"named"},{"name":"-i","type":"named"}],"runtimeHint":"docker","transport":{"type":"stdio"}}],"repository":{"source":"github","url":"https://github.com/CloudCorpRecords/docxredline"},"version":"0.1.0"}
|