dsh-office-kit 0.4.2 → 0.4.3
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/assets/skills/excel-live-control/SKILL.md +119 -152
- package/assets/skills/excel-live-control/domain_guidance/financial_models.md +42 -58
- package/assets/skills/excel-live-control/domain_guidance/healthcare.md +36 -42
- package/assets/skills/excel-live-control/domain_guidance/marketing_advertising.md +33 -34
- package/assets/skills/excel-live-control/domain_guidance/scientific_research.md +34 -36
- package/assets/skills/excel-live-control/references/office-automation.md +127 -153
- package/assets/skills/excel-live-control/references/read_only_qna.md +40 -45
- package/assets/skills/excel-live-control/style_guidelines.md +56 -64
- package/assets/skills/excel-live-control/workflows/create_workflows.md +40 -47
- package/assets/skills/excel-live-control/workflows/edit_workflows.md +37 -45
- package/assets/skills/office-documents/SKILL.md +24 -60
- package/assets/skills/office-documents/examples/end_to_end_smoke_test.md +5 -5
- package/assets/skills/office-documents/ooxml/comments.md +8 -8
- package/assets/skills/office-documents/ooxml/hyperlinks_and_fields.md +10 -10
- package/assets/skills/office-documents/ooxml/rels_and_content_types.md +7 -7
- package/assets/skills/office-documents/ooxml/tracked_changes.md +12 -12
- package/assets/skills/office-documents/references/docx-structure.md +11 -13
- package/assets/skills/office-documents/references/template-elicitation.md +11 -13
- package/assets/skills/office-documents/references/writing-quality.md +20 -20
- package/assets/skills/office-documents/tasks/accessibility_a11y.md +16 -22
- package/assets/skills/office-documents/tasks/captions_crossrefs.md +23 -26
- package/assets/skills/office-documents/tasks/clean_tracked_changes.md +17 -22
- package/assets/skills/office-documents/tasks/comments_manage.md +9 -11
- package/assets/skills/office-documents/tasks/compare_diff.md +10 -16
- package/assets/skills/office-documents/tasks/create_edit.md +14 -16
- package/assets/skills/office-documents/tasks/equations.md +17 -28
- package/assets/skills/office-documents/tasks/fields_update.md +14 -16
- package/assets/skills/office-documents/tasks/fixtures_edge_cases.md +19 -33
- package/assets/skills/office-documents/tasks/footnotes_endnotes.md +12 -14
- package/assets/skills/office-documents/tasks/forms_content_controls.md +16 -20
- package/assets/skills/office-documents/tasks/headings_numbering.md +17 -22
- package/assets/skills/office-documents/tasks/images_figures.md +15 -25
- package/assets/skills/office-documents/tasks/multi_doc_merge.md +13 -23
- package/assets/skills/office-documents/tasks/navigation_internal_links.md +18 -21
- package/assets/skills/office-documents/tasks/privacy_scrub_metadata.md +16 -22
- package/assets/skills/office-documents/tasks/protection_restrict_editing.md +19 -21
- package/assets/skills/office-documents/tasks/read_review.md +9 -20
- package/assets/skills/office-documents/tasks/redaction_anonymization.md +17 -18
- package/assets/skills/office-documents/tasks/sections_layout.md +16 -23
- package/assets/skills/office-documents/tasks/style_lint_normalize.md +18 -22
- package/assets/skills/office-documents/tasks/tables_spreadsheets.md +21 -31
- package/assets/skills/office-documents/tasks/templates_style_packs.md +17 -21
- package/assets/skills/office-documents/tasks/toc_workflow.md +10 -14
- package/assets/skills/office-documents/tasks/verify_render.md +11 -23
- package/assets/skills/office-documents/tasks/watermarks_background.md +11 -15
- package/assets/skills/office-documents/troubleshooting/libreoffice_headless.md +6 -6
- package/assets/skills/office-documents/troubleshooting/run_splitting.md +12 -12
- package/assets/skills/office-pdf/SKILL.md +36 -51
- package/assets/skills/office-pdf/references/form-fields.md +20 -20
- package/assets/skills/office-pdf/references/geometry.md +10 -10
- package/assets/skills/office-pdf/references/layout-quality.md +13 -13
- package/assets/skills/office-pdf/references/limits.md +12 -56
- package/assets/skills/office-pdf/tasks/annotations.md +5 -5
- package/assets/skills/office-pdf/tasks/create.md +6 -9
- package/assets/skills/office-pdf/tasks/edit-pages.md +14 -14
- package/assets/skills/office-pdf/tasks/forms.md +15 -15
- package/assets/skills/office-pdf/tasks/read.md +9 -9
- package/assets/skills/office-pdf/tasks/redact.md +18 -18
- package/assets/skills/office-pdf/tasks/stamp.md +10 -10
- package/assets/skills/office-presentations/SKILL.md +71 -91
- package/assets/skills/office-presentations/container_tools/render_slides.py +1 -1
- package/assets/skills/office-presentations/domain_guidance/finance.md +16 -16
- package/assets/skills/office-presentations/recipes/README.md +24 -35
- package/assets/skills/office-presentations/recipes/autolayout.md +12 -13
- package/assets/skills/office-presentations/recipes/connectors.md +11 -12
- package/assets/skills/office-presentations/recipes/images.md +11 -12
- package/assets/skills/office-presentations/recipes/master-and-layouts.md +18 -18
- package/assets/skills/office-presentations/recipes/native-charts.md +8 -8
- package/assets/skills/office-presentations/recipes/rich-text-runs.md +13 -13
- package/assets/skills/office-presentations/recipes/speaker-notes.md +7 -7
- package/assets/skills/office-presentations/recipes/tables.md +14 -14
- package/assets/skills/office-presentations/recipes/textbox-at-coordinate.md +9 -9
- package/assets/skills/office-presentations/recipes/theme-tokens.md +7 -8
- package/assets/skills/office-presentations/references/authoring-workflow.md +38 -43
- package/assets/skills/office-presentations/references/finalization.md +10 -12
- package/assets/skills/office-presentations/references/native-bullets.md +14 -18
- package/assets/skills/office-presentations/references/native-evidence.md +18 -32
- package/assets/skills/office-presentations/references/style-guidelines.md +27 -32
- package/assets/skills/office-presentations/references/templates.md +31 -60
- package/assets/skills/office-presentations/routing/google_slides.md +4 -4
- package/assets/skills/office-presentations/tasks/apply-annotations.md +17 -17
- package/assets/skills/office-presentations/tasks/charts-tables-notes.md +20 -23
- package/assets/skills/office-presentations/tasks/create-deck.md +19 -19
- package/assets/skills/office-presentations/tasks/edit-deck.md +14 -16
- package/assets/skills/office-presentations/tasks/inspect-and-verify.md +31 -34
- package/assets/skills/office-presentations/tasks/template-following.md +23 -28
- package/assets/skills/office-spreadsheets/SKILL.md +32 -39
- package/assets/skills/office-spreadsheets/recipes/charts.md +8 -26
- package/assets/skills/office-spreadsheets/recipes/conditional-formats.md +9 -26
- package/assets/skills/office-spreadsheets/recipes/data-tables.md +7 -23
- package/assets/skills/office-spreadsheets/recipes/named-ranges.md +8 -21
- package/assets/skills/office-spreadsheets/recipes/number-formats.md +7 -23
- package/assets/skills/office-spreadsheets/recipes/sparklines.md +7 -20
- package/assets/skills/office-spreadsheets/references/charts.md +15 -34
- package/assets/skills/office-spreadsheets/references/domain/financial-models.md +10 -25
- package/assets/skills/office-spreadsheets/references/domain/healthcare.md +8 -19
- package/assets/skills/office-spreadsheets/references/domain/marketing.md +8 -22
- package/assets/skills/office-spreadsheets/references/domain/research.md +8 -19
- package/assets/skills/office-spreadsheets/references/formula-patterns.md +20 -50
- package/assets/skills/office-spreadsheets/references/style-guidelines.md +19 -44
- package/assets/skills/office-spreadsheets/references/workbook-structure.md +12 -35
- package/assets/skills/office-spreadsheets/routing/google-sheets.md +11 -26
- package/assets/skills/office-spreadsheets/tasks/analyze-workbook.md +10 -25
- package/assets/skills/office-spreadsheets/tasks/annotations.md +12 -30
- package/assets/skills/office-spreadsheets/tasks/create-workbook.md +5 -5
- package/assets/skills/office-spreadsheets/tasks/diff-workbooks.md +2 -2
- package/assets/skills/office-spreadsheets/tasks/edit-workbook.md +5 -5
- package/assets/skills/office-spreadsheets/tasks/inspect-workbook.md +9 -9
- package/assets/skills/office-spreadsheets/tasks/recalc-and-check.md +10 -30
- package/assets/skills/shared/annotation-recipes.md +98 -120
- package/assets/skills/shared/common-workflow.md +70 -80
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -1,25 +1,15 @@
|
|
|
1
1
|
# Recalculate and gate consistency
|
|
2
2
|
|
|
3
|
-
A workbook written by openpyxl or by a script has formula text and no cached
|
|
4
|
-
results. Open it in a viewer that does not calculate and every formula cell shows
|
|
5
|
-
blank. `xlsx recalc` runs the real engine to fill the cache; the gate then proves
|
|
6
|
-
every formula cell got a value and none of them is an error.
|
|
3
|
+
A workbook written by openpyxl or by a script has formula text and no cached results. Open it in a viewer that does not calculate and every formula cell shows blank. `xlsx recalc` runs the real engine to fill the cache; the gate then proves every formula cell got a value and none is an error.
|
|
7
4
|
|
|
8
5
|
```bash
|
|
9
6
|
"<python>" "<cli>" xlsx recalc --input "<book.xlsx>" --out "<book-recalculated.xlsx>"
|
|
10
7
|
"<python>" "<skill>/container_tools/recalc_gate.py" --input "<book-recalculated.xlsx>"
|
|
11
8
|
```
|
|
12
9
|
|
|
13
|
-
- `recalc` needs the DSH LibreOffice kit (the same renderer used for images)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- `recalc_gate.py` runs `recalc` for you when given `--cli`: it takes
|
|
17
|
-
`--input`, writes `<stem>-recalculated.xlsx` unless `--out` is given, then
|
|
18
|
-
scans every formula cell.
|
|
19
|
-
- It exits `0` clean, `1` with failures, `2` on a usage or file error, and `3`
|
|
20
|
-
when recalculation was requested but no engine exists. Exit `3` with
|
|
21
|
-
`verdict: unverified` is an honest result — report it as "structure verified,
|
|
22
|
-
numbers not recalculated", never as "numbers verified".
|
|
10
|
+
- `recalc` needs the DSH LibreOffice kit (the same renderer used for images) and runs on Windows, macOS and Linux. Without the kit the verb fails with a plain message and changes nothing.
|
|
11
|
+
- `recalc_gate.py --cli` runs `recalc` for you: it takes `--input`, writes `<stem>-recalculated.xlsx` unless `--out` is given, then scans every formula cell.
|
|
12
|
+
- It exits `0` clean, `1` with failures, `2` on a usage or file error, and `3` when recalculation was requested but no engine exists. Exit `3` with `verdict: unverified` is an honest result — report it as "structure verified, numbers not recalculated", never as "numbers verified".
|
|
23
13
|
|
|
24
14
|
## What the gate flags
|
|
25
15
|
|
|
@@ -30,28 +20,18 @@ every formula cell got a value and none of them is an error.
|
|
|
30
20
|
| `dangling` | Formula references a sheet that does not exist | Renaming a sheet without updating formulas does this; restore the name or repoint the reference |
|
|
31
21
|
| `ignored` | Cells you passed with `--ignore` | Pre-existing errors the user asked you to leave alone |
|
|
32
22
|
|
|
33
|
-
`--strict` treats missing values as failures instead of notes, which is what you
|
|
34
|
-
want after a successful recalc. `--ignore 'Rates!B7,B9'` keeps a known,
|
|
35
|
-
user-accepted error out of the verdict without hiding the rest.
|
|
23
|
+
`--strict` treats missing values as failures instead of notes, which is what you want after a successful recalc. `--ignore 'Rates!B7,B9'` keeps a known, user-accepted error out of the verdict without hiding the rest.
|
|
36
24
|
|
|
37
25
|
## Consistency checks the gate cannot do
|
|
38
26
|
|
|
39
27
|
Recalculation proves formulas evaluate, not that they are right. Check by hand:
|
|
40
28
|
|
|
41
|
-
- Totals equal the sum of the rows above them, and detail rows add to the
|
|
42
|
-
summary — not just that both computed something.
|
|
29
|
+
- Totals equal the sum of the rows above them, and detail rows add to the summary — not just that both computed something.
|
|
43
30
|
- Percentages are stored as fractions and formatted, not typed as `6.4`.
|
|
44
|
-
- A case selector drives every tab that claims to follow the case. Two tabs
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
formula path still exists. If an override replaced a formula, the workbook can
|
|
48
|
-
no longer update.
|
|
49
|
-
- Every input the user expects to change is in a cell they can find, not inside a
|
|
50
|
-
formula constant.
|
|
31
|
+
- A case selector drives every tab that claims to follow the case. Two tabs showing different cases is the most expensive defect in a model.
|
|
32
|
+
- Hard-coded overrides are visible (a different fill or a comment) and the formula path still exists. If an override replaced a formula, the workbook can no longer update.
|
|
33
|
+
- Every input the user expects to change is in a cell they can find, not inside a formula constant.
|
|
51
34
|
|
|
52
35
|
## When there is no engine
|
|
53
36
|
|
|
54
|
-
Report exactly what happened: the file was created and structurally verified, the
|
|
55
|
-
numbers are formulas, and they will populate the first time the user opens the
|
|
56
|
-
file in Excel, LibreOffice, or Sheets. Offer to render so the user sees the layout
|
|
57
|
-
and labels. Do not invent values to make the preview look complete.
|
|
37
|
+
Report exactly what happened: the file was created and structurally verified, the numbers are formulas, and they will populate the first time the user opens the file in Excel, LibreOffice, or Sheets. Offer to render so the user sees the layout and labels. Do not invent values to make the preview look complete.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Annotation recipes
|
|
2
2
|
|
|
3
|
-
The user may reply to a rendered preview with an `office-annotations`
|
|
3
|
+
The user may reply to a rendered preview with an `office-annotations` payload: a
|
|
4
4
|
JSON array wrapped in `<office-annotations>` … `</office-annotations>` tags.
|
|
5
|
-
Each format sends a slightly different item; the shapes
|
|
6
|
-
|
|
5
|
+
Each format sends a slightly different item; these are the shapes the client
|
|
6
|
+
emits.
|
|
7
7
|
|
|
8
8
|
| Format | Item keys | `page` means |
|
|
9
9
|
| --- | --- | --- |
|
|
@@ -23,50 +23,53 @@ client actually emits.
|
|
|
23
23
|
Rules that apply everywhere:
|
|
24
24
|
|
|
25
25
|
1. Render the *current* file first and look at the same page the user annotated.
|
|
26
|
-
|
|
26
|
+
Geometry is only meaningful against a render you can reproduce.
|
|
27
27
|
2. Locate the concrete object, then apply the smallest edit that satisfies
|
|
28
|
-
`requirement`. Do not restyle the document
|
|
29
|
-
3. Re-render and look at the edited page
|
|
30
|
-
|
|
28
|
+
`requirement`. Do not restyle the document or rewrite neighbours.
|
|
29
|
+
3. Re-render and look at the edited page. The item is done when the region shows
|
|
30
|
+
the requested change and the layout still holds.
|
|
31
31
|
4. If the payload cannot be tied to an object (empty region, no geometry, no
|
|
32
32
|
matching text), say what you could not locate. Never guess and never move the
|
|
33
33
|
region to make it fit.
|
|
34
34
|
|
|
35
|
-
Worked examples
|
|
36
|
-
|
|
35
|
+
Worked examples use `report.docx`, `report.pdf`, `deck.pptx`, and `book.xlsx`,
|
|
36
|
+
plus the `python`/`cli`/`skill` values from the `## Office runtime` block.
|
|
37
37
|
|
|
38
|
-
##
|
|
39
|
-
|
|
40
|
-
`page` is the page of the **rendered PDF preview**, not a Word page number.
|
|
41
|
-
|
|
42
|
-
```bash
|
|
43
|
-
"<python>" "<cli>" docx render --input "report.docx" --out-dir "render"
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
This writes `render/page-1.png` and keeps `render/report.pdf`. In the example
|
|
47
|
-
the user circled a sentence on preview page 1 and asked to round it.
|
|
48
|
-
|
|
49
|
-
Locate it from the text, not from the pixels:
|
|
38
|
+
## Turn a region into page text
|
|
50
39
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- Use `region` only to break ties between two similar paragraphs, or to confirm
|
|
54
|
-
the preview page number. If they disagree, trust `text` and say so.
|
|
55
|
-
|
|
56
|
-
Extract the words under the region when you need confirmation. Save this as
|
|
57
|
-
`locate.py` and run it with the runtime python:
|
|
40
|
+
Save this as `region_text.py`; it prints the page rect and the words under the
|
|
41
|
+
region. Run it with the runtime python.
|
|
58
42
|
|
|
59
43
|
```python
|
|
60
44
|
import sys, pymupdf
|
|
61
45
|
pdf, page_no, l, t, w, h = sys.argv[1], int(sys.argv[2]), *map(float, sys.argv[3:7])
|
|
62
46
|
page = pymupdf.open(pdf)[page_no - 1]
|
|
63
47
|
r = page.rect
|
|
64
|
-
|
|
48
|
+
rect = pymupdf.Rect(r.x0 + l * r.width, r.y0 + t * r.height,
|
|
65
49
|
r.x0 + (l + w) * r.width, r.y0 + (t + h) * r.height)
|
|
66
|
-
print(" ".join(word[4] for word in page.get_text("words", clip=
|
|
50
|
+
print(rect, " ".join(word[4] for word in page.get_text("words", clip=rect)))
|
|
67
51
|
```
|
|
68
52
|
|
|
69
|
-
|
|
53
|
+
## docx — rendered-PDF page plus text matching
|
|
54
|
+
|
|
55
|
+
`page` is the page of the **rendered PDF preview**, not a Word page number.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
"<python>" "<cli>" docx render --input "report.docx" --out-dir "render" --pdf
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Writes `render/page-1.png` and `render/report.pdf`.
|
|
62
|
+
|
|
63
|
+
Locate it from the text, not the pixels:
|
|
64
|
+
|
|
65
|
+
- `docx read --input "report.docx"` lists paragraphs with styles. Find the one
|
|
66
|
+
containing `text`.
|
|
67
|
+
- Use `region` only to break ties or confirm the preview page; if they disagree,
|
|
68
|
+
trust `text` and say so.
|
|
69
|
+
- `region_text.py` on `render/report.pdf` confirms the words under the region.
|
|
70
|
+
|
|
71
|
+
Apply the edit and write `<original>-edited.docx` with the pack's replacement
|
|
72
|
+
tool:
|
|
70
73
|
|
|
71
74
|
```json
|
|
72
75
|
{
|
|
@@ -78,63 +81,55 @@ Apply the edit and write `<original>-edited.docx`:
|
|
|
78
81
|
```
|
|
79
82
|
|
|
80
83
|
```bash
|
|
81
|
-
"<python>" "<
|
|
84
|
+
"<python>" "<skill>/scripts/replace_text.py" --input "report.docx" --spec "edit.json" --out "report-edited.docx" --require-all
|
|
82
85
|
```
|
|
83
86
|
|
|
84
|
-
The report returns `replacements
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
The report returns `replacements` (total), per-pair counts in `pairs`, and
|
|
88
|
+
`misses`; `--require-all` exits 1 when any `find` did not match. Zero means the
|
|
89
|
+
text split across runs or differs verbatim — re-read the paragraph instead of
|
|
90
|
+
guessing. Re-render `report-edited.docx` and `read_image` page 1. A longer
|
|
91
|
+
sentence can reflow the page: check the paragraphs below the edit too.
|
|
89
92
|
|
|
90
93
|
## pdf — convert the region and work on the converted rect
|
|
91
94
|
|
|
92
95
|
`page` is a 1-based page of the PDF itself. `region × page.rect` is the real
|
|
93
|
-
place in the file, so convert once and use the device rect directly.
|
|
94
|
-
|
|
95
|
-
```python
|
|
96
|
-
import sys, pymupdf
|
|
97
|
-
pdf, page_no, l, t, w, h = sys.argv[1], int(sys.argv[2]), *map(float, sys.argv[3:7])
|
|
98
|
-
page = pymupdf.open(pdf)[page_no - 1]
|
|
99
|
-
r = page.rect
|
|
100
|
-
rect = pymupdf.Rect(r.x0 + l * r.width, r.y0 + t * r.height,
|
|
101
|
-
r.x0 + (l + w) * r.width, r.y0 + (t + h) * r.height)
|
|
102
|
-
print(rect, " ".join(word[4] for word in page.get_text("words", clip=rect)))
|
|
103
|
-
```
|
|
96
|
+
place in the file, so convert once and use the device rect directly. Run
|
|
97
|
+
`region_text.py` on `report.pdf`.
|
|
104
98
|
|
|
105
99
|
Example: page 1, region over the title, `text` "Field report", requirement
|
|
106
|
-
"make the title say Field report (revised), keep the author".
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
ambiguous or the page is scanned.
|
|
100
|
+
"make the title say Field report (revised), keep the author". If `imageName` is
|
|
101
|
+
present, `read_image` it when the text layer is ambiguous or the page is
|
|
102
|
+
scanned.
|
|
110
103
|
|
|
111
|
-
Choose the edit
|
|
104
|
+
Choose the edit the pack scripts support:
|
|
112
105
|
|
|
113
|
-
- Metadata
|
|
106
|
+
- Metadata → `metadata.py`. Repeat `--set`; unset fields keep their old values.
|
|
114
107
|
|
|
115
|
-
```
|
|
116
|
-
|
|
108
|
+
```bash
|
|
109
|
+
"<python>" "<skill>/scripts/metadata.py" --input "report.pdf" --set "title=Field report (revised)" --out "report-edited.pdf"
|
|
117
110
|
```
|
|
118
111
|
|
|
112
|
+
- Rotation, page order, merge, page removal → `page_ops.py`. Indices are
|
|
113
|
+
zero-based.
|
|
114
|
+
|
|
119
115
|
```bash
|
|
120
|
-
"<python>" "<
|
|
116
|
+
"<python>" "<skill>/scripts/page_ops.py" --input "report.pdf" --out "report-edited.pdf" --merge "cover.pdf" --order "2,0,1"
|
|
121
117
|
```
|
|
122
118
|
|
|
123
|
-
- Text or graphics **on** the page →
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
rebuild as a surgical edit.
|
|
119
|
+
- Text or graphics **on** the page → no in-place edit. Rebuild from a
|
|
120
|
+
`pdf create` spec using `pdf read` text, and say the PDF was rebuilt, not
|
|
121
|
+
patched. Never call a rebuild a surgical edit.
|
|
127
122
|
|
|
128
|
-
Verify with
|
|
123
|
+
Verify with `pdf render --input "report-edited.pdf" --out-dir "render"` and
|
|
129
124
|
`read_image render/page-1.png`. `pdf render` uses PyMuPDF and works without the
|
|
130
125
|
LibreOffice kit, so a PDF annotation is never blocked by a missing renderer.
|
|
131
126
|
|
|
132
127
|
## pptx — select the shapes that overlap the region
|
|
133
128
|
|
|
134
|
-
`page` is the rendered PDF page
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
129
|
+
`page` is the rendered PDF page; one slide per page, so page N is slide N.
|
|
130
|
+
`pptx read` returns slide text but **no shape geometry**, so compute overlap
|
|
131
|
+
yourself with python-pptx. Shape boxes and the payload region are both
|
|
132
|
+
fractions of the slide, so the comparison is direct.
|
|
138
133
|
|
|
139
134
|
```python
|
|
140
135
|
import sys
|
|
@@ -149,42 +144,37 @@ for shape in prs.slides[slide_no - 1].shapes:
|
|
|
149
144
|
print(shape.shape_id, shape.text_frame.text.replace("\n", " | "))
|
|
150
145
|
```
|
|
151
146
|
|
|
152
|
-
Example:
|
|
153
|
-
|
|
154
|
-
and the body — and `text` picks the body.
|
|
147
|
+
Example: slide 2, region over the second bullet, `text` "Renewal 92%". Two
|
|
148
|
+
shapes overlap; `text` picks the body.
|
|
155
149
|
|
|
156
|
-
Apply with the run-level replacement:
|
|
150
|
+
Apply with the pack's run-level replacement; `slide` scopes a rule:
|
|
157
151
|
|
|
158
152
|
```json
|
|
159
|
-
{ "
|
|
153
|
+
{ "replacements": [{ "find": "Renewal 92%", "replace": "Renewal 94%", "slide": 2 }] }
|
|
160
154
|
```
|
|
161
155
|
|
|
162
156
|
```bash
|
|
163
|
-
"<python>" "<
|
|
157
|
+
"<python>" "<skill>/container_tools/replace_text.py" --input "deck.pptx" --spec "edit.json" --out "deck-edited.pptx"
|
|
164
158
|
```
|
|
165
159
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
rather than leaving it clipped.
|
|
160
|
+
Without `slide`, a rule hits every slide: check `total` and `slides`, then scope
|
|
161
|
+
any unintended match. `unmatched` lists patterns that found nothing. Re-render
|
|
162
|
+
and `read_image` the page PNG for slide 2. If type overflows its shape, shrink
|
|
163
|
+
the run or shorten the text rather than leaving it clipped.
|
|
171
164
|
|
|
172
165
|
## xlsx — no cell reference, so resolve it yourself and say so
|
|
173
166
|
|
|
174
167
|
xlsx items are always `unmapped: true` with `text: ""`: you get a region, a
|
|
175
168
|
screenshot in `imageName`, and a requirement — no page, no sheet, no A1 range.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
1. **Look at the screenshot.** `read_image` the attachment
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
3. **Resolve the cells.** Match the visible values and headers from step 1
|
|
186
|
-
against the dumped rows, then derive the A1 address from the row/column
|
|
187
|
-
index:
|
|
169
|
+
Resolve the sheet and cells from evidence, never invent them.
|
|
170
|
+
|
|
171
|
+
1. **Look at the screenshot.** `read_image` the `imageName` attachment; note
|
|
172
|
+
headers, row labels, and values in the region. With no screenshot you have
|
|
173
|
+
only `region` and `requirement` — say so.
|
|
174
|
+
2. **Dump the workbook.** `xlsx read --input "book.xlsx" --json-out "book.json"`.
|
|
175
|
+
Sheets give `name`, `dimensions`, `rows` (no coordinates), and `formulas`.
|
|
176
|
+
3. **Resolve the cells.** Match the visible values and headers against the rows,
|
|
177
|
+
then derive A1 from the row/column index:
|
|
188
178
|
|
|
189
179
|
```python
|
|
190
180
|
import json, sys
|
|
@@ -198,39 +188,27 @@ The sheet and cells must be resolved from evidence, never invented.
|
|
|
198
188
|
print(sheet["name"], f"{get_column_letter(c)}{r}", value)
|
|
199
189
|
```
|
|
200
190
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
5. **Apply and verify.** Example: screenshot shows `Revenue 10 12` and the
|
|
210
|
-
requirement is "show these as currency with 2 decimals"; `xlsx read` finds
|
|
211
|
-
`Revenue` in `Q4!A2` with `10` and `12` beside it:
|
|
212
|
-
|
|
213
|
-
```json
|
|
214
|
-
{
|
|
215
|
-
"operations": [
|
|
216
|
-
{ "action": "style", "sheet": "Q4", "cells": { "B2:C2": { "number_format": "#,##0.00" } } }
|
|
217
|
-
]
|
|
218
|
-
}
|
|
219
|
-
```
|
|
191
|
+
If several sheets share the label, or the screenshot does not pin the
|
|
192
|
+
column, ask which one before editing.
|
|
193
|
+
4. **State the resolution before editing.** Say the sheet and A1 range, e.g.
|
|
194
|
+
"Revenue is `Q4!B2:C2` (10, 12); formatting those cells as currency." A wrong
|
|
195
|
+
guess is then visible before it touches the file.
|
|
196
|
+
5. **Apply and verify.** Screenshot shows `Revenue 10 12`; requirement is
|
|
197
|
+
"currency with 2 decimals"; `xlsx read` finds `Revenue` in `Q4!A2` with `10`
|
|
198
|
+
and `12` beside it:
|
|
220
199
|
|
|
221
200
|
```bash
|
|
222
|
-
"<python>" "<
|
|
201
|
+
"<python>" "<skill>/container_tools/style_cells.py" --input "book.xlsx" --out "book-edited.xlsx" \
|
|
202
|
+
--sheet "Q4" --range "B2:C2" --style '{"number_format":"#,##0.00"}'
|
|
223
203
|
```
|
|
224
204
|
|
|
225
|
-
|
|
226
|
-
`
|
|
227
|
-
|
|
228
|
-
again to confirm the values.
|
|
205
|
+
One `--range` styles the whole range. `set_cells.py` sets values/formulas,
|
|
206
|
+
`append_rows.py` adds rows, `sheet_ops.py` edits sheet structure. Read the
|
|
207
|
+
workbook back to confirm values.
|
|
229
208
|
|
|
230
|
-
Forbidden
|
|
209
|
+
Forbidden — both put a plausible edit in the wrong place:
|
|
231
210
|
|
|
232
|
-
- converting `region` pixels
|
|
233
|
-
its own zoom and scroll;
|
|
211
|
+
- converting `region` pixels to a cell address (the preview zooms and scrolls);
|
|
234
212
|
- naming a sheet the screenshot does not show or `xlsx read` cannot match.
|
|
235
213
|
|
|
236
214
|
Visual confirmation needs `xlsx render`, which requires the DSH LibreOffice kit;
|
|
@@ -242,10 +220,10 @@ platform); do not present openpyxl's empty cache as a computed value.
|
|
|
242
220
|
|
|
243
221
|
Ask one short question, listing what you did find, when:
|
|
244
222
|
|
|
245
|
-
- the region covers text
|
|
223
|
+
- the region covers text on several pages and `text` is empty;
|
|
246
224
|
- two shapes on a slide both match `text`;
|
|
247
225
|
- the xlsx screenshot does not identify the sheet, or the visible values match
|
|
248
226
|
more than one row or column;
|
|
249
|
-
- the requirement needs an operation the
|
|
250
|
-
editing in PDF, animation, comments) — offer the rebuild path
|
|
251
|
-
silent partial change.
|
|
227
|
+
- the requirement needs an operation the pack scripts do not support
|
|
228
|
+
(page-content editing in PDF, animation, comments) — offer the rebuild path
|
|
229
|
+
instead of a silent partial change.
|
|
@@ -1,139 +1,129 @@
|
|
|
1
1
|
# Shared workflow
|
|
2
2
|
|
|
3
|
-
Every format pack follows this loop. The helper CLI is an accelerator
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
commands; the order, the checks and the failure handling are the same.
|
|
3
|
+
Every format pack follows this loop. The helper CLI is an accelerator, not the
|
|
4
|
+
boundary: the format router adds spec keys and commands; the order, checks, and
|
|
5
|
+
failure handling below are the same.
|
|
7
6
|
|
|
8
7
|
## 0. Read the runtime block first
|
|
9
8
|
|
|
10
|
-
Call
|
|
11
|
-
|
|
9
|
+
Call `office_status`, then read the `## Office runtime` section appended to the
|
|
10
|
+
active `SKILL.md`. It is a fenced JSON block:
|
|
12
11
|
|
|
13
12
|
```json
|
|
14
13
|
{ "python": "...\\python.exe", "cli": "...\\python\\office_cli.py", "skill": "...\\assets\\skills\\office-documents", "shared": "...\\assets\\skills\\shared", "version": "3.13.12" }
|
|
15
14
|
```
|
|
16
15
|
|
|
17
16
|
- `python` and `cli` are verified absolute paths. Use them verbatim and quote
|
|
18
|
-
them
|
|
19
|
-
- `skill` is the
|
|
20
|
-
|
|
21
|
-
- `shared` is this directory (workflow
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
fabricate a document.
|
|
17
|
+
them. Never call a bare `python`, `py`, or `pdftoppm`.
|
|
18
|
+
- `skill` is the format pack; its scripts live under `<skill>/scripts/`,
|
|
19
|
+
`container_tools/` (pptx, xlsx), or the pack root.
|
|
20
|
+
- `shared` is this directory (cross-format workflow, recipes, shared scripts).
|
|
21
|
+
- A missing block plus *No working Python runtime is available* means stop: run
|
|
22
|
+
no helper command and fabricate no document.
|
|
25
23
|
|
|
26
|
-
`office_status`
|
|
27
|
-
|
|
24
|
+
`office_status` reports `runtime.state`, `python`, `cli`, `packages`, and the
|
|
25
|
+
kit `renderers` facts (`available`, `backend`, `remedy`). Use it when a command
|
|
26
|
+
fails. `<cli> status` additionally reports `can_render` per format.
|
|
28
27
|
|
|
29
28
|
## 1. Decide: create or edit
|
|
30
29
|
|
|
31
|
-
- No existing file → create
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
asked for): `report.docx` → `report-edited.docx`. State both paths in the
|
|
36
|
-
final summary.
|
|
30
|
+
- No existing file → create, in the user's workspace unless they name a location.
|
|
31
|
+
- Existing file → edit. **Never write over the original.** Write
|
|
32
|
+
`<original>-edited.<ext>` beside it (or the name the user asked for):
|
|
33
|
+
`report.docx` → `report-edited.docx`.
|
|
37
34
|
- "Improve this document" means read it first (`read` verb) and edit the real
|
|
38
|
-
content; do not rebuild it
|
|
35
|
+
content; do not rebuild it and drop its formatting.
|
|
36
|
+
- Never delete the original or "clean up" a temp copy over it. State both paths
|
|
37
|
+
in the final summary, original first.
|
|
39
38
|
|
|
40
|
-
## 2.
|
|
39
|
+
## 2. Read, choose a route, change
|
|
41
40
|
|
|
42
|
-
Read
|
|
43
|
-
cheapest route that actually does the job:
|
|
41
|
+
Read the file first (`read` verb), then take the cheapest route that works:
|
|
44
42
|
|
|
45
|
-
1. **CLI verb** — the common case.
|
|
46
|
-
|
|
47
|
-
`{ "ok": false, "error": "..." }` on stderr and exit non-zero.
|
|
43
|
+
1. **CLI verb** — the common case. Spec-driven; prints one JSON object on stdout.
|
|
44
|
+
Failures print `{ "ok": false, "error": "..." }` on stderr and exit non-zero.
|
|
48
45
|
|
|
49
46
|
```bash
|
|
50
47
|
"<python>" "<cli>" docx|pptx|xlsx|pdf create --spec "<spec.json>" --out "<file>"
|
|
51
48
|
"<python>" "<cli>" docx|pptx|xlsx|pdf read --input "<file>"
|
|
52
|
-
"<python>" "<cli>" docx|pptx|xlsx|pdf
|
|
53
|
-
"<python>" "<cli>" docx|pptx|xlsx|pdf render --input "<file>" --out-dir "<dir>"
|
|
49
|
+
"<python>" "<cli>" docx|pptx|xlsx|pdf render --input "<file>" --out-dir "<render-dir>"
|
|
54
50
|
"<python>" "<cli>" xlsx recalc --input "<file>" --out "<file>"
|
|
55
51
|
```
|
|
56
52
|
|
|
53
|
+
There is no `edit` verb: mutation belongs to a pack script or a snippet.
|
|
57
54
|
Write the spec as a JSON file in the workspace; quote absolute paths.
|
|
58
|
-
`xlsx read` accepts `--data-only`;
|
|
59
|
-
|
|
55
|
+
`xlsx read` accepts `--data-only`; `create` (except `pdf`), `read`, `render`,
|
|
56
|
+
and `xlsx recalc` accept `--json-out <path>`.
|
|
60
57
|
|
|
61
58
|
2. **Pack script** — a specialised job the CLI has no verb for. Use the script
|
|
62
|
-
|
|
63
|
-
pptx/xlsx). Scripts are argparse-based, support `--help`,
|
|
64
|
-
paths
|
|
59
|
+
named by the format router (`<skill>/scripts/`, `container_tools/` for
|
|
60
|
+
pptx/xlsx, or the pack root). Scripts are argparse-based, support `--help`,
|
|
61
|
+
take absolute paths, and run with the same `"<python>"`.
|
|
65
62
|
|
|
66
|
-
3. **Library snippet** — the long tail. When neither the
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`
|
|
70
|
-
|
|
71
|
-
original untouched.
|
|
63
|
+
3. **Library snippet** — the long tail. When neither covers the change, write a
|
|
64
|
+
small runnable script in the workspace with the installed libraries
|
|
65
|
+
(`python-docx`, `openpyxl`, `python-pptx`, `pypdf`, `PyMuPDF`, `reportlab`,
|
|
66
|
+
`pdfplumber`), run it with `"<python>"`, then verify through the render step.
|
|
67
|
+
Keep it small; keep the original untouched.
|
|
72
68
|
|
|
73
|
-
Never
|
|
74
|
-
|
|
75
|
-
route, run it, and verify the result
|
|
69
|
+
Never say "the CLI does not support this, so it cannot be done". A missing verb
|
|
70
|
+
is a missing shortcut, not a missing capability: use the script or library
|
|
71
|
+
route, run it, and verify the result.
|
|
76
72
|
|
|
77
|
-
`check_runtime.py`
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
`check_runtime.py` validates the interpreter: it imports every package in
|
|
74
|
+
`python/requirements.txt` and runs `<cli> status`. Non-zero means a broken
|
|
75
|
+
environment, not a bad spec.
|
|
80
76
|
|
|
81
|
-
## 3. Render and look
|
|
77
|
+
## 3. Render and look
|
|
82
78
|
|
|
83
79
|
```bash
|
|
84
|
-
"<python>" "<cli>" docx render --input "<file>" --out-dir "<dir>"
|
|
80
|
+
"<python>" "<cli>" docx render --input "<file>" --out-dir "<render-dir>"
|
|
85
81
|
```
|
|
86
82
|
|
|
87
|
-
Output
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
Output is `<render-dir>/page-1.png`, `page-2.png`, ... at `--dpi` (default 110).
|
|
84
|
+
For docx/pptx/xlsx, add `--pdf` to also keep `<render-dir>/<stem>.pdf`; use that
|
|
85
|
+
PDF for annotation geometry. `pdf render` uses PyMuPDF and needs no external
|
|
86
|
+
renderer.
|
|
91
87
|
|
|
92
|
-
Open
|
|
88
|
+
Open every page with `read_image`. Check for: text overflowing or clipped at
|
|
93
89
|
margins, overlapping shapes, unexplained blank pages, tables spilling past the
|
|
94
|
-
page, fonts replaced by boxes, missing chart/table content,
|
|
95
|
-
|
|
90
|
+
page, fonts replaced by boxes, missing chart/table content, numbers that do not
|
|
91
|
+
match the request.
|
|
96
92
|
|
|
97
|
-
|
|
93
|
+
Present only after the pages look right.
|
|
98
94
|
|
|
99
95
|
## 4. Fix, re-render, present
|
|
100
96
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
97
|
+
Change the spec or script, re-run the same command, re-render, look again. Two
|
|
98
|
+
or three passes is normal. If a problem survives three passes, say what still
|
|
99
|
+
looks wrong instead of pretending it is fixed.
|
|
104
100
|
|
|
105
101
|
Then call `present` with the final file and reply with one sentence about what
|
|
106
|
-
changed. For an edit,
|
|
102
|
+
changed. For an edit, give the original path and the edited path.
|
|
107
103
|
|
|
108
104
|
## Rasterizing art
|
|
109
105
|
|
|
110
|
-
|
|
111
|
-
PDF page
|
|
106
|
+
Documents, decks, and workbooks embed raster images only. Convert an SVG or one
|
|
107
|
+
PDF page to PNG first; the same call downscales an oversized raster.
|
|
112
108
|
|
|
113
109
|
```bash
|
|
114
110
|
"<python>" "<shared>/scripts/ensure_raster_image.py" --input "<art.svg>" --dpi 300 --out "<art.png>"
|
|
115
111
|
"<python>" "<shared>/scripts/ensure_raster_image.py" --input "<deck.pdf>" --page 2 --width 1600 --out "<slide-2.png>"
|
|
116
112
|
```
|
|
117
113
|
|
|
118
|
-
It prints one JSON object with the PNG
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
It prints one JSON object with the PNG `width`/`height` and never overwrites the
|
|
115
|
+
input. Open the PNG with `read_image` before embedding: a blank, clipped, or
|
|
116
|
+
miscoloured raster is invisible inside the host file.
|
|
121
117
|
|
|
122
118
|
## Failure ladder
|
|
123
119
|
|
|
124
120
|
| Symptom | Meaning | What you do and say |
|
|
125
121
|
| --- | --- | --- |
|
|
126
|
-
| No `## Office runtime` JSON; skill says *No working Python runtime is available* |
|
|
127
|
-
| `office_status` shows `cli.exists: false`, or `python`/`cli`
|
|
128
|
-
| `render` fails: `the LibreOffice renderer is not available` | The
|
|
129
|
-
|
|
|
130
|
-
| `{"ok": false, "error": "..."}`
|
|
131
|
-
| `xlsx recalc` fails
|
|
132
|
-
|
|
|
133
|
-
| `read` returns an empty structure for a file
|
|
134
|
-
|
|
135
|
-
## Naming rule for edits (restated)
|
|
136
|
-
|
|
137
|
-
`<original>-edited.<ext>`, in the same directory as the original, unless the
|
|
138
|
-
user asked for a different name. Never overwrite the original, never delete it,
|
|
139
|
-
never "clean up" a temp copy over it. Deliver both paths, original first.
|
|
122
|
+
| No `## Office runtime` JSON; skill says *No working Python runtime is available* | No usable or installable interpreter | Do not run helper commands or write a document by hand. Give the user the remedy command from the skill body, ask them to run it, then call `office_status` again. |
|
|
123
|
+
| `office_status` shows `cli.exists: false`, or the `python`/`cli` path is missing | The bundled helper or its install is incomplete | Report the missing path. Do not substitute another tool that writes a different file. |
|
|
124
|
+
| `render` fails: `the LibreOffice renderer is not available` | The DSH LibreOffice kit was not found. create/read/scripts still work; docx/pptx/xlsx cannot rasterise | Do not claim you inspected it. Say the file was built and validated structurally, but could not be checked visually. The kit ships with DeepSeek Harness and the plugin records its path in `config.json`; a missing kit or a bad `DSH_LIBREOFFICE_KIT_CLI` causes this. A standalone LibreOffice/Office/WPS install does not help. `pdf render` still works. Offer a rebuild from spec or an unverified file labelled as such. |
|
|
125
|
+
| Region lands on nothing, or the payload says `unmapped: true` | The region cannot be tied to an object in the file | Do not shift the region or guess a cell/shape. Say what you identified and what you could not, then ask for the text or cell. |
|
|
126
|
+
| `{"ok": false, "error": "..."}` plus non-zero exit | The spec was rejected (unknown key, bad type, missing file) | Read the error, fix the named spec key, re-run. Never delete a failing block to make the command pass — that silently deletes user content. |
|
|
127
|
+
| `xlsx recalc` fails: kit missing | openpyxl cannot evaluate formulas; the kit is the only calculation engine | Report formula cells as uncalculated; never invent computed values. Fix the renderer and re-run. |
|
|
128
|
+
| No verb for the requirement | The common path is short, not the capability | Take the pack-script or library route, run it, `read` the file back, and render. Never refuse a job because a verb is missing. |
|
|
129
|
+
| `read` returns an empty structure for a supplied file | Unsupported or corrupt input | Say what failed and stop. Never overwrite the user's file with an empty one. |
|
package/dist/index.js
CHANGED
|
@@ -610,7 +610,7 @@ function apply(ctx) {
|
|
|
610
610
|
const parsed = parseSkill(raw, path);
|
|
611
611
|
const runtime = await ensureRuntime(paths.configFile);
|
|
612
612
|
const kitCli = resolveKitCli();
|
|
613
|
-
const pythonLine = runtime.state === "ok" && runtime.python != null ? `\n\n## Office runtime\n\
|
|
613
|
+
const pythonLine = runtime.state === "ok" && runtime.python != null ? `\n\n## Office runtime\n\nVerified absolute paths. Use them for every helper command; quote paths that contain spaces.\n\n- \`python\` runs all helper scripts, \`cli\` is the Office helper CLI.\n- \`skill\` is this skill's directory: its scripts live under \`<skill>/scripts/\`.\n- \`shared\` holds the cross-format notes and shared scripts.\n- \`renderer\` is the DSH LibreOffice kit; the helper finds it itself, so never pass it.\n\n\`\`\`json\n${JSON.stringify({
|
|
614
614
|
python: runtime.python.path,
|
|
615
615
|
cli: paths.cli,
|
|
616
616
|
skill: dirname(path).split("\\").join("/"),
|