@taloswork/ose-shared 0.1.11 → 0.1.12
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/package.json +3 -3
- package/skills/document/SKILL.md +143 -215
- package/skills/document/art-directions/base.css +27 -2
- package/skills/document/art-directions/cinematic-depth.css +3 -1
- package/skills/document/art-directions/collage-paper.css +5 -1
- package/skills/document/art-directions/editorial-cut.css +5 -1
- package/skills/document/art-directions/geometric-pop.css +6 -0
- package/skills/document/art-directions/gradient-mesh.css +5 -1
- package/skills/document/art-directions/illustrated-doodle.css +5 -0
- package/skills/document/art-directions/luxury-ornament.css +4 -1
- package/skills/document/art-directions/maximalist-blocks.css +6 -0
- package/skills/document/art-directions/technical-grid.css +4 -0
- package/skills/document/assets/fonts/fonts.json +2 -2
- package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Bold.ttf +0 -0
- package/skills/document/assets/pdf-fonts/NotoSansSC-PDF-Regular.ttf +0 -0
- package/skills/document/assets/pdf-fonts/OFL.txt +93 -0
- package/skills/document/assets/pdf-fonts/README.md +35 -0
- package/skills/document/assets/tessdata/LICENSE-Apache-2.0.txt +202 -0
- package/skills/document/assets/tessdata/README.md +21 -0
- package/skills/document/assets/tessdata/chi_sim.traineddata.gz +0 -0
- package/skills/document/assets/tessdata/eng.traineddata.gz +0 -0
- package/skills/document/bin/deck-build.cjs +2311 -41
- package/skills/document/bin/deck-chart.cjs +762 -0
- package/skills/document/bin/deck-plan.cjs +802 -0
- package/skills/document/bin/deck-qa.cjs +984 -249
- package/skills/document/bin/deck-scenario.cjs +1346 -0
- package/skills/document/bin/doc-convert.cjs +130 -39
- package/skills/document/bin/doc-fill.cjs +73121 -170
- package/skills/document/bin/doc-gen.cjs +625 -68
- package/skills/document/bin/doc-run.cjs +162 -24
- package/skills/document/bin/doc-scan.cjs +13 -0
- package/skills/document/bin/doc-text.cjs +13 -0
- package/skills/document/bin/doc-validate.cjs +718 -23
- package/skills/document/bin/doc-xml.cjs +9 -5
- package/skills/document/bin/docx-annotate.cjs +95 -19
- package/skills/document/bin/fontkit.cjs +1 -0
- package/skills/document/bin/html2pptx.cjs +1372 -121
- package/skills/document/bin/ocr-worker.cjs +1 -0
- package/skills/document/bin/ocr.cjs +131 -40
- package/skills/document/bin/pdf-form.cjs +674 -177
- package/skills/document/bin/pdf-images.cjs +13 -0
- package/skills/document/bin/pdf-ops.cjs +634 -199
- package/skills/document/bin/pdf-render.cjs +19 -3
- package/skills/document/bin/pdf-tables.cjs +13 -0
- package/skills/document/bin/pptx-clone.cjs +3 -3
- package/skills/document/bin/pptx-slide.cjs +3 -3
- package/skills/document/bin/xlsx-query.cjs +192 -61
- package/skills/document/bin-reference.md +56 -33
- package/skills/document/content-pipeline.md +129 -242
- package/skills/document/deck-design.md +355 -638
- package/skills/document/layouts/README.md +46 -33
- package/skills/document/layouts/capacity.json +1 -0
- package/skills/document/layouts/slots.json +1 -1
- package/skills/document/layouts/toc-panel.tpl +1 -1
- package/skills/document/motion/README.md +3 -2
- package/skills/document/office.md +3 -3
- package/skills/document/packs/board-brief/backdrop.html +2 -0
- package/skills/document/packs/board-brief/pack.css +2 -1
- package/skills/document/packs/mckinsey-photo/backdrop.html +2 -0
- package/skills/document/packs/mckinsey-photo/pack.css +2 -1
- package/skills/document/packs/press-room/backdrop.html +2 -0
- package/skills/document/packs/press-room/pack.css +2 -1
- package/skills/document/packs/prismatic-3d/backdrop.html +2 -0
- package/skills/document/packs/prismatic-3d/pack.css +4 -1
- package/skills/document/pdf.md +53 -49
- package/skills/document/rewrite.md +241 -308
- package/skills/document/scenarios.json +1290 -0
- package/skills/document/scenarios.schema.json +103 -0
- package/skills/document/styles/README.md +15 -25
- package/skills/document/styles/_contract-defaults.css +25 -1
- package/skills/document/styles/anthropic/skin.css +6 -3
- package/skills/document/styles/apple/skin.css +4 -3
- package/skills/document/styles/bistro-warm/backdrop.html +2 -0
- package/skills/document/styles/bistro-warm/skin.css +4 -1
- package/skills/document/styles/black-gold/skin.css +6 -5
- package/skills/document/styles/bold-clash/backdrop.html +2 -0
- package/skills/document/styles/bold-clash/skin.css +2 -1
- package/skills/document/styles/branding/skin.css +4 -4
- package/skills/document/styles/campus-teal/backdrop.html +2 -0
- package/skills/document/styles/campus-teal/skin.css +2 -1
- package/skills/document/styles/cinereel/skin.css +7 -3
- package/skills/document/styles/clinic-care/backdrop.html +2 -0
- package/skills/document/styles/clinic-care/skin.css +2 -1
- package/skills/document/styles/cool/skin.css +6 -5
- package/skills/document/styles/dark-glass/backdrop.html +2 -0
- package/skills/document/styles/dark-glass/skin.css +2 -1
- package/skills/document/styles/gov-brief/backdrop.html +2 -0
- package/skills/document/styles/gov-brief/skin.css +2 -1
- package/skills/document/styles/grid-tech/backdrop.html +2 -0
- package/skills/document/styles/grid-tech/skin.css +2 -1
- package/skills/document/styles/hand-drawn/skin.css +3 -3
- package/skills/document/styles/ivory-gold/skin.css +7 -6
- package/skills/document/styles/legal-slate/backdrop.html +2 -0
- package/skills/document/styles/legal-slate/skin.css +2 -1
- package/skills/document/styles/magazine-ink/skin.css +4 -3
- package/skills/document/styles/mckinsey/skin.css +2 -1
- package/skills/document/styles/memphis/backdrop.html +2 -0
- package/skills/document/styles/memphis/skin.css +2 -1
- package/skills/document/styles/mono/skin.css +6 -2
- package/skills/document/styles/neon-cyber/backdrop.html +2 -0
- package/skills/document/styles/neon-cyber/skin.css +2 -1
- package/skills/document/styles/olive-gold/skin.css +7 -6
- package/skills/document/styles/organic-earth/backdrop.html +2 -0
- package/skills/document/styles/organic-earth/skin.css +2 -1
- package/skills/document/styles/pastel-saas/backdrop.html +2 -0
- package/skills/document/styles/pastel-saas/skin.css +2 -1
- package/skills/document/styles/portfolio-noir/backdrop.html +2 -0
- package/skills/document/styles/portfolio-noir/skin.css +2 -1
- package/skills/document/styles/retro-press/backdrop.html +2 -0
- package/skills/document/styles/retro-press/skin.css +2 -1
- package/skills/document/styles/sport-arena/backdrop.html +2 -0
- package/skills/document/styles/sport-arena/skin.css +4 -1
- package/skills/document/styles/swiss-grid/skin.css +10 -5
- package/skills/document/styles/wedding-blush/backdrop.html +2 -0
- package/skills/document/styles/wedding-blush/skin.css +4 -1
- package/skills/document/theme-system.md +85 -77
- package/skills/document/themes/README.md +7 -5
- package/skills/document/vision.md +8 -5
- package/skills/document/xlsx.md +15 -25
|
@@ -1,360 +1,293 @@
|
|
|
1
|
-
# Generate
|
|
1
|
+
# Generate, fill and edit documents (L3)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Read only the sections your task needs** (Read with the offset/limit of the line range):
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
> slide colors (`pptx{background,titleColor,textColor}`).
|
|
5
|
+
- New .docx / .pdf / .xlsx → **Generate** (lines 14–55); a formula in a workbook → also **Formulas** (lines 57–73); a particular look, theme or brand → also **Themes** (lines 75–125)
|
|
6
|
+
- Fill a `{{placeholder}}` .docx / .pptx / .xlsx template → **Fill a template** (lines 127–146)
|
|
7
|
+
- Change an existing .docx — text, structure, comments, tracked changes → **Edit an existing .docx** (lines 148–199)
|
|
8
|
+
- New content in an existing deck's look, or restructure a .pptx → **Template decks** (lines 201–271)
|
|
9
|
+
- Nothing above fits → **Custom scripts** (lines 273–293)
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
(theme = colors/fonts for `doc-gen` output; `themes/README.md` lists them with their
|
|
15
|
-
best fit): `cool-tech` · `apple-keynote` · `mckinsey-consulting` · `anthropic-clay` ·
|
|
16
|
-
`black-gold` · `hand-drawn` · `mono` · `warm-editorial` · `ivory-gold` · `cinereel` ·
|
|
17
|
-
`magazine-ink` · `swiss-grid`. For a plain Word/PDF report, `mckinsey-consulting` (white · deep
|
|
18
|
-
navy · structured tables) is a safe default; `mono` for a stark minimal look.
|
|
11
|
+
A PDF's forms, page ops and stamps are in pdf.md; a deck from scratch is the deck route
|
|
12
|
+
(SKILL.md) — `doc-gen` makes no decks. `<skill>` below is this skill's directory.
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
node <skill>/bin/doc-gen.cjs docx out.docx spec.json --theme <skill>/themes/mckinsey-consulting.json
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
**Topic-adaptive color (when the user hasn't named a theme/brand)**: pass
|
|
25
|
-
`--topic "<subject>"` (or put `"topic"` in the spec). The engine picks a
|
|
26
|
-
palette that FITS the subject — deterministically, by keyword then stable hash —
|
|
27
|
-
instead of the same default every time (the #1 "AI-generated" tell). `--palette <id>` forces a
|
|
28
|
-
named one; `doc-gen --list-palettes` prints the library. A `--theme` file or a
|
|
29
|
-
saved/brand preference (step 1 below) still wins — use `--topic` for the "just
|
|
30
|
-
make it look designed-for-this" case, not when the user has a look in mind.
|
|
31
|
-
|
|
32
|
-
**Picking a theme with the user (preference → showcase → confirm → apply)**:
|
|
33
|
-
|
|
34
|
-
1. **Preference first**: check your memory for a saved deck-theme preference
|
|
35
|
-
(记忆里的"deck 主题偏好") and `themes/custom/` for the user's own themes —
|
|
36
|
-
a remembered/custom theme wins over guessing; use it and SAY so ("按你常用的
|
|
37
|
-
X 主题,要换告诉我").
|
|
38
|
-
2. User named a style → match by 适用场景 in `themes/README.md`, apply directly.
|
|
39
|
-
3. User wants to choose (or nothing fits) → shortlist 2–4 by 适用场景. If they
|
|
40
|
-
want to SEE them, render a faithful sample per candidate: a 1-page
|
|
41
|
-
`doc-gen <docx|pdf> … --theme <themes/x.json>` → `doc-convert` to PDF →
|
|
42
|
-
`pdf-render` the page to a PNG (for a document theme this print render IS the
|
|
43
|
-
real look). A text shortlist by 适用场景 is fine too — don't over-render. Then
|
|
44
|
-
ask with the **picker card**: `ask_user_question`
|
|
45
|
-
type single_choice, one option per theme as
|
|
46
|
-
`{ label: 主题名, description: 一句适用场景, imagePath: 样片PNG绝对路径 }` —
|
|
47
|
-
the UI shows a compact thumbnail-card row and the user clicks one. Apply it.
|
|
48
|
-
4. **After an explicit confirmation, remember it**: save the choice to your
|
|
49
|
-
agent memory as the user's deck-theme preference so the next deck starts
|
|
50
|
-
from it — and update the memory when they switch.
|
|
51
|
-
|
|
52
|
-
**User-created themes (`themes/custom/`)**: writing a new theme.json is the
|
|
53
|
-
supported way to make a new "template" — invalid fields fall back with warnings
|
|
54
|
-
on stderr (read them and fix), so iterate freely. When the user tweaks colors
|
|
55
|
-
repeatedly, hands you brand colors/logo, or says "以后都用这个风格", offer to
|
|
56
|
-
save it: write `<skill>/themes/custom/<descriptive-name>.json` (kebab-case,
|
|
57
|
-
with a `bestFor` line). Files there are the USER's — resource updates never
|
|
58
|
-
touch them — and step 1 above picks them up in every later session. A liked
|
|
59
|
-
existing deck can seed one too: `pptx-theme ref.pptx <skill>/themes/custom/x.json`.
|
|
60
|
-
|
|
61
|
-
**Mimic an EXISTING deck**: extract its palette/fonts into a theme first —
|
|
14
|
+
## Generate — `bin/doc-gen`
|
|
62
15
|
|
|
63
16
|
```
|
|
64
|
-
node <skill>/bin/
|
|
65
|
-
node <skill>/bin/html2pptx.cjs deck.html out.pptx --theme /tmp/ref-theme.json
|
|
17
|
+
node <skill>/bin/doc-gen.cjs <docx|xlsx|pdf> <out> <spec.json> [--topic "<subject>" | --theme <file|name>]
|
|
66
18
|
```
|
|
67
19
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
`
|
|
75
|
-
`
|
|
76
|
-
|
|
77
|
-
`
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
`
|
|
81
|
-
`
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
20
|
+
The spec is validated **strictly before rendering**: an unknown field or an empty body
|
|
21
|
+
exits 65 with `DOC_SPEC_INVALID` and the JSON path — correct that path and rerun rather
|
|
22
|
+
than delivering the previous output.
|
|
23
|
+
|
|
24
|
+
- **docx / pdf**: `{ "title"?, "paragraphs"?: ["…"], "blocks"?: [ … ] }` — at least one
|
|
25
|
+
non-empty paragraph or block; never `sections`. A title-only document must set
|
|
26
|
+
`"allowCoverOnly": true`. `blocks` is an ordered list of
|
|
27
|
+
- `{ "type": "heading", "text": "…", "level": 1..4 }`
|
|
28
|
+
- `{ "type": "paragraph", "text": "…", "bold"?, "italic"? }`
|
|
29
|
+
- `{ "type": "list", "items": ["…"], "ordered"?: true }`
|
|
30
|
+
- `{ "type": "table", "rows": [["h"],["a"]], "header"?: true, "columnWidths"?: [3000, …] }`
|
|
31
|
+
(DOCX widths in twips; default: full page width, equal columns)
|
|
32
|
+
- `{ "type": "image", "data": "<base64>", "imageType"?: "png", "width"?, "height"? }` (px, default 300×200)
|
|
33
|
+
- **xlsx**: `{ "sheets": { "Name": [["h1","h2"], ["a", 1]] } }` (row 1 = bold header), or per
|
|
34
|
+
sheet `{ "rows": [...], "header"?, "columnWidths"?: [n…], "numFmt"?: ["#,##0.00"…] }`. A
|
|
35
|
+
cell string starting with `=` is a **live formula** — then run **Formulas** below.
|
|
36
|
+
Fills, font colours, borders or merges need exceljs via doc-run (**Custom scripts**).
|
|
37
|
+
- The spec may also carry `"topic"`, `"palette"` or `"theme"` (same as the flags).
|
|
38
|
+
|
|
39
|
+
**A length limit** (正文 1000 字以内 / under 800 words) is kept in the spec, before
|
|
40
|
+
generating: the result's `textChars` (spaces and URLs not counted) and `cjkChars` count it
|
|
41
|
+
— over the limit, shorten the spec and generate once more. Do not extract text from the
|
|
42
|
+
output to count it.
|
|
43
|
+
|
|
44
|
+
**Look**: with no theme every document gets the same default — pass
|
|
45
|
+
`--topic "<subject>"` (a palette that fits the subject, deterministic) unless the user
|
|
46
|
+
named a look; **Themes** covers the rest.
|
|
47
|
+
|
|
48
|
+
**PDF** is always printed in Chromium from themed HTML — CJK line-breaking, mixed
|
|
49
|
+
Latin/中文 font fallback, tables, clickable links that wrap inside the page, the theme's
|
|
50
|
+
page size and margin. Inside Talos it uses the app's service (`[pdf (chromium)]`),
|
|
51
|
+
elsewhere a headless Chromium (`[pdf (headless chromium)]`); with neither it exits 69 and
|
|
52
|
+
writes nothing — tell the user (a .docx can be offered; never swap formats silently).
|
|
53
|
+
|
|
54
|
+
Anything past these shapes — an exotic layout, a styled workbook — is a doc-run script
|
|
55
|
+
(**Custom scripts**), not an edit of the bundled bin.
|
|
56
|
+
|
|
57
|
+
## Formulas — the xlsx repair loop (mandatory)
|
|
58
|
+
|
|
59
|
+
Write live formulas (`"=SUM(A2:A9)"`), never precomputed values — the file must
|
|
60
|
+
recalculate when the user edits data. doc-gen and exceljs write formulas without cached
|
|
61
|
+
results, so close the loop:
|
|
62
|
+
|
|
63
|
+
1. `node <skill>/bin/xlsx-recalc.cjs out.xlsx out.xlsx` — evaluates every formula and
|
|
64
|
+
caches the results.
|
|
65
|
+
2. Its `errors` array must be **empty** — it exits 0 either way, so read it. A
|
|
66
|
+
`#DIV/0!`, `#REF!` or `#N/A` → fix the formula (guard divisors with IF, correct the
|
|
67
|
+
range) and recalc again. **Never deliver a spreadsheet with formula errors**, and never
|
|
68
|
+
"fix" one by replacing the formula with a hardcoded value.
|
|
69
|
+
|
|
70
|
+
`xlsx-recalc` (HyperFormula) covers ~400 functions — all common math/stat/text/date/lookup;
|
|
71
|
+
gaps are some exotic financial functions and dynamic-array spilling. `#NAME?` on a function
|
|
72
|
+
you know is valid Excel is that engine limit: say so and compute that value with
|
|
73
|
+
`xlsx-query` or a script instead of guessing.
|
|
74
|
+
|
|
75
|
+
## Themes — the look of a doc-gen document (docx / pdf)
|
|
76
|
+
|
|
77
|
+
Decks use **skins** (`--style`, the deck route), not these themes. A theme is a JSON file
|
|
78
|
+
of colours/fonts/page setup; content (the spec) stays separate. This skill ships **12
|
|
79
|
+
built-ins** in `themes/` (`themes/README.md` gives each one's best fit): `cool-tech` ·
|
|
80
|
+
`apple-keynote` · `mckinsey-consulting` · `anthropic-clay` · `black-gold` · `hand-drawn` ·
|
|
81
|
+
`mono` · `warm-editorial` · `ivory-gold` · `cinereel` · `magazine-ink` · `swiss-grid`. For a
|
|
82
|
+
plain Word/PDF report `mckinsey-consulting` (white · deep navy · structured tables) is a
|
|
83
|
+
safe default; `mono` for a stark minimal look. Pass a built-in by path:
|
|
94
84
|
|
|
95
85
|
```
|
|
96
|
-
node <skill>/bin/
|
|
97
|
-
# replacements.json: { "{{region}}": "华东区", "{{owner}}": "Alice" }
|
|
86
|
+
node <skill>/bin/doc-gen.cjs docx out.docx spec.json --theme <skill>/themes/mckinsey-consulting.json
|
|
98
87
|
```
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
**
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
88
|
+
|
|
89
|
+
`--palette <id>` forces one palette (`doc-gen --list-palettes`); `--accent <#rrggbb>` or
|
|
90
|
+
`--style <deck style id>` keeps white paper with one brand colour (bin-reference.md
|
|
91
|
+
"Generate" has the precedence).
|
|
92
|
+
|
|
93
|
+
**Choosing with the user:** (1) a remembered document-theme preference or a theme the
|
|
94
|
+
user saved (`ls "$TALOS_DOC_STYLES_DIR/themes"`) wins — use it and say so ("按你常用的 X
|
|
95
|
+
主题,要换告诉我"); (2) a named style → match by 适用场景 in `themes/README.md` and apply;
|
|
96
|
+
(3) they want to choose → shortlist 2–4 by 适用场景 (a text shortlist is fine). To show
|
|
97
|
+
them, render a 1-page sample per candidate (`doc-gen pdf … --theme <path>` — its preview
|
|
98
|
+
PNG is the real look) and ask with `ask_user_question` type `single_choice`, one option
|
|
99
|
+
per theme `{ label: 主题名, description: 一句适用场景, imagePath: 样片PNG绝对路径 }`;
|
|
100
|
+
(4) after an explicit confirmation, save the choice to memory as their document-theme
|
|
101
|
+
preference, and update it when they switch.
|
|
102
|
+
|
|
103
|
+
**User-created themes** are the supported way to make a new template: write a theme.json
|
|
104
|
+
in the working folder and iterate (invalid fields fall back with warnings on stderr —
|
|
105
|
+
read and fix them). When the user hands you brand colours/logo or says "以后都用这个风格",
|
|
106
|
+
offer to keep it: save it as `$TALOS_DOC_STYLES_DIR/themes/<descriptive-name>.json`
|
|
107
|
+
(kebab-case, with a `bestFor` line) — their own styles, kept across sessions and shared
|
|
108
|
+
by every expert — and apply it by name, `--theme <descriptive-name>`. Never write inside
|
|
109
|
+
the skill directory: it is an integrity-checked install, reinstalled (your file lost)
|
|
110
|
+
once it changes. **An existing deck's colours** seed one:
|
|
111
|
+
`pptx-theme.cjs ref.pptx ref-theme.json` → `--theme ref-theme.json` (palette/fonts only).
|
|
112
|
+
|
|
113
|
+
Theme fields (all optional; an invalid value falls back to the default and warns on
|
|
114
|
+
stderr, never fails): `bestFor` (one-line 适用场景) · `fonts{heading,body}` ·
|
|
115
|
+
`colors{primary,accent,secondary,text,muted,tableHeaderBg,tableBorder}` (`accent`
|
|
116
|
+
defaults to `primary`, `secondary` to `muted`) · `page{size:A4|Letter,margin,header,footer}`
|
|
117
|
+
(`margin` is a CSS length string such as `margin:"20mm"` — mm/cm/in/pt/px; a bare number is not accepted and falls back to the default margin, which changes the page count;
|
|
118
|
+
`{title}` `{page}` `{pages}` interpolate in header/footer; **prefer Latin/digits there —
|
|
119
|
+
CJK in a PDF header/footer renders blank**) · `heading{h1..h4}{size,color,bold,spaceAfter}`
|
|
120
|
+
(color: hex or a palette key like `"primary"`) · `body{size,lineHeight,justify}` ·
|
|
121
|
+
`cover{enabled,subtitle,date,logo}` (base64 logo; the title moves onto the cover) ·
|
|
122
|
+
`pptx{…}` (read only by the legacy `html2pptx --theme`; ignore for documents). A spec may
|
|
123
|
+
embed `"theme": {...}`; `--theme` wins. In a **pdf** the theme becomes CSS + cover +
|
|
124
|
+
live page headers/footers; in a **docx**, real styles (Heading1–4, default font, table
|
|
125
|
+
header shading) + header/footer parts + cover.
|
|
126
|
+
|
|
127
|
+
## Fill a template — `bin/doc-fill` (keeps the template's styling)
|
|
128
|
+
|
|
129
|
+
When the user has (or wants) a styled **template** with `{{placeholder}}` tokens, this is
|
|
130
|
+
the lossless way to fill it — fonts, tables, headers and layout stay verbatim.
|
|
124
131
|
|
|
125
132
|
```
|
|
126
|
-
node <skill>/bin/
|
|
133
|
+
node <skill>/bin/doc-fill.cjs <template.docx|pptx|xlsx> <values.json> <out>
|
|
127
134
|
```
|
|
128
135
|
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
- Values: a JSON object. `{{name}}` ← `name`; nested `{{a.b}}` ← `{"a":{"b":…}}`.
|
|
137
|
+
- Loops (.docx/.pptx): `{{#rows}} … {{col}} … {{/rows}}` over `{"rows":[{...}, ...]}`.
|
|
138
|
+
- Prints `{ ok, out, unfilled }`: `unfilled` lists tokens left blank — surface them, never
|
|
139
|
+
call the document complete. Tokens Word split across runs are still filled; a malformed
|
|
140
|
+
or unclosed tag exits 1 and writes nothing.
|
|
141
|
+
- **.xlsx**: tokens are filled cell by cell (`{{a}}`, `{{a.b}}`), each cell keeping its
|
|
142
|
+
style; a cell that is exactly one token takes the value's type, so pass numbers as
|
|
143
|
+
numbers. No loops — build repeated rows with `doc-gen xlsx` (or exceljs via doc-run).
|
|
144
|
+
- No template yet → generate a first version with `doc-gen`, then fill that.
|
|
131
145
|
|
|
132
|
-
|
|
146
|
+
Then `doc-validate` and render-and-look (SKILL.md "After writing a file").
|
|
133
147
|
|
|
134
|
-
|
|
135
|
-
user explicitly wants their new deck to KEEP an existing deck's look (their exact
|
|
136
|
-
layouts/master/brand) — i.e. they chose it as their **style** (the Q3 "复用这份 PPT
|
|
137
|
-
的风格" custom path). If the `.pptx` was handed in as **content** (they just want its
|
|
138
|
-
text in a NEW deck), do NOT tokenize — extract its text (`.txt` sidecar / `doc-text`)
|
|
139
|
-
and author fresh in the chosen skin. Same file, two intents; the user's answer to the
|
|
140
|
-
style question (Q3), not the file type, decides.
|
|
148
|
+
## Edit an existing .docx
|
|
141
149
|
|
|
142
|
-
|
|
143
|
-
master, brand — NOT one of our themes) — turn the deck into a fillable template
|
|
144
|
-
instead of guessing a theme:
|
|
150
|
+
Pick the least lossy path, and tell the user which trade-off you took:
|
|
145
151
|
|
|
146
|
-
1. **
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
1. **Change text** → `doc-xml replace` (cross-run, keeps formatting; body incl. tables,
|
|
153
|
+
headers/footers, footnotes/endnotes — not comments). One call per string, matched within
|
|
154
|
+
one paragraph; `replaced: 0` means the text is not there as written — copy the exact
|
|
155
|
+
wording from the sidecar.
|
|
156
|
+
```
|
|
157
|
+
node <skill>/bin/doc-xml.cjs replace in.docx out.docx "旧措辞" "新措辞"
|
|
158
|
+
```
|
|
159
|
+
2. **`{{placeholders}}`** → **Fill a template** above.
|
|
160
|
+
3. **Anything structural** (no targeted tool fits) → raw XML. A .docx is a zip of XML
|
|
161
|
+
parts that reference each other through `_rels/*.rels`, each listed in
|
|
162
|
+
`[Content_Types].xml` — add or rename a part and update both. Coalesce split runs
|
|
163
|
+
first so your find hits:
|
|
164
|
+
```
|
|
165
|
+
node <skill>/bin/doc-xml.cjs merge-runs report.docx merged.docx
|
|
166
|
+
node <skill>/bin/doc-xml.cjs unpack merged.docx unpacked/ # XML pretty-printed
|
|
167
|
+
# … edit unpacked/word/document.xml …
|
|
168
|
+
node <skill>/bin/doc-xml.cjs pack unpacked/ report-edited.docx
|
|
169
|
+
node <skill>/bin/doc-validate.cjs report-edited.docx --strict # MUST pass before delivering
|
|
170
|
+
```
|
|
171
|
+
4. **Content-first rewrite** → regenerate with `doc-gen` from the edited text — the
|
|
172
|
+
original styling is lost.
|
|
173
|
+
|
|
174
|
+
### Review: comments and tracked changes — `bin/docx-annotate`
|
|
175
|
+
|
|
176
|
+
- **Comments**: `docx-annotate.cjs comment in.docx out.docx comments.json` with
|
|
177
|
+
`[{ "author": "…", "text": "…", "anchor"?: "…", "replyTo"?: 0 }]`. `anchor` is a
|
|
178
|
+
substring of ONE paragraph (the first paragraph containing it gets the comment; no
|
|
179
|
+
anchor → the document's first paragraph). An anchor that matches nothing is NOT
|
|
180
|
+
written: stdout reports `written` and `skipped` (`[{ index, anchor }]`) and stderr warns;
|
|
181
|
+
add `--strict-anchors` to exit 1 on any skip (all unmatched always exits 1). Copy anchors
|
|
182
|
+
verbatim from the text. `replyTo` = index of another comment in the same file, shown as
|
|
183
|
+
a threaded reply (a reply to a skipped comment warns and is written top-level).
|
|
184
|
+
- **Suggest an addition**: `docx-annotate.cjs suggest in.docx out.docx "<author>" "<text>"`
|
|
185
|
+
appends ONE new paragraph at the end of the body (before the final section properties)
|
|
186
|
+
as a tracked insertion — it cannot target a position. Rejecting it removes the paragraph.
|
|
187
|
+
- **Tracked change in place** (replace or delete wording): raw XML (path 3 above) — wrap
|
|
188
|
+
the old run in `<w:del>` (its `<w:t>` becomes `<w:delText>`) and the new one in `<w:ins>`:
|
|
189
|
+
```xml
|
|
190
|
+
<w:del w:id="9101" w:author="Claude" w:date="2026-01-01T00:00:00Z"><w:r><w:delText>旧</w:delText></w:r></w:del><w:ins w:id="9102" w:author="Claude" w:date="2026-01-01T00:00:00Z"><w:r><w:t>新</w:t></w:r></w:ins>
|
|
191
|
+
```
|
|
192
|
+
(`w:id` unique in the document; keep the run's `<w:rPr>` in both to keep its format.)
|
|
193
|
+
- **After ANY redline edit**: `docx-annotate.cjs redline-check original.docx edited.docx`
|
|
194
|
+
rejects every tracked change and requires the original text back exactly — the proof
|
|
195
|
+
that nothing changed untracked. Exit 1 prints `firstDivergence`; redo that edit as a
|
|
196
|
+
tracked change.
|
|
197
|
+
- **Accept all**: `docx-annotate.cjs accept in.docx out.docx` keeps insertions, drops
|
|
198
|
+
deletions and format-change records, and prints the counts. A deleted paragraph mark
|
|
199
|
+
keeps both paragraphs (content is never lost).
|
|
200
|
+
|
|
201
|
+
## Template decks — fill or restructure an existing .pptx
|
|
202
|
+
|
|
203
|
+
**Which tool**: keep an uploaded deck's LAYOUTS for new content → `pptx-tokenize` →
|
|
204
|
+
`doc-fill`; repeat one styled slide per data item → `pptx-clone`; add, duplicate, drop or
|
|
205
|
+
reorder slides → `pptx-slide`; only its COLOURS → `pptx-theme` into a user skin
|
|
206
|
+
(deck-design.md "Custom skins"). Always `doc-validate` the result, then render-and-look.
|
|
207
|
+
|
|
208
|
+
### Reuse an uploaded deck — `pptx-tokenize` → `doc-fill`
|
|
209
|
+
|
|
210
|
+
Only when the user chose the deck as their **style** (the picker's "使用我的模板文件"); a
|
|
211
|
+
deck handed in as **content** is read for its text and authored fresh in the chosen skin.
|
|
212
|
+
|
|
213
|
+
1. **Tokenize** — every content slot's text (placeholders and plain text boxes) becomes a
|
|
214
|
+
clean single-run `{{token}}`; styling/master/layouts stay verbatim:
|
|
149
215
|
```
|
|
150
216
|
node <skill>/bin/pptx-tokenize.cjs user.pptx template.pptx schema.json
|
|
151
217
|
```
|
|
152
|
-
`schema.json`
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
unfilled
|
|
160
|
-
slots take a `\n`-joined string (renders as line breaks). Respect
|
|
161
|
-
`capacityChars` — rewrite shorter rather than overflow (CJK counts double).
|
|
218
|
+
`schema.json` = `{ slides: [{ index, tokens: [{ token, role, type, multiline?,
|
|
219
|
+
original?, capacityChars?, rel? }] }] }` — `role` is title / subtitle / body / text /
|
|
220
|
+
pic / chart; `type` is `text`, `image` or `chart`.
|
|
221
|
+
2. **Fill** — `values.json` has one key per `text` token (the key is the token name, no
|
|
222
|
+
braces). **Fill EVERY token**: a slot you keep gets its `original` back (an unfilled
|
|
223
|
+
token renders EMPTY). `multiline:true` takes a `\n`-joined string. Respect
|
|
224
|
+
`capacityChars` (CJK-width units; a Latin letter ≈ ½) — rewrite shorter rather than
|
|
225
|
+
overflow. `unfilled` in the output names tokens you missed — add them and refill.
|
|
162
226
|
```
|
|
163
227
|
node <skill>/bin/doc-fill.cjs template.pptx values.json out.pptx
|
|
164
228
|
```
|
|
165
|
-
|
|
166
|
-
tokens (they blanked); add those keys and refill.
|
|
167
|
-
|
|
168
|
-
3. **Swap images** — schema `type:"image"` tokens carry the slide + `rel`:
|
|
229
|
+
3. **Images** — an `image` token carries its slide and `rel`:
|
|
169
230
|
```
|
|
170
231
|
node <skill>/bin/pptx-set-image.cjs out.pptx <slide> <rel> new.png final.pptx
|
|
171
232
|
```
|
|
172
|
-
The
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
4. **Charts** keep their look but hold the SOURCE deck's embedded data (schema
|
|
176
|
-
marks them `type:"chart"`, not a fill token) — edit the chart part separately,
|
|
177
|
-
or tell the user the chart numbers are still from their file.
|
|
178
|
-
|
|
179
|
-
5. **`doc-validate`** the result, then render-and-look (doc-convert → PDF).
|
|
233
|
+
The frame stays put; a different aspect ratio STRETCHES — pre-crop to the slot's ratio.
|
|
234
|
+
4. **Charts** keep the source deck's embedded data (`type:"chart"`, not a fill token) —
|
|
235
|
+
edit the chart part separately, or tell the user the numbers are still theirs.
|
|
180
236
|
|
|
181
|
-
|
|
182
|
-
template slide — the `pptx-clone` workflow above now applies (clone N times, fill
|
|
183
|
-
each, drop the template slide).
|
|
184
|
-
|
|
185
|
-
**Which path**: reuse their LAYOUTS → this (B2). Reuse only their COLORS in OUR
|
|
186
|
-
layouts → `pptx-theme` (extract palette) + our families. Repeat a styled slide
|
|
187
|
-
per data row → `pptx-clone`.
|
|
188
|
-
|
|
189
|
-
All-Node generation (no Python): `docx`, `exceljs`, and Chromium for PDF; decks go
|
|
190
|
-
through html2pptx. Same capability tier as the Anthropic docx/pptx/xlsx skills.
|
|
191
|
-
|
|
192
|
-
## Generate — `bin/doc-gen` (self-contained; no npm install, works offline)
|
|
237
|
+
### One styled slide per data item — `pptx-clone`
|
|
193
238
|
|
|
194
239
|
```
|
|
195
|
-
node <skill>/bin/
|
|
240
|
+
node <skill>/bin/pptx-clone.cjs template.pptx 1 out.pptx replacements.json
|
|
241
|
+
# replacements.json: { "{{region}}": "华东区", "{{owner}}": "Alice" }
|
|
196
242
|
```
|
|
197
|
-
Simple spec shapes:
|
|
198
|
-
- **docx / pdf**: `{ "title"?: "...", "paragraphs": ["...", ...] }`
|
|
199
|
-
- **xlsx**: `{ "sheets": { "SheetName": [["h1","h2"], ["a", 1], ...] } }` (row 1 = bold header)
|
|
200
|
-
|
|
201
|
-
`doc-gen pptx` exits 64 and points at html2pptx: decks are laid out as HTML
|
|
202
|
-
(deck-design.md), never generated from a slide spec.
|
|
203
|
-
|
|
204
|
-
`doc-gen` validates these shapes **strictly before rendering**: unknown fields are
|
|
205
|
-
errors, not ignored. For DOCX/PDF use `paragraphs` and/or `blocks`—never
|
|
206
|
-
`sections`. A title-only artifact also fails unless it is intentional and the
|
|
207
|
-
spec explicitly sets `"allowCoverOnly": true`; on `DOC_SPEC_INVALID`, correct
|
|
208
|
-
the reported JSON path and rerun rather than delivering the previous output.
|
|
209
|
-
|
|
210
|
-
Rich spec shapes (use these for real documents, not just prose):
|
|
211
|
-
- **docx** `blocks`: ordered list of
|
|
212
|
-
- `{ "type": "heading", "text": "...", "level": 1..4 }`
|
|
213
|
-
- `{ "type": "paragraph", "text": "...", "bold"?, "italic"? }`
|
|
214
|
-
- `{ "type": "list", "items": ["..."], "ordered"?: true }`
|
|
215
|
-
- `{ "type": "table", "rows": [["h"],["a"]], "header"?: true, "columnWidths"?: [3000, ...] }` (DOCX widths are twips; defaults to full-page equal-width fixed table)`
|
|
216
|
-
- `{ "type": "image", "data": "<base64>", "imageType"?: "png", "width"?, "height"? }`
|
|
217
|
-
- **xlsx** per sheet, either the row array above OR
|
|
218
|
-
`{ "rows": [...], "header"?, "columnWidths"?: [n...], "numFmt"?: ["#,##0.00"...] }`.
|
|
219
|
-
A cell string starting with `=` is written as a **live formula** (e.g. `"=SUM(A2:A3)"`).
|
|
220
|
-
Under the hood it uses `docx` / `exceljs`, and prints PDF in Chromium — the same
|
|
221
|
-
tier as the Anthropic docx/pptx/xlsx skills, but all-Node (no Python /
|
|
222
|
-
LibreOffice) and bundled into the script. For anything past these shapes, write a
|
|
223
|
-
short Node script using those libraries directly (they are on the skill's path).
|
|
224
|
-
|
|
225
|
-
**PDF rendering (always Chromium, one layout everywhere):** `doc-gen pdf`
|
|
226
|
-
renders your spec as themed HTML and prints it in Chromium — professional CJK
|
|
227
|
-
line-breaking, per-glyph font fallback (Latin + 中文 mixed just works),
|
|
228
|
-
justification, tables, clickable links that wrap inside the page, and the
|
|
229
|
-
theme's page size and margin. Inside Talos it goes through the app's service
|
|
230
|
-
(`[pdf (chromium)]`); elsewhere it uses a headless Chromium
|
|
231
|
-
(`[pdf (headless chromium)]`, needs Playwright). With neither it exits 69 and
|
|
232
|
-
writes nothing — tell the user the PDF could not be produced. The spec's
|
|
233
|
-
`blocks` (headings/lists/tables) are supported, not just `title`/`paragraphs`.
|
|
234
|
-
|
|
235
|
-
## Fill a template — `bin/doc-fill` (preserves styling; the best "edit" path)
|
|
236
|
-
|
|
237
|
-
When the user has (or wants) a styled **template** with `{{placeholder}}` tokens,
|
|
238
|
-
this is the lossless way to produce a filled document — the original fonts,
|
|
239
|
-
tables, headers and layout are kept verbatim.
|
|
240
243
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
- Works across .docx/.pptx/.xlsx and correctly fills placeholders that Word split
|
|
249
|
-
across runs (a naive find/replace misses those). Malformed/unclosed tags fail
|
|
250
|
-
loudly — no half-rendered file.
|
|
244
|
+
The clone is appended at the end; the template slide keeps its tokens for the next clone
|
|
245
|
+
(chain the output for N items). Surface `missing` tokens instead of pretending they
|
|
246
|
+
filled. For N items with no leftover token slide: clone N-1 times, then `doc-fill` the
|
|
247
|
+
whole deck with the LAST item's values — that fills the template slide (it becomes item
|
|
248
|
+
N) plus global tokens (`{{company}}` on cover/footer) in one pass. Or clone N times and
|
|
249
|
+
drop the template: `pptx-clone.cjs remove out.pptx 1 final.pptx`. To fill a deck ONCE (no
|
|
250
|
+
repetition), `doc-fill` the .pptx directly.
|
|
251
251
|
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
- **CAPACITY warnings** (`CAPACITY {{token}}: … likely overflow` on stderr): rewrite the
|
|
253
|
+
value shorter — never shrink the font (breaks the type hierarchy), never truncate with
|
|
254
|
+
an ellipsis.
|
|
255
|
+
- **Slots ≠ item count**: more slots than content (a 4-person team page for 3 people) →
|
|
256
|
+
delete the WHOLE leftover group — picture + name + title — via raw XML (remove its
|
|
257
|
+
`<p:sp>`/`<p:grpSp>`); emptying just the text leaves orphaned portraits. More items
|
|
258
|
+
than slots → another cloned slide, not squeezed-in lines.
|
|
254
259
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
Structural changes to an existing `.pptx` that need NO `{{token}}` template and
|
|
258
|
-
preserve every slide's styling/master (the OOXML rels/Content_Types/sldIdLst
|
|
259
|
-
bookkeeping is done for you — lossless):
|
|
260
|
+
### Slide surgery — `pptx-slide`
|
|
260
261
|
|
|
261
262
|
```
|
|
262
263
|
node <skill>/bin/pptx-slide.cjs add in.pptx out.pptx [--layout N] # append a blank slide (inherits a layout)
|
|
263
264
|
node <skill>/bin/pptx-slide.cjs dup in.pptx 2 out.pptx # duplicate slide 2 (copy appended at end)
|
|
264
265
|
node <skill>/bin/pptx-slide.cjs remove in.pptx 5 out.pptx # drop slide 5
|
|
265
|
-
node <skill>/bin/pptx-slide.cjs reorder in.pptx 3,1,2 out.pptx #
|
|
266
|
+
node <skill>/bin/pptx-slide.cjs reorder in.pptx 3,1,2 out.pptx # new order, as current 1-based positions
|
|
266
267
|
```
|
|
267
268
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
replacements. `reorder` rewrites only `<p:sldIdLst>` (no parts move). This is the
|
|
272
|
-
"edit any deck in place" toolkit for STRUCTURE; for in-slide TEXT edits use
|
|
273
|
-
`doc-fill` (templated) or `doc-xml` (raw). Always `doc-validate` the output.
|
|
274
|
-
|
|
275
|
-
## Other edits (all lossy — pick per goal)
|
|
276
|
-
|
|
277
|
-
Editing arbitrary content *in place while preserving layout* (no placeholders) is
|
|
278
|
-
an unsolved problem. The realistic paths:
|
|
279
|
-
|
|
280
|
-
1. **Fill a template** — above; lossless when the doc is (or can be made) a
|
|
281
|
-
`{{placeholder}}` template. Prefer this.
|
|
282
|
-
2. **Extract → rewrite → regenerate** — `doc-text`, edit the text, regenerate a
|
|
283
|
-
fresh file. Loses original styling. Best for content-first edits.
|
|
284
|
-
3. **PDF page ops** — `bin/pdf-ops` merges/splits/rotates/watermarks existing PDFs
|
|
285
|
-
without re-rendering (preserves every page verbatim). Additive/structural only.
|
|
286
|
-
4. **Overlay / annotate** — stamp marks on an existing PDF with `pdf-lib`.
|
|
287
|
-
Preserves the original; additive only.
|
|
288
|
-
|
|
289
|
-
State which trade-off you took so the user isn't surprised by lost formatting.
|
|
269
|
+
Lossless; rels/Content_Types/sldIdLst bookkeeping is done for you. `add` binds the last
|
|
270
|
+
slide's layout unless `--layout N`; its body is empty — fill it with `doc-xml`. In-slide
|
|
271
|
+
TEXT goes through `doc-fill` (templated) or `doc-xml` (raw).
|
|
290
272
|
|
|
291
273
|
## Custom scripts — `bin/doc-run` (the long-tail escape hatch)
|
|
292
274
|
|
|
293
|
-
When
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
so this works on ANY machine (packaged app, clean install, no node_modules):
|
|
275
|
+
When no verb or spec block expresses it (an exotic layout, a bulk transformation, a
|
|
276
|
+
styled workbook), write a CommonJS Node script and run it with `doc-run` — the engine's
|
|
277
|
+
pure-JS libraries are bundled inside it, so it works on any machine:
|
|
297
278
|
|
|
298
279
|
```
|
|
299
280
|
node <skill>/bin/doc-run.cjs myscript.js [args…]
|
|
300
281
|
node <skill>/bin/doc-run.cjs --list # the guaranteed module registry
|
|
301
282
|
```
|
|
302
283
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
`
|
|
308
|
-
|
|
309
|
-
`bin/xlsx-recalc.cjs` on the file when the values must show before Excel
|
|
310
|
-
recalculates. Nothing else is guaranteed — do not
|
|
311
|
-
`require` other packages, and never run `npm install`.
|
|
312
|
-
|
|
313
|
-
Rules of the tier ladder (try in this order, don't skip levels). **NOTE — decks
|
|
314
|
-
are the exception: a deck DEFAULTS to html2pptx (SKILL.md routing), not to
|
|
315
|
-
doc-gen; the ladder below is the escalation order for the long-tail / non-deck
|
|
316
|
-
custom case (exotic layouts, bulk transforms, bespoke workbooks).**
|
|
317
|
-
1. **Built-in verbs / spec blocks** (doc-gen, doc-fill, pptx-clone, docx-annotate) — stable, themed, tested.
|
|
318
|
-
2. **html2pptx** (decks — the default deck path; in-app or bundled-headless) — think in HTML/CSS layout, get native editable slides (see deck-design.md).
|
|
319
|
-
3. **`doc-run` custom script** — full library power, themed by reading the theme JSON yourself (see deck-design.md).
|
|
320
|
-
4. **Raw XML** (doc-xml unpack/pack) — when even the libraries can't express it.
|
|
321
|
-
|
|
322
|
-
And the one unbreakable rule: **every produced file goes through
|
|
323
|
-
`bin/doc-validate.cjs` before delivery — with `--strict` for anything from
|
|
324
|
-
tiers 2–4** (custom code has no safety net except that gate; --strict is the
|
|
325
|
-
full ECMA-376 schema check).
|
|
326
|
-
|
|
327
|
-
## Raw XML editing (advanced escape hatch)
|
|
328
|
-
|
|
329
|
-
When no targeted tool (doc-fill / docx-annotate / pptx-clone) covers an edit:
|
|
330
|
-
|
|
331
|
-
```
|
|
332
|
-
node <skill>/bin/doc-xml.cjs unpack report.docx unpacked/ # XML pretty-printed
|
|
333
|
-
# … edit unpacked/word/document.xml …
|
|
334
|
-
node <skill>/bin/doc-xml.cjs pack unpacked/ report-edited.docx
|
|
335
|
-
node <skill>/bin/doc-validate.cjs report-edited.docx # MUST pass before delivering
|
|
336
|
-
```
|
|
284
|
+
`require()` of these is guaranteed: `docx` (Word) · `exceljs` (formatted workbooks:
|
|
285
|
+
fills, fonts, borders, merges, formulas — then **Formulas**) · `xlsx` (SheetJS: reading
|
|
286
|
+
and plain-data workbooks only — it writes no cell styles) · `pdf-lib` (PDF assembly) ·
|
|
287
|
+
`jszip` / `pizzip` (OOXML zip surgery) · `hyperformula` (formula evaluation) ·
|
|
288
|
+
`pptxgenjs` (non-deck pptx only — every deck goes through html2pptx). Nothing else: no
|
|
289
|
+
other package, never `npm install`. Theme the output by reading a theme JSON yourself.
|
|
337
290
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
Text is often split across multiple `<w:r>` runs — for a .docx, run
|
|
342
|
-
`doc-xml.cjs merge-runs in.docx merged.docx` FIRST to coalesce same-format
|
|
343
|
-
fragments ("He"+"llo"→"Hello"), then unpack; your find/replace will hit.
|
|
344
|
-
|
|
345
|
-
## Review workflow (comments + tracked changes)
|
|
346
|
-
|
|
347
|
-
- Comment threads: give a comment `"replyTo": <index-in-this-batch>` in
|
|
348
|
-
comments.json and Word shows it as a reply; resolved-state/author-identity
|
|
349
|
-
parts are written automatically.
|
|
350
|
-
- `docx-annotate.cjs suggest` adds a tracked insertion the user can
|
|
351
|
-
accept/reject in Word; `docx-annotate.cjs accept in.docx out.docx` accepts
|
|
352
|
-
ALL tracked changes (insertions kept, deletions dropped, format-change
|
|
353
|
-
records cleared) and prints the counts. A deleted *paragraph mark* keeps both
|
|
354
|
-
paragraphs (documented limitation — content is never lost).
|
|
355
|
-
- **After ANY redline edit** (suggest, or hand-written `<w:ins>`/`<w:del>` via
|
|
356
|
-
doc-xml): run `docx-annotate.cjs redline-check original.docx edited.docx`.
|
|
357
|
-
It rejects every tracked change and requires the result to equal the
|
|
358
|
-
original text exactly — the mathematical proof that you changed nothing
|
|
359
|
-
without tracking it. Non-zero exit prints the first diverging paragraph;
|
|
360
|
-
redo that edit as a tracked change.
|
|
291
|
+
Order of escalation: built-in verbs → `doc-run` → raw XML (`doc-xml`). Custom code has no
|
|
292
|
+
safety net but the gate: **`doc-validate --strict` every file it produces** (the full
|
|
293
|
+
ECMA-376 schema check).
|