@qvac/skills 0.1.11 → 0.1.13

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.
@@ -1,110 +1,160 @@
1
1
  ---
2
2
  name: pdf
3
- description: Create PDF documents with fpdf2 or transform existing ones with pypdf — merge, split, rotate, watermark, encrypt, decrypt, fill forms, extract text — and deliver them as chat attachments.
4
- tools: [exec(python)]
5
- platform: [darwin, linux, win32]
6
- metadata:
7
- {
8
- "openclaw":
9
- {
10
- "setup":
11
- {
12
- "summary": "Runs pypdf and fpdf2 in the in-process Python runtime, from packages that ship with the app. The first use waits for the runtime to start."
13
- }
14
- }
15
- }
3
+ description: Read, answer questions about, create, merge, split, rotate, watermark, password-protect, unlock, fill in, or pull the text out of PDF files, including any attached .pdf — always this skill, never Python, for a PDF — and deliver the result as a chat attachment.
4
+ tools: [pdf]
5
+ platform: [darwin, linux, win32, ios, android]
16
6
  ---
17
7
 
18
8
  # PDF
19
9
 
20
- Build or transform `.pdf` files by running Python through the `exec` tool with
21
- `language: "python"`. Declare every produced file in `outputs` and it comes back
22
- as a chat attachment the user can save.
23
-
24
- Two libraries, split by job — pick by whether a PDF already exists:
25
-
26
- - **fpdf2** — create a new PDF from scratch: reports, letters, invoices, cheat
27
- sheets, anything laid out page by page.
28
- - **pypdf** — transform a PDF that already exists: merge, split, reorder or
29
- rotate pages, watermark, encrypt or decrypt, fill form fields, extract text.
30
-
31
- ## Load the Recipe File First
32
-
33
- This file contains no Python. The working recipes live in two reference files —
34
- load the one for the job with the `skill` tool BEFORE writing any Python, then
35
- copy its recipe and change the content:
36
-
37
- Each load is a real `skill` tool call — printing the call as JSON or text in
38
- your reply loads nothing.
39
-
40
- - **Creating a new PDF** (no existing PDF involved): call the `skill` tool with
41
- `name: "pdf"` and `file: "references/create.md"`.
42
- - **Anything with an existing PDF** (merge, split, rotate, watermark, encrypt,
43
- decrypt, fill forms, extract text): call the `skill` tool with
44
- `name: "pdf"` and `file: "references/transform.md"`.
45
- - **Adding or changing words in an existing PDF** (add a paragraph or section,
46
- reword, restyle): pypdf cannot edit page content — do not try. The job is a
47
- REBUILD: load `references/create.md` and write the whole document again with
48
- fpdf2 — every original section, unchanged, plus the requested change. When
49
- the original text is not already in this chat, load
50
- `references/transform.md` too and extract it first.
51
- - **Both in one flow** (build a page with fpdf2, then stamp it onto an existing
52
- PDF): load both files.
53
-
54
- Never write the Python from memory. The recipes carry required arguments
55
- (`new_x`/`new_y`, exact version pins, attachment staging rules) that fail in
56
- non-obvious ways when improvised; loading the file is one cheap read-only call.
57
-
58
- ## When to Use
59
-
60
- - The user asks for a new `.pdf` document.
61
- - The user attached one or more PDFs and wants them merged, split, rotated,
62
- watermarked, password-protected, unlocked, filled in, or their text pulled out.
63
- - A flow needs both: build a page with fpdf2, then stamp it onto an existing
64
- PDF with pypdf — one `exec` call can use both libraries.
10
+ Every PDF job is one or two `pdf` tool calls. A call with an `action` saves a
11
+ new file named `output` and attaches it to the chat; a call without `action`
12
+ changes nothing and returns what the PDF holds.
13
+
14
+ ## Pick the Call
15
+
16
+ | The user wants | The `pdf` call |
17
+ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
18
+ | A new PDF — "write a report", "make a cheat sheet" | `{ "action": "create", "markdown": "# Cat Facts\n\n...", "output": "cat_facts.pdf" }` |
19
+ | New or changed words in an existing PDF | read first, then one `create` call with the whole document |
20
+ | "Merge these PDFs", "combine them into one" | `{ "action": "merge", "attachmentIds": ["4f9c2ab1", "9be07c12"], "output": "merged.pdf" }` |
21
+ | "Keep pages 1-3", "split out page 5" | `{ "action": "split", "attachmentId": "4f9c2ab1", "pages": [1, 2, 3], "output": "report_pages_1-3.pdf" }` |
22
+ | "Delete page 3", "remove the last page" | `split` with every page to keep: a 4-page PDF without page 3 is `{ "action": "split", "attachmentId": "4f9c2ab1", "pages": [1, 2, 4], "output": "report_without_page_3.pdf" }` |
23
+ | "Rotate it", "turn page 2 sideways" | `{ "action": "rotate", "attachmentId": "4f9c2ab1", "degrees": 90, "pages": [2], "output": "report_rotated.pdf" }` |
24
+ | "Stamp DRAFT on every page", "add a watermark" | `{ "action": "watermark", "attachmentId": "4f9c2ab1", "text": "DRAFT", "output": "report_draft.pdf" }` |
25
+ | "Fill in this form" | read first, then `{ "action": "fill", "attachmentId": "4f9c2ab1", "values": { "name": "Ada Lovelace", "agree": true }, "output": "form_filled.pdf" }` |
26
+ | "Put a password on it", "lock it" | `{ "action": "encrypt", "attachmentId": "4f9c2ab1", "password": "s3cret", "output": "report_locked.pdf" }` |
27
+ | "Remove the password", "unlock it" | `{ "action": "decrypt", "attachmentId": "4f9c2ab1", "password": "s3cret", "output": "report_unlocked.pdf" }` |
28
+ | "Give me the text as a file" | `{ "action": "text", "attachmentId": "4f9c2ab1", "output": "report.txt" }` |
29
+ | What is in it — its pages, text, or form fields | `{ "attachmentId": "4f9c2ab1" }` |
30
+
31
+ - `pages` counts the way the user does: `1` is the first page. Leave `pages`
32
+ out and `rotate` and `watermark` apply to every page.
33
+ - `merge` joins the files in the order of `attachmentIds`.
34
+ - `degrees` is `90`, `180` or `270`, clockwise.
35
+ - `watermark` also takes `"opacity": 0.2` (0 to 1) and `"position": "under"`
36
+ to put the mark behind the content.
37
+ - `encrypt` locks the file with AES-256. Add `"ownerPassword": "admin"` and
38
+ `"permissions": { "print": false, "copy": false }` to restrict what a viewer
39
+ allows.
40
+ - A locked PDF needs its password on every action, not only `decrypt`:
41
+ `{ "action": "rotate", "attachmentId": "4f9c2ab1", "password": "s3cret", "degrees": 90, "output": "report_rotated.pdf" }`.
42
+ The password always comes from the user — never guess one.
43
+
44
+ ## A New PDF
45
+
46
+ ```json
47
+ {
48
+ "action": "create",
49
+ "markdown": "# Cat Facts\n\n## Senses\n\n- A cat's hearing reaches **64 kHz**.\n- Cats have about 200 million scent receptors.\n\n## Sleep\n\nCats sleep 12 to 16 hours a day, most of it in short naps.\n\n| Breed | Weight (kg) |\n| --- | --- |\n| Siamese | 4 |\n| Maine Coon | 8 |\n",
50
+ "output": "cat_facts.pdf"
51
+ }
52
+ ```
53
+
54
+ - `markdown` is the whole document: `#` headings, paragraphs, `-` and `1.`
55
+ lists, `**bold**`, `*italic*`, `>` quotes, fenced code, tables, `---` rules
56
+ and `\pagebreak`. The tool lays it out, wraps it and adds pages itself.
57
+ - **Write the whole document, not a cover page.** "A small PDF about X" still
58
+ means a title, then several short sections, each a heading plus a paragraph
59
+ or bullets. The content belongs inside the PDF, not in your chat reply.
60
+ - `create` also takes `"pageSize": "Letter"` (the default is `A4`; `A5` and
61
+ `Legal` work too), `"landscape": true` and `"pageNumbers": true`.
62
+ - Lock a new PDF only when the user asks for a password, and use the one the
63
+ user gave: add `"password"` with it. Otherwise leave `password` out.
64
+ - Western European text only: accents, curly quotes, dashes and `€` are fine.
65
+ A character outside it (Chinese, Cyrillic, emoji) fails the call and the
66
+ error names it — leave it out, or tell the user it cannot be drawn.
67
+ - A picture goes on its own line as `![caption](attachmentId)`:
68
+ `![Sales by month](9be07c12)` for an image this chat generated, or
69
+ `![Our logo](logo.png)` — the file name — for an image the user uploaded.
70
+ Only PNG and JPEG images work, and an image behind a URL cannot be
71
+ downloaded.
72
+
73
+ ## Changing the Words in an Existing PDF
74
+
75
+ A PDF's pages cannot be edited in place. Adding a paragraph, rewording, or
76
+ restyling is a REBUILD: read the PDF when its text is not already in this chat,
77
+ then one `create` call with every original section, unchanged, plus the
78
+ requested change. A rebuild that condenses, summarizes, or drops original
79
+ sections is a failed turn. Name it after the original: `report.pdf` →
80
+ `report_revised.pdf`.
81
+
82
+ **Keep the original pages.** Write each page's text from the read and put
83
+ `\pagebreak` between the pages, so the rebuild has the original page count. A
84
+ page the user asks to add goes where they asked, after its own `\pagebreak`.
85
+ A 3-page report with a line fixed on page 2:
86
+
87
+ ```json
88
+ {
89
+ "action": "create",
90
+ "markdown": "# Annual Report\n\nPage 1 text, unchanged.\n\n\\pagebreak\n\n## Costs\n\nPage 2 text, with the fixed line.\n\n\\pagebreak\n\n## Outlook\n\nPage 3 text, unchanged.\n",
91
+ "output": "report_revised.pdf"
92
+ }
93
+ ```
94
+
95
+ A picture cannot be added to an existing PDF's pages. Tell the user, or offer
96
+ a rebuild with the picture at the top, which keeps the words but not the
97
+ original layout.
98
+
99
+ ## Attachment Ids
100
+
101
+ The id comes from the tool result that produced the PDF, or from the user's
102
+ `[Attached file …]` line:
103
+
104
+ ```
105
+ [Attached file "contract.pdf" (application/pdf) — attachmentId: 4f9c2ab1]
106
+ ```
107
+
108
+ Copy the id verbatim. Never make one up. If no id for the PDF is in the chat,
109
+ ask the user to attach it again.
110
+
111
+ ## Reading a PDF
112
+
113
+ ```json
114
+ { "attachmentId": "4f9c2ab1" }
115
+ ```
116
+
117
+ It returns the page count, each page's text, and the form fields — each with
118
+ its `name`, `type`, current `value`, and the `options` a choice field accepts.
119
+ You cannot fill a form or write a summary in the same call that reads, because
120
+ the values are fixed before the PDF is opened. Read, then act on what came
121
+ back.
122
+
123
+ - **Filling a form** — `values` keys are the exact field `name`s from the
124
+ read. A checkbox takes `true` or `false`; a radio group or dropdown takes one
125
+ of its `options`. A PDF with no fields has no form layer: say so instead of
126
+ guessing where the text goes.
127
+ - **A page with empty text is a scanned image.** There is no OCR here — tell
128
+ the user those pages are images rather than calling again.
129
+ - **A question about an attached PDF** ("summarize this", "what does it say
130
+ about X") — answer from the document text already in the chat. Read only
131
+ when that text is missing.
65
132
 
66
133
  ## When NOT to Use
67
134
 
68
- - A `create_pdf` tool is available in this chat and the user only wants prose
69
- you are writing turned into a downloadable document — call `create_pdf` with
70
- the markdown instead; it is cheaper and handles layout itself. Reach for this
71
- skill when that tool is absent on this device, when an existing PDF is
72
- involved, or when the layout must be controlled page by page.
73
- - The user asks about an attached PDF — answer from the document text or
74
- knowledge excerpts already in the chat. Run text extraction only when the
75
- user wants the text itself delivered as a file.
76
135
  - The user wants a slide deck — use the presentations skill; a spreadsheet or
77
- Word file — this skill cannot read or write Office formats.
136
+ Word file — the excel or word skill.
78
137
  - The user wants a single image — call `generate_image` alone.
79
138
 
80
139
  ## Rules for Every Job
81
140
 
82
- **You build it, not the user.** Deliver the file, never the recipe. Do NOT
83
- print the Python source in chat, do NOT tell the user to install pypdf, run a
84
- script, or open a terminal — they have no terminal in this chat and the code
85
- would not run there. The PDF exists only if an `exec` call with `outputs`
86
- succeeds and returns the attachment.
87
-
88
- **Success = stop.** When `exitCode` is `0` and the result's `attachments` lists
89
- the `.pdf`, the job is done. Do not call `exec` again for the same request —
90
- not to "confirm", not to "improve". Reply with a single line: file name + the
91
- page count from stdout. If the result has `missingOutputs` instead, the file
92
- was never written: check the save name matches the declared output and rerun
93
- once.
94
-
95
- **Failures are fixed in the code, not around it.** If a run fails, fix the
96
- Python against the loaded reference file's recipes and Errors table and call
97
- `exec` again. If two consecutive calls fail with the same error, re-read the
98
- traceback line-by-line before a third. An error is never a fault in pypdf,
99
- fpdf2, or the runtime — do not switch package versions, do not wrap source in
100
- `python -c` or shell, and do not "debug" with `os.listdir` or no-op scripts
101
- while `outputs` still lists the PDF.
102
-
103
- **The runtime is sealed.** There is no shell (`ls`, `cat` raise
104
- `SyntaxError` — the `command` is Python source) and no network — `requests`,
105
- `urllib`, and `socket` all fail, so a PDF behind a URL cannot be downloaded;
106
- ask the user to attach the file. The working directory starts empty on every
107
- call: a file from an earlier call is gone unless staged again, and a file you
108
- write but do not declare in `outputs` is discarded.
109
-
110
- **Never overwrite a staged input.** Transforms always write a new output name.
141
+ **You build it, not the user.** The PDF exists only when a call returns its
142
+ attachment. Never write it as chat text instead, and never tell the user to
143
+ run anything.
144
+
145
+ **Success = stop.** When the result lists the attachment, your whole answer is
146
+ the result's `reply` line — the file name and the page count — and you call
147
+ nothing else.
148
+
149
+ **Never overwrite the input.** Name the output after the file you opened:
150
+ `report.pdf` → `report_rotated.pdf`. Never the input's own name.
151
+
152
+ **Errors name the fix.** A failed call says what is wrong — a page out of
153
+ range and how many pages there are, a field name and the fields that exist, a
154
+ value and the options it takes, a wrong or missing password, a character the
155
+ font cannot draw. Correct that one argument and call again. Never repeat an
156
+ identical call, and never switch to exec, Python or the shell for a PDF job:
157
+ the `pdf` tool is the only way this skill works.
158
+
159
+ **No network.** A PDF behind a URL cannot be downloaded — ask the user to
160
+ attach the file.
@@ -1,118 +1,142 @@
1
1
  ---
2
2
  name: presentations
3
- description: Create, edit, or read PowerPoint (.pptx) decks with python-pptx — deliver decks as chat attachments, or read an attached one to summarize it or answer questions in the chat. Can embed images the user attached to the chat as well as images generated in it.
4
- tools: [exec(python)]
5
- platform: [darwin, linux, win32]
6
- metadata:
7
- {
8
- "openclaw":
9
- {
10
- "setup":
11
- {
12
- "summary": "Runs python-pptx in the in-process Python runtime, from packages that ship with the app. The first use waits for the runtime to start."
13
- }
14
- }
15
- }
3
+ description: Create, edit, or read PowerPoint (.pptx) decks — deliver decks as chat attachments, or read an attached one to summarize it or answer questions in the chat. Can embed images the user attached to the chat as well as images generated in it.
4
+ tools: [pptx]
5
+ platform: [darwin, linux, win32, ios, android]
16
6
  ---
17
7
 
18
8
  # Presentations
19
9
 
20
- Build, edit, or read `.pptx` decks by running python-pptx through the `exec`
21
- tool with `language: "python"`. Declare a produced deck in `outputs` and it
22
- comes back as a chat attachment the user can save — exactly like a
23
- `generate_image` result, the `exec` result carries
24
- `attachments: [{ attachmentId, fileName, byteLength }]`. To answer *from* a
25
- deck instead of building one, run a read call alone — no `outputs` — and reply
26
- in the chat.
27
-
28
- ## Load the Recipe File First
29
-
30
- This file contains no Python. The working recipes live in three 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
- - **Building a new deck** (no existing deck involved; includes embedding
38
- images generated or uploaded in this chat): call the `skill` tool with
39
- `name: "presentations"` and `file: "references/create.md"`.
40
- - **Editing a deck already in this chat** (retitle, recolor, add or revise
41
- slides on a deck the user attached or a prior call built): call the `skill`
42
- tool with `name: "presentations"` and `file: "references/edit.md"`.
43
- - **Answering from an attached deck** (a summary, a question answered,
44
- content pulled into the chat — no file produced): call the `skill` tool
45
- with `name: "presentations"` and `file: "references/read.md"`.
46
- - **A summary delivered as a new file** is a read followed by a build: load
47
- both `references/read.md` and `references/create.md`.
48
-
49
- Never write the Python from memory. The recipes carry required rules (typed
50
- `Inches`/`Pt` lengths, layout reuse, the read-before-edit flow, exact version
51
- pins, attachment staging) that fail in non-obvious ways when improvised;
52
- loading the file is one cheap read-only call.
53
-
54
- ## When to Use
55
-
56
- - The user asks for a presentation, deck, slides, `.pptx`, PowerPoint, or Keynote-openable file.
57
- - The user attaches a `.pptx` and asks what it says — a summary, a question
58
- answered, or content pulled out into the chat.
59
- - The user wants a slide deck that embeds images generated in this chat.
60
- - The user attaches an image — a photo, a logo, a screenshot — and wants it on a
61
- slide. You can see the image, and you can also stage the file it came from:
62
- load `references/create.md` (new deck) or `references/edit.md` (existing
63
- deck). Generating a lookalike instead is a failed turn.
64
-
65
- ## When NOT to Use
66
-
67
- - The user wants markdown or a document in the chat and no deck is involved —
68
- just write it. Summarizing or answering from an attached `.pptx` **is** this
69
- skill: load `references/read.md`.
70
- - The user wants a single image — call `generate_image` alone.
10
+ Every deck job is one or two `pptx` tool calls. A call with `ops` saves a new
11
+ file named `output` and attaches it to the chat; a call without `ops` changes
12
+ nothing and returns what the deck holds.
13
+
14
+ ## Pick the Call
15
+
16
+ | The user wants | The calls |
17
+ | ------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
18
+ | A new deck — "make slides about …", "a deck with this image" | one call: `create` + `ops` + `output` |
19
+ | A mechanical edit — "make the title red", "retitle slide 1 to …" | one call: `attachmentId` + `ops` + `output` |
20
+ | An edit whose words depend on the deck — "add a summary slide", "fix typos" | read (`attachmentId` alone), then one edit call |
21
+ | An answer in the chat — "summarize this deck", "what does slide 3 say?" | read (`attachmentId` alone), then reply in the chat — no second call |
22
+ | A summary of the deck as a new file | read, then one `create` call written from what the read returned |
23
+
24
+ A deck already in this chat is never rebuilt with `create` — that throws away
25
+ every slide the user has. Its `attachmentId` is in the tool result that
26
+ produced it, or on the user's `[Attached file …]` line:
27
+
28
+ ```
29
+ [Attached file "quarterly.pptx" (application/vnd.openxmlformats-officedocument.presentationml.presentation) — attachmentId: 4f9c2ab1]
30
+ ```
31
+
32
+ Copy the id verbatim. Never make one up.
33
+
34
+ ## A New Deck
35
+
36
+ ```json
37
+ {
38
+ "create": { "size": "16:9" },
39
+ "ops": [
40
+ { "op": "addSlide", "layout": "Title Slide", "title": "Why the Sky Is Blue", "body": "Rayleigh scattering, in four points" },
41
+ { "op": "addSlide", "title": "What Happens", "body": [
42
+ "Sunlight carries every visible wavelength",
43
+ "Air scatters short wavelengths hardest",
44
+ { "text": "Blue scatters far more than red", "level": 1 },
45
+ "So the sky reads blue in every direction"
46
+ ] },
47
+ { "op": "addSlide", "layout": "Title Only", "title": "By Wavelength" },
48
+ { "op": "addChart", "slide": 2, "type": "column", "title": "Scattered light",
49
+ "categories": ["Blue", "Green", "Red"], "series": [{ "name": "Relative", "values": [5.5, 3.1, 1] }] }
50
+ ],
51
+ "output": "sky.pptx"
52
+ }
53
+ ```
54
+
55
+ Layouts in a new deck: `Title Slide`, `Title and Content` (the default),
56
+ `Section Header`, `Two Content`, `Title Only`, `Blank`. Each bullet is its own
57
+ item in `body` — never type `-`, `•` or `1.` in front of it; the slide draws
58
+ the bullet. Five bullets per slide at most; a sixth is a new slide titled
59
+ `… (cont.)`.
60
+
61
+ ## Reading a Deck
62
+
63
+ ```json
64
+ { "attachmentId": "4f9c2ab1" }
65
+ ```
66
+
67
+ It returns every slide with its index, layout and shapes — each shape's `id`,
68
+ `name`, and its paragraphs or table rows — plus speaker notes. You cannot write
69
+ a summary or a closing slide in the same call that reads, because the words
70
+ are fixed before the deck is opened. Read, then write from what came back.
71
+
72
+ ## Editing a Deck
73
+
74
+ ```json
75
+ {
76
+ "attachmentId": "4f9c2ab1",
77
+ "ops": [
78
+ { "op": "setText", "slide": 0, "shape": "title", "text": "Why the Sky Is Blue — Revised" },
79
+ { "op": "setStyle", "slide": 0, "shape": "title", "style": { "color": "C0392B", "bold": true } },
80
+ { "op": "addSlide", "title": "What Changed", "body": ["Retitled the cover", "Recoloured the title"] }
81
+ ],
82
+ "output": "quarterly-v2.pptx"
83
+ }
84
+ ```
85
+
86
+ Slides count from 0. A `shape` is `"title"`, `"subtitle"`, `"body"`, or the
87
+ `id` a read returned.
88
+
89
+ | The change | The op |
90
+ | ---------------------------- | ------------------------------------------------------------------------------------------ |
91
+ | Replace a shape's text | `{ "op": "setText", "slide": 1, "shape": "body", "text": ["First", "Second"] }` |
92
+ | Replace one table cell | `{ "op": "setCellText", "slide": 2, "shape": 4, "row": 1, "column": 1, "text": "450 nm" }` |
93
+ | Size, colour, font, bold | `{ "op": "setStyle", "slide": 0, "shape": "title", "style": { "size": 40, "font": "Georgia" } }` |
94
+ | Add a slide like its neighbours | `{ "op": "addSlide", "title": "Next Steps", "body": ["Ship it"] }` |
95
+ | Add a slide at a position | `{ "op": "addSlide", "at": 1, "title": "Agenda", "body": ["Why", "How"] }` |
96
+ | Delete a slide | `{ "op": "deleteSlide", "slide": 3 }` |
97
+ | Move a slide | `{ "op": "moveSlide", "from": 4, "to": 1 }` |
98
+ | Add a picture | `{ "op": "addPicture", "slide": 2, "image": { "attachmentId": "9be07c12" }, "width": 6 }` |
99
+ | Add a free text box | `{ "op": "addTextBox", "slide": 5, "text": "Thanks", "style": { "size": 54, "align": "center" } }` |
100
+ | Add a table | `{ "op": "addTable", "slide": 3, "rows": [["Band", "nm"], ["Blue", 450]], "header": true }` |
101
+ | Change a chart's numbers | `{ "op": "setChartData", "slide": 2, "shape": 4, "categories": ["Q1", "Q2"], "series": [{ "name": "2026", "values": [11, 13] }] }` |
102
+ | Retitle a chart | `{ "op": "setChartTitle", "slide": 2, "shape": 4, "title": "Revenue by quarter" }` |
103
+ | Solid background | `{ "op": "setBackground", "slide": 5, "color": "101820" }` |
104
+
105
+ `addSlide` without `layout` copies the layout of the deck's last content slide,
106
+ so the new slide matches its neighbours. Never pick `Blank` for an ordinary
107
+ title-and-bullets slide on a deck the user gave you. Positions and sizes (`x`,
108
+ `y`, `width`, `height`) are inches; give one of `width`/`height` and the
109
+ picture keeps its shape. Colours are six hex digits, no `#`.
110
+
111
+ Name the output after the deck you opened and bump a version: `quarterly.pptx`
112
+ → `quarterly-v2.pptx`, and an edit of that → `quarterly-v3.pptx`. Never the
113
+ input's own name.
114
+
115
+ ## Images
116
+
117
+ - An image this chat generated: `"image": { "attachmentId": "<id from the generate_image result>" }`.
118
+ - An image the user attached to their latest message: `"image": {}` — an
119
+ uploaded image shows no id, and the empty form picks it up.
120
+ - An image behind a URL cannot be downloaded. Say so, and ask the user to attach
121
+ it or offer `generate_image`.
71
122
 
72
123
  ## Rules for Every Job
73
124
 
74
- **You build it, not the user.** Deliver the deck, never the recipe. Do NOT
75
- print the python source in chat, do NOT tell the user to install python-pptx,
76
- run a script, or open a terminal — they have no terminal in this chat and the
77
- code would not run there. The deck exists only if an `exec` call with
78
- `outputs` succeeds and returns the attachment. Falling back to "here is the
79
- script, run it yourself" is a failed turn, and so is writing slides as
80
- markdown/chat text instead of the `.pptx` the user asked for — answering a
81
- *read* request as chat text is the read flow's finish, not this failure.
82
-
83
- **Success = stop.** When `exitCode` is `0` and `attachments` lists the
84
- `.pptx`, the deck is done. Do not call `exec` again for the same request —
85
- not to "confirm", not to "improve", not a second identical build; a second
86
- deck attach is rejected. Exactly one successful **build** `exec` per deck
87
- request — a no-`outputs` read delivers nothing and is not one of them: it
88
- belongs before the build on an edit, never after it, and on a read request it
89
- stands alone. Reply with a single line: file name + slide count from stdout.
90
- If the result has `missingOutputs` instead, the file was never written: check
91
- the `save()` name matches the declared output and rerun once.
92
-
93
- **Failures are fixed in the code, not around it.** If a run fails, fix the
94
- Python against the loaded reference file's recipes and errors table and call
95
- `exec` again. If two consecutive calls fail with the same error, the fix from
96
- the first attempt did not land — re-read the traceback line-by-line before a
97
- third call; retrying the identical `command`, or a version with only cosmetic
98
- changes, is a loop, not a fix. An error in your code is never a fault in
99
- python-pptx or in the runtime: do not switch package pins or hunt a "more
100
- compatible" version — keep `packages: ["python-pptx==1.0.2"]` — do not wrap
101
- source in `python -c "…"`, `python3`, `pip`, or shell, and do not "debug" with
102
- `os.listdir`, `print`, or a non-deck script while `outputs` still lists the
103
- deck. Never search the web about an error; the answer is always in the result
104
- you already have.
105
-
106
- **The runtime is sealed.** There is no shell — `ls`, `cat` and `file` raise
107
- `SyntaxError`, because the `command` is Python source — and no filesystem to
108
- check outside the `exec` result. There is no network: `requests`, `urllib`,
109
- and `socket` all fail, and `http_request` returns truncated text, never image
110
- bytes — an image behind a URL cannot be downloaded; ask the user to attach it
111
- or offer `generate_image`. The working directory starts empty on every call:
112
- a file from an earlier call is gone unless staged again by its attachment id,
113
- and a file you write but do not declare in `outputs` is discarded.
114
-
115
- **Never overwrite a staged input.** An edit always saves under a new,
116
- version-bumped name — `deck.pptx` → `deck-v2.pptx` — and a corrected rebuild
117
- after a broken delivery goes out under the next version, never the name
118
- already attached this turn.
125
+ **You build it, not the user.** The deck exists only when a `pptx` call with
126
+ `ops` returns its attachment. Never write the slides as chat text instead, and
127
+ never tell the user to run anything.
128
+
129
+ **Success = stop.** When the result lists the attachment, reply with one line —
130
+ the file name and the slide count — and call nothing else. Check `slides` in
131
+ the result against the request first: one slide added to a 2-slide deck is
132
+ `3`; `4` means an op ran twice, so fix the ops and send them again under the
133
+ next version name.
134
+
135
+ **Errors name the fix.** A failed call returns a message that says what exists
136
+ — `slide 7 does not exist; the deck has 3 slides (0 to 2)`, or the shapes on
137
+ the slide. Correct that one op and call again. Never repeat an identical call.
138
+
139
+ ## What This Cannot Do
140
+
141
+ Say so instead of faking it: writing speaker notes, editing scatter, bubble or
142
+ stock charts, SmartArt, and legacy `.ppt` files.
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: sheet-music
3
+ description: Turn audio into sheet music. Transcribe a recording or a generated track into a score you can read, print, or open in any notation app.
4
+ tools: [transcribe_music]
5
+ platform: [darwin, linux, win32]
6
+ ---
7
+
8
+ # Sheet music
9
+
10
+ Call `transcribe_music` when the user wants notation for audio that already
11
+ exists: sheet music, a score, the chords, the notes, the melody, "write this
12
+ out". It reads an audio attachment from the chat and saves a `.musicxml` score,
13
+ which the app shows as readable sheet music in the conversation with a PDF
14
+ download; it also opens in MuseScore, Sibelius or Dorico.
15
+
16
+ ```json
17
+ { "title": "kitchen demo" }
18
+ ```
19
+
20
+ **Do not invent an `attachmentId`.** An attached audio file never appears in the
21
+ conversation with an id, so there is nothing to copy: leaving the field out is
22
+ how you say "the track the user just attached".
23
+
24
+ ## When to Use
25
+
26
+ - The user attached audio and wants it written out as notation.
27
+ - A track was generated earlier in this chat and the user now asks for its sheet
28
+ music, chords, or notes.
29
+ - The user asks what notes or chords are in a recording.
30
+
31
+ ## When NOT to Use
32
+
33
+ - The user is asking for a *new* song — that is `generate_music`. If they want the
34
+ track and its sheet music together, call `generate_music` with
35
+ `sheetMusic: true` instead of making two calls.
36
+ - The user wants spoken words written down — that is speech transcription, not
37
+ this.
38
+
39
+ **If `transcribe_music` returns an error, report it and stop.** Never fall back to
40
+ `generate_music`: that writes a brand new piece, and presenting it as the user's
41
+ transcription is a lie about their own audio. The error text says what to change,
42
+ usually to omit `attachmentId`, so read it and retry the same tool.
43
+
44
+ ## Parameters
45
+
46
+ - `attachmentId` — **omit it** to transcribe the most recent audio in the chat,
47
+ which is what an attached track means. Pass one only to reach back to an older
48
+ track whose id a previous `generate_music` actually returned.
49
+ - `title` — a short title, 2 to 5 words; it names the saved files.
50
+ - `bpm` — pass it whenever the tempo is known. **If you generated the track in
51
+ this chat with a `bpm`, pass that same value here.** Tempo estimated from audio
52
+ is right about the beat but can land an octave off (60 where the track is 120),
53
+ and a known tempo removes that guess entirely.
54
+ - `keyscale` — same idea for the key, e.g. `C minor`. Estimated when omitted.
55
+
56
+ ## What comes back
57
+
58
+ The result names the score attachment and reports what was transcribed: `notes`,
59
+ `bars`, `bpm`, `key`, how many artifacts were filtered out, and two flags worth
60
+ reading before you reply:
61
+
62
+ - `tempoFromRequest` — false means the tempo was estimated. Worth a word to the
63
+ user if the rhythm looks doubled or halved.
64
+ - `melodyMissing` — nothing was found above middle C. Say so plainly: on a dense
65
+ mix the transcription captures the bass and the loudest lines, and the melody
66
+ is buried. Do not present that score as a full transcription.
67
+
68
+ ## Honesty about quality
69
+
70
+ This is automatic transcription, not a human copyist, and the result depends
71
+ almost entirely on how dense the audio is.
72
+
73
+ - **A solo instrument or a small ensemble transcribes well.** Piano, guitar,
74
+ a single voice, a duo.
75
+ - **A full band mix does not.** Distortion and cymbals mask everything in the
76
+ middle, so what survives is the bass line and whichever line is loudest.
77
+ - Percussion is not pitched and is not transcribed.
78
+ - Sustained notes are approximate: the score reads the right pitches with
79
+ quantized rhythm, not a performance-accurate transcription.
80
+
81
+ Tell the user which of these they are getting rather than letting them discover
82
+ it.
83
+
84
+ ## A track and its score together
85
+
86
+ Sheet music never changes the track. Generate what the user asked for: their
87
+ duration, their instrumentation, their genre.
88
+
89
+ Dense or long material makes a crowded score, and that is worth saying. Before
90
+ generating, when they have left the choice open, offer it: a sparse 30-60 second
91
+ piece gives a score a person can actually read. After generating, say plainly
92
+ what they got when the result comes back with `melodyMissing` or sixty-odd bars.
93
+ Never quietly substitute something shorter or sparser than they asked for.