@qvac/skills 0.0.0 → 0.1.0

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 +49 -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,392 @@
1
+ {
2
+ "type": "object",
3
+ "additionalProperties": false,
4
+ "properties": {
5
+ "version": {
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "properties": {},
9
+ "x-effect": "read"
10
+ },
11
+ "vaults": {
12
+ "type": "object",
13
+ "additionalProperties": false,
14
+ "properties": {},
15
+ "x-effect": "read"
16
+ },
17
+ "vault": {
18
+ "type": "object",
19
+ "additionalProperties": false,
20
+ "properties": {
21
+ "list": {
22
+ "type": "object",
23
+ "additionalProperties": false,
24
+ "properties": {},
25
+ "x-effect": "read"
26
+ },
27
+ "info": {
28
+ "type": "string"
29
+ },
30
+ "vault": {
31
+ "type": "string"
32
+ }
33
+ },
34
+ "x-effect": "read"
35
+ },
36
+ "files": {
37
+ "type": "object",
38
+ "additionalProperties": false,
39
+ "properties": {
40
+ "folder": {
41
+ "type": "string"
42
+ },
43
+ "ext": {
44
+ "type": "string"
45
+ },
46
+ "vault": {
47
+ "type": "string"
48
+ },
49
+ "path": {
50
+ "type": "string",
51
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
52
+ "description": "vault-relative path (not absolute)"
53
+ },
54
+ "limit": {
55
+ "type": "string"
56
+ },
57
+ "total": {
58
+ "type": "boolean"
59
+ },
60
+ "counts": {
61
+ "type": "boolean"
62
+ },
63
+ "verbose": {
64
+ "type": "boolean"
65
+ }
66
+ },
67
+ "x-effect": "read"
68
+ },
69
+ "search": {
70
+ "type": "object",
71
+ "additionalProperties": false,
72
+ "required": [
73
+ "query"
74
+ ],
75
+ "properties": {
76
+ "query": {
77
+ "type": "string"
78
+ },
79
+ "limit": {
80
+ "type": "string"
81
+ },
82
+ "vault": {
83
+ "type": "string"
84
+ },
85
+ "path": {
86
+ "type": "string",
87
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
88
+ "description": "vault-relative path (not absolute)"
89
+ },
90
+ "folder": {
91
+ "type": "string"
92
+ },
93
+ "verbose": {
94
+ "type": "boolean"
95
+ },
96
+ "total": {
97
+ "type": "boolean"
98
+ }
99
+ },
100
+ "x-effect": "read"
101
+ },
102
+ "read": {
103
+ "type": "object",
104
+ "additionalProperties": false,
105
+ "properties": {
106
+ "file": {
107
+ "type": "string"
108
+ },
109
+ "path": {
110
+ "type": "string",
111
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
112
+ "description": "vault-relative path (not absolute)"
113
+ },
114
+ "vault": {
115
+ "type": "string"
116
+ },
117
+ "verbose": {
118
+ "type": "boolean"
119
+ }
120
+ },
121
+ "x-effect": "read"
122
+ },
123
+ "create": {
124
+ "type": "object",
125
+ "additionalProperties": false,
126
+ "properties": {
127
+ "name": {
128
+ "type": "string"
129
+ },
130
+ "path": {
131
+ "type": "string",
132
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
133
+ "description": "vault-relative path (not absolute)"
134
+ },
135
+ "file": {
136
+ "type": "string"
137
+ },
138
+ "content": {
139
+ "type": "string"
140
+ },
141
+ "vault": {
142
+ "type": "string"
143
+ },
144
+ "template": {
145
+ "type": "string"
146
+ },
147
+ "silent": {
148
+ "type": "boolean"
149
+ }
150
+ }
151
+ },
152
+ "append": {
153
+ "type": "object",
154
+ "additionalProperties": false,
155
+ "properties": {
156
+ "file": {
157
+ "type": "string"
158
+ },
159
+ "path": {
160
+ "type": "string",
161
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
162
+ "description": "vault-relative path (not absolute)"
163
+ },
164
+ "content": {
165
+ "type": "string"
166
+ },
167
+ "vault": {
168
+ "type": "string"
169
+ },
170
+ "silent": {
171
+ "type": "boolean"
172
+ }
173
+ }
174
+ },
175
+ "prepend": {
176
+ "type": "object",
177
+ "additionalProperties": false,
178
+ "properties": {
179
+ "file": {
180
+ "type": "string"
181
+ },
182
+ "path": {
183
+ "type": "string",
184
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
185
+ "description": "vault-relative path (not absolute)"
186
+ },
187
+ "content": {
188
+ "type": "string"
189
+ },
190
+ "vault": {
191
+ "type": "string"
192
+ },
193
+ "silent": {
194
+ "type": "boolean"
195
+ }
196
+ }
197
+ },
198
+ "backlinks": {
199
+ "type": "object",
200
+ "additionalProperties": false,
201
+ "properties": {
202
+ "file": {
203
+ "type": "string"
204
+ },
205
+ "path": {
206
+ "type": "string",
207
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
208
+ "description": "vault-relative path (not absolute)"
209
+ },
210
+ "vault": {
211
+ "type": "string"
212
+ },
213
+ "format": {
214
+ "type": "string"
215
+ },
216
+ "verbose": {
217
+ "type": "boolean"
218
+ }
219
+ },
220
+ "x-effect": "read"
221
+ },
222
+ "tags": {
223
+ "type": "object",
224
+ "additionalProperties": false,
225
+ "properties": {
226
+ "vault": {
227
+ "type": "string"
228
+ },
229
+ "format": {
230
+ "type": "string"
231
+ },
232
+ "counts": {
233
+ "type": "boolean"
234
+ },
235
+ "verbose": {
236
+ "type": "boolean"
237
+ }
238
+ },
239
+ "x-effect": "read"
240
+ },
241
+ "tasks": {
242
+ "type": "object",
243
+ "additionalProperties": false,
244
+ "properties": {
245
+ "vault": {
246
+ "type": "string"
247
+ },
248
+ "path": {
249
+ "type": "string",
250
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
251
+ "description": "vault-relative path (not absolute)"
252
+ },
253
+ "folder": {
254
+ "type": "string"
255
+ },
256
+ "format": {
257
+ "type": "string"
258
+ },
259
+ "todo": {
260
+ "type": "boolean"
261
+ },
262
+ "done": {
263
+ "type": "boolean"
264
+ },
265
+ "verbose": {
266
+ "type": "boolean"
267
+ }
268
+ },
269
+ "x-effect": "read"
270
+ },
271
+ "daily:read": {
272
+ "type": "object",
273
+ "additionalProperties": false,
274
+ "properties": {
275
+ "vault": {
276
+ "type": "string"
277
+ },
278
+ "verbose": {
279
+ "type": "boolean"
280
+ }
281
+ },
282
+ "x-effect": "read"
283
+ },
284
+ "daily:append": {
285
+ "type": "object",
286
+ "additionalProperties": false,
287
+ "properties": {
288
+ "content": {
289
+ "type": "string"
290
+ },
291
+ "vault": {
292
+ "type": "string"
293
+ },
294
+ "silent": {
295
+ "type": "boolean"
296
+ }
297
+ }
298
+ },
299
+ "daily:prepend": {
300
+ "type": "object",
301
+ "additionalProperties": false,
302
+ "properties": {
303
+ "content": {
304
+ "type": "string"
305
+ },
306
+ "vault": {
307
+ "type": "string"
308
+ },
309
+ "silent": {
310
+ "type": "boolean"
311
+ }
312
+ }
313
+ },
314
+ "rename": {
315
+ "type": "object",
316
+ "additionalProperties": false,
317
+ "properties": {
318
+ "file": {
319
+ "type": "string"
320
+ },
321
+ "path": {
322
+ "type": "string",
323
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
324
+ "description": "vault-relative path (not absolute)"
325
+ },
326
+ "name": {
327
+ "type": "string"
328
+ },
329
+ "vault": {
330
+ "type": "string"
331
+ }
332
+ }
333
+ },
334
+ "move": {
335
+ "type": "object",
336
+ "additionalProperties": false,
337
+ "properties": {
338
+ "file": {
339
+ "type": "string"
340
+ },
341
+ "path": {
342
+ "type": "string",
343
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
344
+ "description": "vault-relative path (not absolute)"
345
+ },
346
+ "to": {
347
+ "type": "string"
348
+ },
349
+ "vault": {
350
+ "type": "string"
351
+ }
352
+ }
353
+ },
354
+ "delete": {
355
+ "type": "object",
356
+ "additionalProperties": false,
357
+ "properties": {
358
+ "file": {
359
+ "type": "string"
360
+ },
361
+ "path": {
362
+ "type": "string",
363
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
364
+ "description": "vault-relative path (not absolute)"
365
+ },
366
+ "vault": {
367
+ "type": "string"
368
+ },
369
+ "force": {
370
+ "type": "boolean"
371
+ }
372
+ }
373
+ },
374
+ "open": {
375
+ "type": "object",
376
+ "additionalProperties": false,
377
+ "properties": {
378
+ "file": {
379
+ "type": "string"
380
+ },
381
+ "path": {
382
+ "type": "string",
383
+ "pattern": "^(?![/\\\\~]|[A-Za-z]:).+$",
384
+ "description": "vault-relative path (not absolute)"
385
+ },
386
+ "vault": {
387
+ "type": "string"
388
+ }
389
+ }
390
+ }
391
+ }
392
+ }
@@ -0,0 +1,79 @@
1
+ # Finding and Reading Notes
2
+
3
+ One `obsidian` command per `exec` call, no chaining. Skip `vault info` unless
4
+ the vault is ambiguous.
5
+
6
+ | Request | Command |
7
+ | ----------------------------------------- | ------------------------------------------ |
8
+ | "what are my notes" / "list my notes" | `obsidian files` |
9
+ | "list notes in <folder>" | `obsidian files folder="<Folder>"` |
10
+ | "find / search notes about X" | `obsidian search query="X"` |
11
+ | "read / open / summarize note X" | `obsidian read file="X"` (name, no `.md`) |
12
+ | "what's in my daily note" | `obsidian daily:read` |
13
+ | "list my tags" | `obsidian tags` |
14
+
15
+ ## List or search
16
+
17
+ ```bash
18
+ obsidian files
19
+ obsidian files folder="Projects" ext=md
20
+ obsidian search query="release handoff" limit=10
21
+ ```
22
+
23
+ `search` REQUIRES `query="..."`. Add `limit=` to keep results small; if they are
24
+ still broad, ask a narrowing question or rerun with a tighter query.
25
+
26
+ ## Read
27
+
28
+ Read by NAME (no `.md`) or by a VAULT-RELATIVE path — never an absolute
29
+ filesystem path (`/Users/.../Vault/Note.md` fails with "File not found").
30
+ Prefer `path=` when two notes may share a name.
31
+
32
+ ```bash
33
+ obsidian read file="QVAC"
34
+ obsidian read path="Projects/QVAC.md"
35
+ obsidian daily:read
36
+ ```
37
+
38
+ ## Structured output
39
+
40
+ For counts or machine-readable output add `total`, `counts`, or `format=json`:
41
+
42
+ ```bash
43
+ obsidian files folder="Projects" ext=md total
44
+ obsidian tags counts format=json
45
+ ```
46
+
47
+ ## Backlinks and tasks (CLI mode only)
48
+
49
+ `backlinks` and `tasks` need the running Obsidian app; in vault-folder mode
50
+ they fail with `not supported in vault file mode` — then say so instead of
51
+ retrying or falling back to shell tools.
52
+
53
+ ```bash
54
+ obsidian backlinks path="Projects/QVAC.md" format=json
55
+ obsidian tasks todo verbose
56
+ ```
57
+
58
+ ## Vault checks (only when needed)
59
+
60
+ ```bash
61
+ obsidian vaults
62
+ obsidian vault info=name
63
+ obsidian version
64
+ ```
65
+
66
+ ## Answering
67
+
68
+ Give the note path and the exact line or excerpt that supports the answer. Do
69
+ not dump whole notes or the whole vault unless asked. A read-only request ends
70
+ after the read — never follow it with a write.
71
+
72
+ ## Common Mistakes
73
+
74
+ - Shelling out with `cat`, `ls`, `find`, `grep`, or filesystem tools instead of
75
+ `obsidian` commands. Always use `obsidian`, even for a single note.
76
+ - Passing an absolute filesystem path to `read`. Use `file="Name"` or a
77
+ vault-relative `path="Folder/Note.md"`.
78
+ - Calling `search` without `query=`.
79
+ - Running `obsidian vault info` before every request.
@@ -0,0 +1,67 @@
1
+ # Creating and Changing Notes
2
+
3
+ One `obsidian` command per `exec` call, no chaining. Writes must go through
4
+ `obsidian` CLI commands — never direct filesystem writes.
5
+
6
+ | Request | Command |
7
+ | -------------------------------- | --------------------------------------------------------- |
8
+ | "create a note" | `obsidian create name="Title" content="..."` |
9
+ | "create a note in <folder>" | `obsidian create path="Folder/Title.md" content="..."` |
10
+ | "add to note X" | `obsidian append file="X" content="..."` |
11
+ | "add to the top of note X" | `obsidian prepend file="X" content="..."` |
12
+ | "add to my daily note" | `obsidian daily:append content="..."` |
13
+ | "rename / move / delete note X" | only when explicitly asked — see below |
14
+
15
+ ## Create
16
+
17
+ Name new notes with `name="Title"` or an exact `path="Folder/Title.md"`. Never
18
+ use `file=` with `create`: the official CLI ignores it and silently creates an
19
+ "Untitled" note. `content=` is Markdown; write line breaks as `\n`.
20
+
21
+ ```bash
22
+ obsidian create name="QVAC" content="# QVAC\n\nInitial note"
23
+ obsidian create path="Projects/QVAC.md" content="# QVAC\n\nInitial note"
24
+ ```
25
+
26
+ ## Append and prepend
27
+
28
+ Target by NAME (no `.md`) or by a VAULT-RELATIVE path — never an absolute
29
+ filesystem path. Prefer `path=` when two notes may share a name.
30
+
31
+ ```bash
32
+ obsidian append file="QVAC" content="- [ ] Follow up on QVAC notes"
33
+ obsidian append path="Daily/2026-05-29.md" content="- [ ] Follow up on QVAC notes"
34
+ obsidian prepend path="Projects/QVAC.md" content="> Updated 2026-05-29"
35
+ obsidian daily:append content="- [ ] Review local Obsidian skill wiring"
36
+ ```
37
+
38
+ To change existing text inside a note, read it first (`references/read.md`),
39
+ then `append`/`prepend` the corrected content — there is no in-place replace.
40
+
41
+ ## Rename, move, delete
42
+
43
+ Run these ONLY when the user explicitly asks for that exact side effect, and
44
+ confirm the target path when the request is ambiguous.
45
+
46
+ ```bash
47
+ obsidian rename path="Projects/QVAC.md" name="QVAC v2"
48
+ obsidian move path="Projects/QVAC.md" to="Archive/QVAC.md"
49
+ obsidian delete path="Archive/QVAC.md" force
50
+ ```
51
+
52
+ Avoid `overwrite`, plugin commands, sync commands, and theme commands unless the
53
+ user explicitly asks.
54
+
55
+ ## Answering
56
+
57
+ Do not claim a note changed unless the `obsidian` command completed
58
+ successfully. Confirm with the note path and what was written.
59
+
60
+ ## Common Mistakes
61
+
62
+ - Using `create file="Title"` — the CLI ignores `file=` on create and makes an
63
+ "Untitled" note. Use `name="Title"` or `path=`.
64
+ - Passing an absolute filesystem path to `create` or `append`.
65
+ - Using `file=` when two notes may share the same name. Prefer exact `path=`.
66
+ - Writing through `echo`, redirection, or file tools instead of `obsidian`.
67
+ - Treating a failed command as success.
@@ -0,0 +1,110 @@
1
+ ---
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
+ }
16
+ ---
17
+
18
+ # PDF
19
+
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.
65
+
66
+ ## When NOT to Use
67
+
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
+ - The user wants a slide deck — use the presentations skill; a spreadsheet or
77
+ Word file — this skill cannot read or write Office formats.
78
+ - The user wants a single image — call `generate_image` alone.
79
+
80
+ ## Rules for Every Job
81
+
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.