@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.
- package/LICENSE.md +198 -0
- package/README.md +40 -0
- package/index.d.ts +4 -0
- package/index.js +9 -0
- package/package.json +87 -1
- package/skills/apple-notes/SKILL.md +92 -0
- package/skills/apple-notes/append-note.applescript +9 -0
- package/skills/apple-notes/cli.schema.json +32 -0
- package/skills/apple-notes/create-note.applescript +15 -0
- package/skills/apple-notes/delete-note.applescript +10 -0
- package/skills/apple-notes/edit-note.applescript +10 -0
- package/skills/apple-notes/read-note.applescript +28 -0
- package/skills/apple-notes/references/read.md +65 -0
- package/skills/apple-notes/references/write.md +105 -0
- package/skills/apple-notes/search-notes.applescript +21 -0
- package/skills/apple-reminders/SKILL.md +129 -0
- package/skills/apple-reminders/cli.schema.json +201 -0
- package/skills/apple-reminders/references/edit.md +69 -0
- package/skills/apple-reminders/references/view.md +58 -0
- package/skills/asana/SKILL.md +59 -0
- package/skills/diagrams/SKILL.md +107 -0
- package/skills/diagrams/references/class.md +29 -0
- package/skills/diagrams/references/er.md +27 -0
- package/skills/diagrams/references/flowchart.md +33 -0
- package/skills/diagrams/references/gantt.md +38 -0
- package/skills/diagrams/references/mindmap.md +35 -0
- package/skills/diagrams/references/pie.md +27 -0
- package/skills/diagrams/references/sequence.md +32 -0
- package/skills/diagrams/references/state.md +30 -0
- package/skills/diagrams/references/timeline.md +28 -0
- package/skills/excel/SKILL.md +120 -0
- package/skills/excel/references/create.md +374 -0
- package/skills/excel/references/edit.md +353 -0
- package/skills/excel/references/read.md +99 -0
- package/skills/github/SKILL.md +42 -0
- package/skills/gmail/SKILL.md +142 -0
- package/skills/gmail/operations.json +71 -0
- package/skills/google-calendar/SKILL.md +139 -0
- package/skills/google-calendar/operations.json +62 -0
- package/skills/google-docs/SKILL.md +74 -0
- package/skills/google-docs/operations.json +61 -0
- package/skills/google-docs/references/create.md +97 -0
- package/skills/google-docs/references/edit.md +146 -0
- package/skills/google-docs/references/read.md +49 -0
- package/skills/google-drive/SKILL.md +118 -0
- package/skills/google-drive/operations.json +40 -0
- package/skills/google-sheets/SKILL.md +71 -0
- package/skills/google-sheets/operations.json +85 -0
- package/skills/google-sheets/references/create.md +54 -0
- package/skills/google-sheets/references/edit.md +124 -0
- package/skills/google-sheets/references/read.md +74 -0
- package/skills/image-generation/SKILL.md +48 -0
- package/skills/music-generation/SKILL.md +76 -0
- package/skills/notion/SKILL.md +61 -0
- package/skills/notion/operations.json +53 -0
- package/skills/notion/references/comments.md +65 -0
- package/skills/notion/references/databases.md +68 -0
- package/skills/notion/references/pages.md +119 -0
- package/skills/notion/references/tasks.md +28 -0
- package/skills/obsidian/SKILL.md +122 -0
- package/skills/obsidian/cli.schema.json +392 -0
- package/skills/obsidian/references/read.md +79 -0
- package/skills/obsidian/references/write.md +67 -0
- package/skills/pdf/SKILL.md +110 -0
- package/skills/pdf/references/create.md +169 -0
- package/skills/pdf/references/transform.md +270 -0
- package/skills/pdf/scripts/decrypt.py +26 -0
- package/skills/pdf/scripts/encrypt.py +25 -0
- package/skills/pdf/scripts/extract_text.py +25 -0
- package/skills/pdf/scripts/merge.py +21 -0
- package/skills/pdf/scripts/rotate.py +27 -0
- package/skills/presentations/SKILL.md +118 -0
- package/skills/presentations/references/create.md +399 -0
- package/skills/presentations/references/edit.md +314 -0
- package/skills/presentations/references/read.md +127 -0
- package/skills/spotify/SKILL.md +86 -0
- package/skills/weather/SKILL.md +33 -0
- package/skills/word/SKILL.md +141 -0
- package/skills/word/references/create.md +368 -0
- package/skills/word/references/edit.md +704 -0
- package/skills/word/references/read.md +141 -0
- package/skills/word/references/replace.md +86 -0
- package/skills/word/scripts/list_paragraphs.py +19 -0
- 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.
|