@qvac/skills 0.0.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE.md +198 -0
  2. package/README.md +40 -0
  3. package/index.d.ts +4 -0
  4. package/index.js +9 -0
  5. package/package.json +87 -1
  6. package/skills/apple-notes/SKILL.md +92 -0
  7. package/skills/apple-notes/append-note.applescript +9 -0
  8. package/skills/apple-notes/cli.schema.json +32 -0
  9. package/skills/apple-notes/create-note.applescript +15 -0
  10. package/skills/apple-notes/delete-note.applescript +10 -0
  11. package/skills/apple-notes/edit-note.applescript +10 -0
  12. package/skills/apple-notes/read-note.applescript +28 -0
  13. package/skills/apple-notes/references/read.md +65 -0
  14. package/skills/apple-notes/references/write.md +105 -0
  15. package/skills/apple-notes/search-notes.applescript +21 -0
  16. package/skills/apple-reminders/SKILL.md +129 -0
  17. package/skills/apple-reminders/cli.schema.json +201 -0
  18. package/skills/apple-reminders/references/edit.md +69 -0
  19. package/skills/apple-reminders/references/view.md +58 -0
  20. package/skills/asana/SKILL.md +59 -0
  21. package/skills/diagrams/SKILL.md +107 -0
  22. package/skills/diagrams/references/class.md +29 -0
  23. package/skills/diagrams/references/er.md +27 -0
  24. package/skills/diagrams/references/flowchart.md +33 -0
  25. package/skills/diagrams/references/gantt.md +38 -0
  26. package/skills/diagrams/references/mindmap.md +35 -0
  27. package/skills/diagrams/references/pie.md +27 -0
  28. package/skills/diagrams/references/sequence.md +32 -0
  29. package/skills/diagrams/references/state.md +30 -0
  30. package/skills/diagrams/references/timeline.md +28 -0
  31. package/skills/excel/SKILL.md +120 -0
  32. package/skills/excel/references/create.md +374 -0
  33. package/skills/excel/references/edit.md +353 -0
  34. package/skills/excel/references/read.md +99 -0
  35. package/skills/github/SKILL.md +42 -0
  36. package/skills/gmail/SKILL.md +142 -0
  37. package/skills/gmail/operations.json +71 -0
  38. package/skills/google-calendar/SKILL.md +139 -0
  39. package/skills/google-calendar/operations.json +62 -0
  40. package/skills/google-docs/SKILL.md +74 -0
  41. package/skills/google-docs/operations.json +61 -0
  42. package/skills/google-docs/references/create.md +97 -0
  43. package/skills/google-docs/references/edit.md +146 -0
  44. package/skills/google-docs/references/read.md +49 -0
  45. package/skills/google-drive/SKILL.md +118 -0
  46. package/skills/google-drive/operations.json +40 -0
  47. package/skills/google-sheets/SKILL.md +71 -0
  48. package/skills/google-sheets/operations.json +85 -0
  49. package/skills/google-sheets/references/create.md +54 -0
  50. package/skills/google-sheets/references/edit.md +124 -0
  51. package/skills/google-sheets/references/read.md +74 -0
  52. package/skills/image-generation/SKILL.md +48 -0
  53. package/skills/music-generation/SKILL.md +76 -0
  54. package/skills/notion/SKILL.md +61 -0
  55. package/skills/notion/operations.json +53 -0
  56. package/skills/notion/references/comments.md +65 -0
  57. package/skills/notion/references/databases.md +68 -0
  58. package/skills/notion/references/pages.md +119 -0
  59. package/skills/notion/references/tasks.md +28 -0
  60. package/skills/obsidian/SKILL.md +122 -0
  61. package/skills/obsidian/cli.schema.json +392 -0
  62. package/skills/obsidian/references/read.md +79 -0
  63. package/skills/obsidian/references/write.md +67 -0
  64. package/skills/pdf/SKILL.md +110 -0
  65. package/skills/pdf/references/create.md +169 -0
  66. package/skills/pdf/references/transform.md +270 -0
  67. package/skills/pdf/scripts/decrypt.py +26 -0
  68. package/skills/pdf/scripts/encrypt.py +25 -0
  69. package/skills/pdf/scripts/extract_text.py +25 -0
  70. package/skills/pdf/scripts/merge.py +21 -0
  71. package/skills/pdf/scripts/rotate.py +27 -0
  72. package/skills/presentations/SKILL.md +118 -0
  73. package/skills/presentations/references/create.md +399 -0
  74. package/skills/presentations/references/edit.md +314 -0
  75. package/skills/presentations/references/read.md +127 -0
  76. package/skills/spotify/SKILL.md +86 -0
  77. package/skills/weather/SKILL.md +33 -0
  78. package/skills/word/SKILL.md +141 -0
  79. package/skills/word/references/create.md +368 -0
  80. package/skills/word/references/edit.md +704 -0
  81. package/skills/word/references/read.md +141 -0
  82. package/skills/word/references/replace.md +86 -0
  83. package/skills/word/scripts/list_paragraphs.py +19 -0
  84. package/skills/word/scripts/replace_paragraphs.py +58 -0
@@ -0,0 +1,141 @@
1
+ ---
2
+ name: word
3
+ description: Create, edit, or read Word (.docx) documents with python-docx — deliver documents as chat attachments, or read an attached one to summarize it or answer questions in the chat. Can embed images generated in the chat. Opens in Pages and Google Docs too.
4
+ aliases: [docx, word-document, memo]
5
+ preload_on_name: false
6
+ tools: [exec(python)]
7
+ platform: [darwin, linux, win32]
8
+ metadata:
9
+ {
10
+ "openclaw":
11
+ {
12
+ "setup":
13
+ {
14
+ "summary": "Runs python-docx in the in-process Python runtime, from packages that ship with the app. The first use waits for the runtime to start."
15
+ }
16
+ }
17
+ }
18
+ ---
19
+
20
+ # Word
21
+
22
+ Build, edit, or read `.docx` documents by running python-docx through the
23
+ `exec` tool with `language: "python"`. Declare a produced document in
24
+ `outputs` and it comes back as a chat attachment the user can save. To answer
25
+ _from_ a document instead of building one, run a read call — no `outputs` —
26
+ and reply in the chat.
27
+
28
+ ## Load the Recipe File First
29
+
30
+ This file contains no Python. The working recipes live in four reference
31
+ files — load the one for the job with the `skill` tool BEFORE writing any
32
+ Python, then copy its recipe and change the content:
33
+
34
+ Each load is a real `skill` tool call — printing the call as JSON or text in
35
+ your reply loads nothing.
36
+
37
+ - **Creating a new document** (no existing `.docx` involved; may embed
38
+ images): call the `skill` tool with `name: "word"` and
39
+ `file: "references/create.md"`.
40
+ - **Changing some of the facts, points, bullets, items, or paragraphs** of an
41
+ existing `.docx` — "replace the first 10 facts", "change fact 3", "swap the
42
+ bullets for these", "reword paragraph 7": call the `skill` tool with
43
+ `name: "word"` and `file: "references/replace.md"`. This is the file even
44
+ when the user says edit, replace, change, update, or rewrite; it runs two
45
+ bundled scripts and no Python is written.
46
+ - **Any other edit of an existing document** (extend it, trim it, rework a
47
+ whole section, resize the text, embed an image into it): call the `skill`
48
+ tool with `name: "word"` and `file: "references/edit.md"`.
49
+ - **Reading a document to answer in chat** (a summary, a question answered,
50
+ content pulled out — no file delivered): call the `skill` tool with
51
+ `name: "word"` and `file: "references/read.md"`.
52
+ - **A summary delivered as a file** is a read followed by a build: load both
53
+ `references/read.md` and `references/create.md`.
54
+
55
+ Never write the Python from memory. The recipes carry rules (exact version
56
+ pins, attachment staging, run-level formatting, in-place replacement, the only
57
+ working removal idiom) that fail in non-obvious ways when improvised; loading
58
+ the file is one cheap read-only call.
59
+
60
+ ## When to Use
61
+
62
+ - The user asks for a document, report, letter, memo, `.docx`, or Word file.
63
+ - The user attaches a `.docx` and wants its content changed, replaced in part,
64
+ extended, trimmed, or reworked.
65
+ - The user attaches a `.docx` and asks what it says — a summary, a question
66
+ answered, or content pulled out into the chat.
67
+ - The user wants a document that embeds images generated in this chat.
68
+
69
+ ## When NOT to Use
70
+
71
+ - The user wants text in the chat and no document is involved — just write it.
72
+ Summarizing or answering from an attached `.docx` **is** this skill: load
73
+ `references/read.md`.
74
+ - The user wants slides or a deck — that is the presentations skill.
75
+ - The user wants a spreadsheet or a PDF — python-docx writes only `.docx`.
76
+
77
+ ## What This Skill Cannot Do
78
+
79
+ Say so instead of faking these; a fake is worse than a clear "not supported":
80
+
81
+ - **No table of contents.** A real TOC is a Word field that Word itself computes;
82
+ python-docx cannot insert one. Do not fake a TOC by typing headings and page
83
+ numbers — the page numbers would be wrong. Offer headings (`Heading 1..9`)
84
+ instead; Word can generate a TOC from them later.
85
+ - **No tracked changes or comments.** There is no revisions API. Edits land as
86
+ plain content; say that when the user asks for a redline.
87
+ - **No legacy `.doc`.** Only `.docx`. A `.doc` output name is rejected — name it
88
+ `.docx`.
89
+ - **No PDF export and no rendering.** The runtime cannot convert or preview the
90
+ document; it can only write the file.
91
+
92
+ ## Rules for Every Job
93
+
94
+ **You build it, not the user.** Deliver the document, never the recipe. Do NOT
95
+ print the python source in chat, do NOT tell the user to install python-docx,
96
+ run a script, or open a terminal — they have no terminal in this chat and the
97
+ code would not run there. The document exists only if an `exec` call with
98
+ `outputs` succeeds and returns the attachment; falling back to "here is the
99
+ script, run it yourself" is a failed turn.
100
+
101
+ **Success = stop.** When `exitCode` is `0` and the result's `attachments`
102
+ lists the `.docx`, the document is done. Do not call `exec` again for the same
103
+ request — not to "confirm", not to "improve", not to "add the image" after the
104
+ fact. Exactly one successful _build_ `exec` per document request — a
105
+ no-`outputs` read that precedes a build delivers nothing and is not one of
106
+ them, but it belongs before the build, never after it. Reply with a single
107
+ line: file name + the count line from stdout. If the result has
108
+ `missingOutputs` instead, the file was never written: read stderr first — an
109
+ `AssertionError` there means a guard stopped the save on purpose (see the edit
110
+ recipe); only when stderr is clean check the `save()` name matches the
111
+ declared output and rerun once.
112
+
113
+ **Failures are fixed in the code, not around it.** An error in your code is
114
+ never a fault in python-docx or in the runtime; fix the Python against the
115
+ loaded reference file's recipes and Errors and call `exec` again. If two
116
+ consecutive calls fail with the same error, re-read the traceback
117
+ line-by-line before a third — retrying the identical `command`, or a version
118
+ with only cosmetic changes, is a loop, not a fix. Do not switch package pins
119
+ (keep `python-docx==1.2.0`), do not wrap source in `python -c` / `pip` /
120
+ shell, do not "debug" with `os.listdir` or no-op scripts while `outputs`
121
+ still lists the document, and do not write the document as markdown/chat text
122
+ instead of a `.docx`. Never search the web about an error; the answer is
123
+ always in the `exec` result you already have.
124
+
125
+ **The runtime is sealed.** There is no shell — `ls`, `cat`, and `file` raise
126
+ `SyntaxError` because `command` is Python source — and no network:
127
+ `requests`, `urllib`, and `socket` all fail. The working directory starts
128
+ empty on every call: a file from an earlier call is gone unless staged again,
129
+ and a file you write but do not declare in `outputs` is discarded. The `exec`
130
+ result is the only account of what happened — there is no filesystem to check
131
+ and no shell to check it with.
132
+
133
+ **Never overwrite a staged input.** Edits always save a new output name,
134
+ derived from the document edited — `report.docx` becomes `report_revised.docx`,
135
+ never a fresh name taken from the new content.
136
+
137
+ **An edit changes the document in place.** `add_paragraph` and `add_heading`
138
+ append at the end and nowhere else, so replacing content that is already there
139
+ means rewriting those paragraphs, not adding new ones. Delivering the original
140
+ with the new version appended is a failed turn — the edit recipe carries the
141
+ guards that catch it.
@@ -0,0 +1,368 @@
1
+ # Creating a Word Document (python-docx)
2
+
3
+ Create a new `.docx` from scratch by running Python through the `exec` tool.
4
+ A new document needs **no** `inputs` — do not invent attachment ids — unless
5
+ it embeds an image (see Embedding Images). **Exactly one** `exec` call per
6
+ user request when that call succeeds.
7
+
8
+ **A document that already exists in this chat is never rebuilt here.** "Add a
9
+ section", "reword this", "extend the doc" — any request that starts from an
10
+ existing `.docx` is an EDIT: load `references/edit.md` and stage the document
11
+ by its `attachmentId`. Building a fresh document for an edit request throws
12
+ away everything the user already has.
13
+
14
+ ## The exec call
15
+
16
+ ```json
17
+ {
18
+ "language": "python",
19
+ "packages": ["python-docx==1.2.0"],
20
+ "outputs": ["report.docx"],
21
+ "command": "..."
22
+ }
23
+ ```
24
+
25
+ - `language` — always `"python"`.
26
+ - `packages` — `["python-docx==1.2.0"]` on every call. The PyPI package is
27
+ `python-docx` but the import is `docx`; never list `docx` as the package —
28
+ that resolves a different, abandoned library. Pin the version; an unpinned
29
+ install resolves a potentially different library version. This exact version
30
+ ships with the app and installs with no network; any other version has to be
31
+ downloaded, which fails on a device that is offline.
32
+ - `outputs` — `["report.docx"]`. `save("report.docx")` must match the declared
33
+ output name. A file you write but do not declare here is discarded. A `.doc`
34
+ output name is rejected — name it `.docx`.
35
+ - `command` — the multi-line Python source, with real newline characters.
36
+ Never collapse it to one line joined by `;` — a `for`/`if`/`with` after a
37
+ semicolon is a `SyntaxError`. Its first line is the first line of Python
38
+ that runs: there is no shell and no interpreter to invoke, and no
39
+ installer — packages are declared in `packages`.
40
+
41
+ ## Embedding Images
42
+
43
+ Two kinds of image input, told apart by where the file came from:
44
+
45
+ **Tool-produced images** (`generate_image` output): stage them with the exact
46
+ `attachmentId` from the tool result — never placeholders like `att_image` or
47
+ any id you made up.
48
+
49
+ **Images the user uploaded** ("use this photo"): there is no id to copy — an
50
+ uploaded image never shows one. Stage it with `path` only and **no
51
+ `attachmentId` key**; the first id-less entry is the first image of the user's
52
+ latest message, the second is its second image, and so on. Id-less entries
53
+ resolve _images only_.
54
+
55
+ ```json
56
+ {
57
+ "language": "python",
58
+ "packages": ["python-docx==1.2.0"],
59
+ "inputs": [{ "path": "photo.png" }],
60
+ "outputs": ["report.docx"],
61
+ "command": "..."
62
+ }
63
+ ```
64
+
65
+ Staged files land in the working directory under the bare `path` names —
66
+ reference `doc.add_picture("photo.png", …)` by that name only. Paths must be
67
+ unique bare filenames. `attachment … not found in this chat` means you
68
+ invented an id or the file is not attached: re-copy the exact id from the tool
69
+ result, or for a document with no image drop `inputs` entirely.
70
+
71
+ If the image was staged in `inputs`, embed it in **that** single build with
72
+ `doc.add_picture` — never deliver a document and then rebuild to add the
73
+ image. Soft-failing (`try`/`except` around the picture) and saving without it
74
+ is a failed turn, not a success.
75
+
76
+ **Image URLs do not work — never download.** Your Python code has **no
77
+ network access**: `requests`, `urllib`, and `socket` all fail with a network
78
+ error, and `http_request` returns truncated text, never image bytes. When the
79
+ user gives an image URL, do not try to fetch it from Python and do not retry
80
+ through other tools — that is a dead end. Say the link cannot be downloaded
81
+ and ask the user to attach the image itself, or offer `generate_image` for a
82
+ similar visual. Then build the document with the staged attachment as above.
83
+
84
+ ## The Recipe
85
+
86
+ Start from this. It is a complete, working document — a title, headings,
87
+ paragraphs with bold and italic runs, a bulleted list, and a table — saved
88
+ under the declared output name. Copy it and change the content; do not
89
+ assemble a document from memory.
90
+
91
+ **Keep the source multi-line.** A `for`/`if`/`with` after a semicolon is a
92
+ `SyntaxError` — paste the block with real newlines, not `stmt; for x in y: …`.
93
+
94
+ **Hold content in plain lists of strings, and walk them.** Every list of bullets
95
+ is a flat `["…", "…"]`, and every table is a list of row lists. Do not reach for
96
+ a dict, a tuple of mixed widths, or a nested comprehension to hold document
97
+ content — those are where a `SyntaxError` or a
98
+ `ValueError: too many values to unpack` comes from, and they buy nothing here.
99
+
100
+ **Keep every underscore in API names.** `add_heading`, `add_paragraph`,
101
+ `add_run`, `add_table`, `add_row`, `add_picture`, `add_page_break` — stripping
102
+ them to `addheading` / `addparagraph` fails. Copy identifiers exactly as written
103
+ below:
104
+
105
+ ```python
106
+ from docx import Document
107
+ from docx.shared import Inches, Pt, RGBColor # one import line covers sizes, widths, colors
108
+
109
+ doc = Document()
110
+
111
+ doc.add_heading("Quarterly Report", level=0)
112
+ doc.add_paragraph("Prepared by the finance team.")
113
+
114
+ doc.add_heading("Summary", level=1)
115
+ p = doc.add_paragraph("Revenue grew ")
116
+ strong = p.add_run("18 percent")
117
+ strong.bold = True
118
+ p.add_run(" against a ")
119
+ emphasis = p.add_run("flat")
120
+ emphasis.italic = True
121
+ p.add_run(" cost base.")
122
+
123
+ doc.add_heading("Highlights", level=1)
124
+ for point in [
125
+ "New retail partners in two regions",
126
+ "Churn down for the third quarter",
127
+ "Support backlog cleared",
128
+ ]:
129
+ doc.add_paragraph(point, style="List Bullet")
130
+
131
+ doc.add_heading("Key Figures", level=1)
132
+ figures = [
133
+ ["Metric", "Q3", "Q4"], # first list is the header row
134
+ ["Revenue", "$1.2M", "$1.4M"],
135
+ ["Costs", "$0.9M", "$0.9M"],
136
+ ]
137
+ table = doc.add_table(rows=1, cols=len(figures[0]))
138
+ table.style = "Table Grid"
139
+ for index, cells in enumerate(figures):
140
+ row = table.rows[0].cells if index == 0 else table.add_row().cells
141
+ for column, value in enumerate(cells):
142
+ row[column].text = value
143
+
144
+ doc.save("report.docx") # must match the declared output exactly
145
+ print(f"{len(doc.paragraphs)} paragraphs, {len(doc.tables)} table(s)")
146
+ ```
147
+
148
+ ## Write a document, not markdown
149
+
150
+ A `.docx` carries real styles, so the structure is the style — never the
151
+ punctuation. Markdown written into text stays there verbatim and reads as a
152
+ typo in the finished document:
153
+
154
+ - **No markdown characters in any string.** `#`, `##`, `-`, `*`, `1.`, `**bold**`
155
+ and backticks all render literally. `add_heading("Security", level=2)` — never
156
+ `add_heading("- Security", level=2)` or `"## Security"`. A numbered list is
157
+ `style="List Number"`, which numbers itself; a typed `"1. "` prefix double-numbers.
158
+ - **No typed rules or line breaks.** A row of dashes or underscores as a section
159
+ divider is just those characters on the page, and a leading `"\n"` is a blank
160
+ line inside the paragraph. Headings already separate sections.
161
+ - **Every section title is a heading.** A first section called "Introduction" or
162
+ "Overview" goes through `add_heading(..., level=1)` like every other one; as a
163
+ plain `add_paragraph` it renders as body text and the document looks unstructured.
164
+ - **No blank paragraphs for spacing.** `add_paragraph("")` leaves a visible gap —
165
+ the heading and body styles already carry their own space before and after.
166
+ - **Bold is for a few words, not a sentence.** A fully bold paragraph reads as a
167
+ formatting mistake; bold the term, then continue in a normal run.
168
+
169
+ ## One paragraph, one string
170
+
171
+ `add_paragraph` takes a single text string, optionally with `style=` — nothing
172
+ else. Several sentences passed positionally raise
173
+ `TypeError: Document.add_paragraph() takes from 1 to 3 positional arguments but 4
174
+ were given`. Join them into one string, or open the paragraph with the first
175
+ piece and add the rest as runs:
176
+
177
+ ```python
178
+ p = doc.add_paragraph("As of 2026, Bitcoin is widely held. ")
179
+ p.add_run("Adoption keeps growing.")
180
+ ```
181
+
182
+ **The text you pass to `add_paragraph` is already the paragraph's first run.** A
183
+ run added afterwards _appends_ — repeating any of those words writes them twice
184
+ into the document (`"…finite supplyfinite supply"`). Each run carries the next
185
+ words and only those, so give a mixed-format paragraph an empty start and add
186
+ every piece as its own run:
187
+
188
+ ```python
189
+ p = doc.add_paragraph()
190
+ p.add_run("Digital scarcity ")
191
+ tail = p.add_run("and a finite supply")
192
+ tail.italic = True
193
+ ```
194
+
195
+ ## Bold and italic live on runs, never on paragraphs
196
+
197
+ `paragraph.bold = True` raises no error and changes **nothing** in the file — a
198
+ paragraph has no bold; the assignment lands on the Python object and is silently
199
+ discarded on save. Formatting belongs to runs:
200
+
201
+ ```python
202
+ p = doc.add_paragraph("normal, then ")
203
+ strong = p.add_run("bold")
204
+ strong.bold = True
205
+ p.add_run(" and ")
206
+ emphasis = p.add_run("italic")
207
+ emphasis.italic = True
208
+ ```
209
+
210
+ Two rules make that shape the only one to write:
211
+
212
+ - **`add_run` takes the text and nothing else.** `p.add_run("x", bold=True)`
213
+ raises `TypeError: Paragraph.add_run() got an unexpected keyword argument
214
+ 'bold'` — create the run, then set the attribute.
215
+ - **Never chain an attribute onto the `add_run(...)` call.** Name the run on one
216
+ line and format it on the next, as above. A run that needs no formatting is a
217
+ bare `p.add_run("plain text")` and the line ends there — a trailing `.` left
218
+ over from a half-written chain is `SyntaxError: invalid syntax`.
219
+ - **Runs join with no gap between them.** The next run starts exactly where the
220
+ last one ended, so the separating space belongs inside one of the strings —
221
+ `"…without intermediaries. "` then `"It was invented"`, never
222
+ `"…intermediaries."` followed by `"It was invented"`.
223
+
224
+ **`add_run` belongs to the paragraph, not to a run.** Keep the paragraph in a
225
+ variable and call `p.add_run(...)` for every run in it — chaining a second run off
226
+ the first raises `AttributeError: 'Run' object has no attribute 'add_run'`. A run
227
+ owns `.text`, `.bold`, `.italic` and `.font`, and nothing else: it has no
228
+ `add_run`, no `add_paragraph`, and no `.style`.
229
+
230
+ A run is also not a string: `p.add_run(" ") * 2` raises
231
+ `TypeError: unsupported operand type(s) for *: 'Run' and 'int'`. Put any repeated
232
+ text inside the string itself — and reach for neither, since spacing is the
233
+ style's job, not padding you type.
234
+
235
+ Character detail goes through `run.font` — size, color:
236
+
237
+ ```python
238
+ from docx.shared import Pt, RGBColor
239
+
240
+ p = doc.add_paragraph()
241
+ run = p.add_run("Key finding")
242
+ run.font.size = Pt(14)
243
+ run.font.color.rgb = RGBColor(0x1A, 0x73, 0xE8) # RGB in all caps
244
+ ```
245
+
246
+ `Pt`, `Inches`, and `RGBColor` all import from `docx.shared` — there is no
247
+ `docx.util` and no `docx.dml.color`; those are python-pptx paths and fail here.
248
+
249
+ ## Styles must exist in the document
250
+
251
+ `style="List Bullet"` names a style **inside the document**. A missing name
252
+ raises `KeyError: "no style with name 'List Bullet'"` at `add_paragraph` time.
253
+
254
+ A **new** `Document()` ships these styles — safe to use without checking:
255
+ `Title`, `Heading 1` … `Heading 9`, `Normal`, `List Bullet` (+ ` 2`, ` 3`),
256
+ `List Number` (+ ` 2`, ` 3`), `Intense Quote`, and the table style `Table Grid`.
257
+ Do not invent other names for a new document. (An uploaded document carries
258
+ only its own styles — when editing one, load `references/edit.md` for the
259
+ guard.)
260
+
261
+ ## Headings and lists
262
+
263
+ - `doc.add_heading(text, level=N)` — level `0` is the document title style,
264
+ `1`–`9` map to `Heading 1`–`Heading 9`. Any other level raises
265
+ `ValueError: level must be in range 0-9`.
266
+ - Bullets: one `add_paragraph(point, style="List Bullet")` per point, over a flat
267
+ list of plain strings. Never pack several points into one paragraph with `\n` —
268
+ a `\n` is a soft line break inside the same list item, not a new bullet. A
269
+ bullet that needs a label and a detail is one string (`"Limited supply — 21
270
+ million coins"`), never a dict entry or a tuple.
271
+ - Numbered lists: `style="List Number"`. Indent a level with `List Bullet 2` /
272
+ `List Number 2`.
273
+
274
+ ## Tables
275
+
276
+ Write the whole table as a list of row lists — header first — then let the code
277
+ above derive everything from it. **Always `rows=1` and `cols=len(rows[0])`**:
278
+
279
+ ```python
280
+ rows = [
281
+ ["Item", "Status"], # header
282
+ ["Search", "Shipped"],
283
+ ["Export", "In review"],
284
+ ]
285
+ table = doc.add_table(rows=1, cols=len(rows[0]))
286
+ table.style = "Table Grid" # borders; omit for invisible grid
287
+ for index, cells in enumerate(rows):
288
+ row = table.rows[0].cells if index == 0 else table.add_row().cells
289
+ for column, value in enumerate(cells):
290
+ row[column].text = value
291
+ ```
292
+
293
+ That shape exists because the two hand-written alternatives both fail:
294
+
295
+ - **`rows=` is a count of blank rows created immediately, not a maximum.**
296
+ `add_table(rows=4, …)` followed by `add_row()` per entry leaves three empty
297
+ rows sitting between the header and the data, plainly visible in the finished
298
+ document. `rows=1` is the header; every other row comes from `add_row()`.
299
+ - **Unpacking a row into fixed names breaks the moment a row is a different
300
+ width.** `for name, q3, q4 in data:` raises
301
+ `ValueError: too many values to unpack (expected 3, got 4)`, and hand-counting
302
+ `cols=` against the data is the same mistake one step earlier. Index the cells
303
+ instead, and take the column count from the header.
304
+
305
+ Address cells as `table.cell(row, col)` or `table.rows[r].cells[c]` — they are
306
+ the same cell. Rows only grow at the bottom: there is no insert-at.
307
+ `table.rows[9]` on a 4-row table raises `IndexError`. Write text with
308
+ `cell.text = "…"`; for formatting inside a cell go through `cell.paragraphs[0]`
309
+ and its runs like any other paragraph.
310
+
311
+ ## Images and page breaks
312
+
313
+ `doc.add_picture(name, width=…)` appends the image in its own paragraph. Pass
314
+ only one of `width`/`height`; passing both distorts the picture.
315
+
316
+ ```python
317
+ from docx.shared import Inches
318
+
319
+ doc.add_picture("figure1.png", width=Inches(5.5))
320
+ doc.add_page_break()
321
+ ```
322
+
323
+ **Do not soft-fail images or imports.** Never wrap `add_picture` or an import in
324
+ `try`/`except` that prints a warning and continues. A missing file must raise so
325
+ you fix it and rerun — a document saved without the requested image is a failed
326
+ turn, not a success.
327
+
328
+ ## Errors
329
+
330
+ - `ModuleNotFoundError: No module named 'docx'` means `packages` was missing or
331
+ wrong — add `["python-docx==1.2.0"]` and rerun. Never try to install it, and
332
+ never "fix" it by importing `python_docx`; the import stays `docx`.
333
+ - `TypeError: 'Table' object is not subscriptable` — a table was indexed
334
+ directly (`table[0]`). Cells are reached through `table.rows[r].cells[c]` or
335
+ `table.cell(r, c)`; a whole row of cells is `table.add_row().cells`.
336
+ - `KeyError: "no style with name '…'"` — the style is not in this document. For
337
+ a new document use only the names listed under Styles.
338
+ - `NameError: name 'RGBColor' is not defined` (or `Pt`, `Inches`) — the import
339
+ line is missing that name. Keep the sample's single
340
+ `from docx.shared import Inches, Pt, RGBColor` rather than importing one at a time.
341
+ - `SyntaxError: invalid syntax` on a one-line `for`/`if` means the source was
342
+ collapsed — restore multi-line newlines from the sample and rerun. Underscores
343
+ in names (`add_paragraph`, not `addparagraph`) must stay. Do not switch to
344
+ `python -c` or change the package pin.
345
+ - On an `AttributeError` from python-docx the API name is wrong; on a `TypeError`
346
+ about positional arguments the call passes the wrong number of them — usually
347
+ several strings where one is allowed. Fix either against this file's examples,
348
+ reading the line number in the traceback. Do not retry the same call, and do
349
+ not switch to a shell.
350
+ - `attachment … not found in this chat` means `inputs` listed an id that is not
351
+ in this chat (often a copied placeholder like `att_doc`). For a new document,
352
+ omit `inputs` entirely and rerun. Only stage real ids from prior tool results.
353
+ - Never print the document's bytes or base64 — stdout is capped and the file
354
+ travels through `outputs`. A build call prints exactly one line (e.g. `9
355
+ paragraphs, 1 table(s)`).
356
+ - Never pass an absolute path to `save()`.
357
+
358
+ ## Finish
359
+
360
+ When `exitCode` is `0` and `attachments` lists the `.docx`, the document is
361
+ done — the `exec` result carries
362
+ `attachments: [{ attachmentId, fileName, byteLength }]` and the file is already
363
+ attached to the chat for the user to open or save, exactly like a
364
+ `generate_image` result. Stop tool use and reply with a single line: file name
365
+
366
+ - the count line from stdout. Exactly one successful `exec` per request. If
367
+ the result has `missingOutputs` instead, the file was never written: check the
368
+ `save()` name matches the declared output and rerun once.