@farazirfan/costar-server-executor 1.7.37 → 1.7.39

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 (253) hide show
  1. package/dist/agent/agent.d.ts +90 -0
  2. package/dist/agent/agent.d.ts.map +1 -1
  3. package/dist/agent/agent.js +606 -0
  4. package/dist/agent/agent.js.map +1 -1
  5. package/dist/agent/pi-embedded-runner/run.d.ts.map +1 -1
  6. package/dist/agent/pi-embedded-runner/run.js +2 -1
  7. package/dist/agent/pi-embedded-runner/run.js.map +1 -1
  8. package/dist/agent/pi-embedded-runner/system-prompt.d.ts.map +1 -1
  9. package/dist/agent/pi-embedded-runner/system-prompt.js +16 -37
  10. package/dist/agent/pi-embedded-runner/system-prompt.js.map +1 -1
  11. package/dist/agent/pi-embedded-runner/tools.d.ts +4 -1
  12. package/dist/agent/pi-embedded-runner/tools.d.ts.map +1 -1
  13. package/dist/agent/pi-embedded-runner/tools.js +3 -1
  14. package/dist/agent/pi-embedded-runner/tools.js.map +1 -1
  15. package/dist/agent/pi-embedded-runner/types.d.ts +4 -0
  16. package/dist/agent/pi-embedded-runner/types.d.ts.map +1 -1
  17. package/dist/cli/env-loader.d.ts.map +1 -1
  18. package/dist/cli/env-loader.js +1 -0
  19. package/dist/cli/env-loader.js.map +1 -1
  20. package/dist/cli/setup.js +2 -2
  21. package/dist/cli/setup.js.map +1 -1
  22. package/dist/cron/normalize.d.ts +31 -0
  23. package/dist/cron/normalize.d.ts.map +1 -0
  24. package/dist/cron/normalize.js +211 -0
  25. package/dist/cron/normalize.js.map +1 -0
  26. package/dist/cron/scheduler.d.ts +33 -3
  27. package/dist/cron/scheduler.d.ts.map +1 -1
  28. package/dist/cron/scheduler.js +253 -48
  29. package/dist/cron/scheduler.js.map +1 -1
  30. package/dist/heartbeat/runner.d.ts +27 -12
  31. package/dist/heartbeat/runner.d.ts.map +1 -1
  32. package/dist/heartbeat/runner.js +82 -104
  33. package/dist/heartbeat/runner.js.map +1 -1
  34. package/dist/infra/heartbeat-events-filter.d.ts +29 -0
  35. package/dist/infra/heartbeat-events-filter.d.ts.map +1 -0
  36. package/dist/infra/heartbeat-events-filter.js +80 -0
  37. package/dist/infra/heartbeat-events-filter.js.map +1 -0
  38. package/dist/infra/index.d.ts +9 -0
  39. package/dist/infra/index.d.ts.map +1 -0
  40. package/dist/infra/index.js +9 -0
  41. package/dist/infra/index.js.map +1 -0
  42. package/dist/infra/system-events.d.ts +58 -2
  43. package/dist/infra/system-events.d.ts.map +1 -1
  44. package/dist/infra/system-events.js +80 -14
  45. package/dist/infra/system-events.js.map +1 -1
  46. package/dist/server.d.ts.map +1 -1
  47. package/dist/server.js +6 -1
  48. package/dist/server.js.map +1 -1
  49. package/dist/services/platform-keys.d.ts +19 -0
  50. package/dist/services/platform-keys.d.ts.map +1 -0
  51. package/dist/services/platform-keys.js +74 -0
  52. package/dist/services/platform-keys.js.map +1 -0
  53. package/dist/subagent/registry.d.ts +96 -0
  54. package/dist/subagent/registry.d.ts.map +1 -0
  55. package/dist/subagent/registry.js +180 -0
  56. package/dist/subagent/registry.js.map +1 -0
  57. package/dist/tools/complete-turn.d.ts +2 -2
  58. package/dist/tools/complete-turn.js +10 -10
  59. package/dist/tools/complete-turn.js.map +1 -1
  60. package/dist/tools/contacts.d.ts +13 -0
  61. package/dist/tools/contacts.d.ts.map +1 -0
  62. package/dist/tools/contacts.js +80 -0
  63. package/dist/tools/contacts.js.map +1 -0
  64. package/dist/tools/cron.d.ts +17 -2
  65. package/dist/tools/cron.d.ts.map +1 -1
  66. package/dist/tools/cron.js +117 -35
  67. package/dist/tools/cron.js.map +1 -1
  68. package/dist/tools/google-maps.d.ts +6 -6
  69. package/dist/tools/google-maps.d.ts.map +1 -1
  70. package/dist/tools/google-maps.js +207 -262
  71. package/dist/tools/google-maps.js.map +1 -1
  72. package/dist/tools/index.d.ts +17 -7
  73. package/dist/tools/index.d.ts.map +1 -1
  74. package/dist/tools/index.js +40 -9
  75. package/dist/tools/index.js.map +1 -1
  76. package/dist/tools/phone-call.d.ts +11 -0
  77. package/dist/tools/phone-call.d.ts.map +1 -0
  78. package/dist/tools/phone-call.js +151 -0
  79. package/dist/tools/phone-call.js.map +1 -0
  80. package/dist/tools/sessions-spawn.d.ts +33 -0
  81. package/dist/tools/sessions-spawn.d.ts.map +1 -0
  82. package/dist/tools/sessions-spawn.js +164 -0
  83. package/dist/tools/sessions-spawn.js.map +1 -0
  84. package/dist/tools/spotify.d.ts +12 -0
  85. package/dist/tools/spotify.d.ts.map +1 -0
  86. package/dist/tools/spotify.js +251 -0
  87. package/dist/tools/spotify.js.map +1 -0
  88. package/dist/tools/subagents.d.ts +23 -0
  89. package/dist/tools/subagents.d.ts.map +1 -0
  90. package/dist/tools/subagents.js +209 -0
  91. package/dist/tools/subagents.js.map +1 -0
  92. package/dist/tools/whatsapp.d.ts +13 -0
  93. package/dist/tools/whatsapp.d.ts.map +1 -0
  94. package/dist/tools/whatsapp.js +215 -0
  95. package/dist/tools/whatsapp.js.map +1 -0
  96. package/dist/tools/youtube.d.ts +12 -0
  97. package/dist/tools/youtube.d.ts.map +1 -0
  98. package/dist/tools/youtube.js +218 -0
  99. package/dist/tools/youtube.js.map +1 -0
  100. package/dist/utils/asterizk-auth.d.ts +43 -0
  101. package/dist/utils/asterizk-auth.d.ts.map +1 -0
  102. package/dist/utils/asterizk-auth.js +125 -0
  103. package/dist/utils/asterizk-auth.js.map +1 -0
  104. package/dist/web-server.d.ts.map +1 -1
  105. package/dist/web-server.js +132 -0
  106. package/dist/web-server.js.map +1 -1
  107. package/dist/workspace/index.d.ts +3 -4
  108. package/dist/workspace/index.d.ts.map +1 -1
  109. package/dist/workspace/index.js +3 -4
  110. package/dist/workspace/index.js.map +1 -1
  111. package/dist/workspace/templates.d.ts +8 -7
  112. package/dist/workspace/templates.d.ts.map +1 -1
  113. package/dist/workspace/templates.js +18 -127
  114. package/dist/workspace/templates.js.map +1 -1
  115. package/dist/workspace/workspace.d.ts +2 -4
  116. package/dist/workspace/workspace.d.ts.map +1 -1
  117. package/dist/workspace/workspace.js +7 -16
  118. package/dist/workspace/workspace.js.map +1 -1
  119. package/package.json +1 -1
  120. package/public/index.html +231 -0
  121. package/skills/docx/SKILL.md +468 -0
  122. package/skills/docx/scripts/__init__.py +1 -0
  123. package/skills/docx/scripts/accept_changes.py +181 -0
  124. package/skills/docx/scripts/comment.py +347 -0
  125. package/skills/docx/scripts/helpers/__init__.py +0 -0
  126. package/skills/docx/scripts/helpers/merge_runs.py +231 -0
  127. package/skills/docx/scripts/helpers/simplify_redlines.py +240 -0
  128. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  129. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  130. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  131. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  132. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  133. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  134. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  135. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  136. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  137. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  138. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  139. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  140. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  141. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  142. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  143. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  144. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  145. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  146. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  147. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  148. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  149. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  150. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  151. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  152. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  153. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  154. package/skills/docx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  155. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  156. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  157. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  158. package/skills/docx/scripts/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  159. package/skills/docx/scripts/ooxml/schemas/mce/mc.xsd +75 -0
  160. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  161. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  162. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  163. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  164. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  165. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  166. package/skills/docx/scripts/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  167. package/skills/docx/scripts/ooxml/scripts/pack.py +159 -0
  168. package/skills/docx/scripts/ooxml/scripts/unpack.py +29 -0
  169. package/skills/docx/scripts/ooxml/scripts/validate.py +106 -0
  170. package/skills/docx/scripts/ooxml/scripts/validation/__init__.py +15 -0
  171. package/skills/docx/scripts/ooxml/scripts/validation/base.py +1023 -0
  172. package/skills/docx/scripts/ooxml/scripts/validation/docx.py +519 -0
  173. package/skills/docx/scripts/ooxml/scripts/validation/pptx.py +315 -0
  174. package/skills/docx/scripts/ooxml/scripts/validation/redlining.py +284 -0
  175. package/skills/docx/scripts/pack.py +166 -0
  176. package/skills/docx/scripts/templates/comments.xml +3 -0
  177. package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  178. package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  179. package/skills/docx/scripts/templates/commentsIds.xml +3 -0
  180. package/skills/docx/scripts/templates/people.xml +3 -0
  181. package/skills/docx/scripts/unpack.py +134 -0
  182. package/skills/longform-video-generation/SKILL.md +298 -0
  183. package/skills/longform-video-generation/references/advanced_techniques.md +474 -0
  184. package/skills/longform-video-generation/references/google_api_guide.md +288 -0
  185. package/skills/longform-video-generation/scripts/video_generator.py +579 -0
  186. package/skills/pdf/FORMS.md +305 -0
  187. package/skills/pdf/REFERENCE.md +612 -0
  188. package/skills/pdf/SKILL.md +293 -0
  189. package/skills/pdf/scripts/check_bounding_boxes.py +70 -0
  190. package/skills/pdf/scripts/check_fillable_fields.py +12 -0
  191. package/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
  192. package/skills/pdf/scripts/create_validation_image.py +41 -0
  193. package/skills/pdf/scripts/extract_form_field_info.py +152 -0
  194. package/skills/pdf/scripts/extract_form_structure.py +124 -0
  195. package/skills/pdf/scripts/fill_fillable_fields.py +116 -0
  196. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +136 -0
  197. package/skills/pptx/SKILL.md +171 -0
  198. package/skills/pptx/editing.md +205 -0
  199. package/skills/pptx/pptxgenjs.md +377 -0
  200. package/skills/pptx/scripts/add_slide.py +225 -0
  201. package/skills/pptx/scripts/clean.py +309 -0
  202. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  203. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  204. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  205. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  206. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  207. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  208. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  209. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  210. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  211. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  212. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  213. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  214. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  215. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  216. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  217. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  218. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  219. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  220. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  221. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  222. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  223. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  224. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  225. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  226. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  227. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  228. package/skills/pptx/scripts/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  229. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  230. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  231. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  232. package/skills/pptx/scripts/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  233. package/skills/pptx/scripts/ooxml/schemas/mce/mc.xsd +75 -0
  234. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  235. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  236. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  237. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  238. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  239. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  240. package/skills/pptx/scripts/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  241. package/skills/pptx/scripts/ooxml/scripts/pack.py +159 -0
  242. package/skills/pptx/scripts/ooxml/scripts/unpack.py +29 -0
  243. package/skills/pptx/scripts/ooxml/scripts/validate.py +106 -0
  244. package/skills/pptx/scripts/ooxml/scripts/validation/__init__.py +15 -0
  245. package/skills/pptx/scripts/ooxml/scripts/validation/base.py +1023 -0
  246. package/skills/pptx/scripts/ooxml/scripts/validation/docx.py +519 -0
  247. package/skills/pptx/scripts/ooxml/scripts/validation/pptx.py +315 -0
  248. package/skills/pptx/scripts/ooxml/scripts/validation/redlining.py +284 -0
  249. package/skills/pptx/scripts/pack.py +168 -0
  250. package/skills/pptx/scripts/thumbnail.py +318 -0
  251. package/skills/pptx/scripts/unpack.py +86 -0
  252. package/skills/xlsx/SKILL.md +291 -0
  253. package/skills/xlsx/recalc.py +247 -0
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Extract form structure from a non-fillable PDF.
4
+
5
+ This script analyzes the PDF to find:
6
+ - Text labels with their exact coordinates
7
+ - Horizontal lines (row boundaries)
8
+ - Checkboxes (small rectangles)
9
+
10
+ Output: A JSON file with the form structure that can be used to generate
11
+ accurate field coordinates for filling.
12
+
13
+ Usage: python extract_form_structure.py <input.pdf> <output.json>
14
+ """
15
+
16
+ import json
17
+ import sys
18
+ import pdfplumber
19
+
20
+
21
+ def extract_form_structure(pdf_path):
22
+ """Extract structural elements from a PDF form."""
23
+ structure = {
24
+ "pages": [],
25
+ "labels": [],
26
+ "lines": [],
27
+ "checkboxes": [],
28
+ "row_boundaries": []
29
+ }
30
+
31
+ with pdfplumber.open(pdf_path) as pdf:
32
+ for page_num, page in enumerate(pdf.pages, 1):
33
+ # Page info
34
+ structure["pages"].append({
35
+ "page_number": page_num,
36
+ "width": float(page.width),
37
+ "height": float(page.height)
38
+ })
39
+
40
+ # Extract text labels with positions
41
+ words = page.extract_words()
42
+ for word in words:
43
+ structure["labels"].append({
44
+ "page": page_num,
45
+ "text": word["text"],
46
+ "x0": round(float(word["x0"]), 1),
47
+ "top": round(float(word["top"]), 1),
48
+ "x1": round(float(word["x1"]), 1),
49
+ "bottom": round(float(word["bottom"]), 1)
50
+ })
51
+
52
+ # Extract horizontal lines (row separators)
53
+ for line in page.lines:
54
+ # Horizontal lines span most of page width
55
+ if abs(float(line["x1"]) - float(line["x0"])) > page.width * 0.5:
56
+ structure["lines"].append({
57
+ "page": page_num,
58
+ "y": round(float(line["top"]), 1),
59
+ "x0": round(float(line["x0"]), 1),
60
+ "x1": round(float(line["x1"]), 1)
61
+ })
62
+
63
+ # Extract checkboxes (small square rectangles)
64
+ for rect in page.rects:
65
+ width = float(rect["x1"]) - float(rect["x0"])
66
+ height = float(rect["bottom"]) - float(rect["top"])
67
+ # Checkboxes are typically 5-15 points square
68
+ if 5 <= width <= 15 and 5 <= height <= 15 and abs(width - height) < 2:
69
+ structure["checkboxes"].append({
70
+ "page": page_num,
71
+ "x0": round(float(rect["x0"]), 1),
72
+ "top": round(float(rect["top"]), 1),
73
+ "x1": round(float(rect["x1"]), 1),
74
+ "bottom": round(float(rect["bottom"]), 1),
75
+ "center_x": round((float(rect["x0"]) + float(rect["x1"])) / 2, 1),
76
+ "center_y": round((float(rect["top"]) + float(rect["bottom"])) / 2, 1)
77
+ })
78
+
79
+ # Calculate row boundaries from horizontal lines
80
+ lines_by_page = {}
81
+ for line in structure["lines"]:
82
+ page = line["page"]
83
+ if page not in lines_by_page:
84
+ lines_by_page[page] = []
85
+ lines_by_page[page].append(line["y"])
86
+
87
+ for page, y_coords in lines_by_page.items():
88
+ y_coords = sorted(set(y_coords))
89
+ for i in range(len(y_coords) - 1):
90
+ structure["row_boundaries"].append({
91
+ "page": page,
92
+ "row_top": y_coords[i],
93
+ "row_bottom": y_coords[i + 1],
94
+ "row_height": round(y_coords[i + 1] - y_coords[i], 1)
95
+ })
96
+
97
+ return structure
98
+
99
+
100
+ def main():
101
+ if len(sys.argv) != 3:
102
+ print("Usage: extract_form_structure.py <input.pdf> <output.json>")
103
+ sys.exit(1)
104
+
105
+ pdf_path = sys.argv[1]
106
+ output_path = sys.argv[2]
107
+
108
+ print(f"Extracting structure from {pdf_path}...")
109
+ structure = extract_form_structure(pdf_path)
110
+
111
+ with open(output_path, "w") as f:
112
+ json.dump(structure, f, indent=2)
113
+
114
+ print(f"Found:")
115
+ print(f" - {len(structure['pages'])} pages")
116
+ print(f" - {len(structure['labels'])} text labels")
117
+ print(f" - {len(structure['lines'])} horizontal lines")
118
+ print(f" - {len(structure['checkboxes'])} checkboxes")
119
+ print(f" - {len(structure['row_boundaries'])} row boundaries")
120
+ print(f"Saved to {output_path}")
121
+
122
+
123
+ if __name__ == "__main__":
124
+ main()
@@ -0,0 +1,116 @@
1
+ import json
2
+ import sys
3
+
4
+ from pypdf import PdfReader, PdfWriter
5
+
6
+ from extract_form_field_info import get_field_info
7
+
8
+
9
+ # Fills fillable form fields in a PDF. See FORMS.md.
10
+
11
+
12
+ def fill_pdf_fields(input_pdf_path: str, fields_json_path: str, output_pdf_path: str):
13
+ with open(fields_json_path) as f:
14
+ fields = json.load(f)
15
+ # Group by page number.
16
+ fields_by_page = {}
17
+ for field in fields:
18
+ if "value" in field:
19
+ field_id = field["field_id"]
20
+ page = field["page"]
21
+ if page not in fields_by_page:
22
+ fields_by_page[page] = {}
23
+ fields_by_page[page][field_id] = field["value"]
24
+
25
+ reader = PdfReader(input_pdf_path)
26
+
27
+ has_error = False
28
+ field_info = get_field_info(reader)
29
+ fields_by_ids = {f["field_id"]: f for f in field_info}
30
+ for field in fields:
31
+ existing_field = fields_by_ids.get(field["field_id"])
32
+ if not existing_field:
33
+ has_error = True
34
+ print(f"ERROR: `{field['field_id']}` is not a valid field ID")
35
+ elif field["page"] != existing_field["page"]:
36
+ has_error = True
37
+ print(f"ERROR: Incorrect page number for `{field['field_id']}` (got {field['page']}, expected {existing_field['page']})")
38
+ else:
39
+ if "value" in field:
40
+ err = validation_error_for_field_value(existing_field, field["value"])
41
+ if err:
42
+ print(err)
43
+ has_error = True
44
+ if has_error:
45
+ sys.exit(1)
46
+
47
+ writer = PdfWriter(clone_from=reader)
48
+ for page, field_values in fields_by_page.items():
49
+ writer.update_page_form_field_values(writer.pages[page - 1], field_values, auto_regenerate=False)
50
+
51
+ # This seems to be necessary for many PDF viewers to format the form values correctly.
52
+ # It may cause the viewer to show a "save changes" dialog even if the user doesn't make any changes.
53
+ writer.set_need_appearances_writer(True)
54
+
55
+ with open(output_pdf_path, "wb") as f:
56
+ writer.write(f)
57
+
58
+ print(f"PDF form filled and saved to {output_pdf_path}")
59
+
60
+
61
+ def validation_error_for_field_value(field_info, field_value):
62
+ field_type = field_info["type"]
63
+ field_id = field_info["field_id"]
64
+ if field_type == "checkbox":
65
+ checked_val = field_info["checked_value"]
66
+ unchecked_val = field_info["unchecked_value"]
67
+ if field_value != checked_val and field_value != unchecked_val:
68
+ return f'ERROR: Invalid value "{field_value}" for checkbox field "{field_id}". The checked value is "{checked_val}" and the unchecked value is "{unchecked_val}"'
69
+ elif field_type == "radio_group":
70
+ option_values = [opt["value"] for opt in field_info["radio_options"]]
71
+ if field_value not in option_values:
72
+ return f'ERROR: Invalid value "{field_value}" for radio group field "{field_id}". Valid values are: {option_values}'
73
+ elif field_type == "choice":
74
+ choice_values = [opt["value"] for opt in field_info["choice_options"]]
75
+ if field_value not in choice_values:
76
+ return f'ERROR: Invalid value "{field_value}" for choice field "{field_id}". Valid values are: {choice_values}'
77
+ return None
78
+
79
+
80
+ # pypdf (at least version 5.7.0) has a bug when setting the value for a selection list field.
81
+ # In _writer.py around line 966:
82
+ #
83
+ # if field.get(FA.FT, "/Tx") == "/Ch" and field_flags & FA.FfBits.Combo == 0:
84
+ # txt = "\n".join(annotation.get_inherited(FA.Opt, []))
85
+ #
86
+ # The problem is that for selection lists, `get_inherited` returns a list of two-element lists like
87
+ # [["value1", "Text 1"], ["value2", "Text 2"], ...]
88
+ # This causes `join` to throw a TypeError because it expects an iterable of strings.
89
+ # The horrible workaround is to patch `get_inherited` to return a list of the value strings.
90
+ # We call the original method and adjust the return value only if the argument to `get_inherited`
91
+ # is `FA.Opt` and if the return value is a list of two-element lists.
92
+ def monkeypatch_pydpf_method():
93
+ from pypdf.generic import DictionaryObject
94
+ from pypdf.constants import FieldDictionaryAttributes
95
+
96
+ original_get_inherited = DictionaryObject.get_inherited
97
+
98
+ def patched_get_inherited(self, key: str, default = None):
99
+ result = original_get_inherited(self, key, default)
100
+ if key == FieldDictionaryAttributes.Opt:
101
+ if isinstance(result, list) and all(isinstance(v, list) and len(v) == 2 for v in result):
102
+ result = [r[0] for r in result]
103
+ return result
104
+
105
+ DictionaryObject.get_inherited = patched_get_inherited
106
+
107
+
108
+ if __name__ == "__main__":
109
+ if len(sys.argv) != 4:
110
+ print("Usage: fill_fillable_fields.py [input pdf] [field_values.json] [output pdf]")
111
+ sys.exit(1)
112
+ monkeypatch_pydpf_method()
113
+ input_pdf = sys.argv[1]
114
+ fields_json = sys.argv[2]
115
+ output_pdf = sys.argv[3]
116
+ fill_pdf_fields(input_pdf, fields_json, output_pdf)
@@ -0,0 +1,136 @@
1
+ import json
2
+ import sys
3
+
4
+ from pypdf import PdfReader, PdfWriter
5
+ from pypdf.annotations import FreeText
6
+
7
+
8
+ # Fills a PDF by adding text annotations defined in `fields.json`. See FORMS.md.
9
+
10
+
11
+ def transform_from_image_coords(bbox, image_width, image_height, pdf_width, pdf_height):
12
+ """Transform bounding box from image coordinates to PDF coordinates"""
13
+ # Image coordinates: origin at top-left, y increases downward
14
+ # PDF coordinates: origin at bottom-left, y increases upward
15
+ x_scale = pdf_width / image_width
16
+ y_scale = pdf_height / image_height
17
+
18
+ left = bbox[0] * x_scale
19
+ right = bbox[2] * x_scale
20
+
21
+ # Flip Y coordinates for PDF
22
+ top = pdf_height - (bbox[1] * y_scale)
23
+ bottom = pdf_height - (bbox[3] * y_scale)
24
+
25
+ return left, bottom, right, top
26
+
27
+
28
+ def transform_from_pdf_coords(bbox, pdf_height):
29
+ """Transform bounding box from pdfplumber coordinates to pypdf coordinates.
30
+
31
+ pdfplumber uses y=0 at top, y increases downward (like images).
32
+ pypdf FreeText expects y=0 at bottom, y increases upward.
33
+ Both use the same scale (PDF points), so only Y needs flipping.
34
+ """
35
+ left = bbox[0]
36
+ right = bbox[2]
37
+
38
+ # bbox is [left, top, right, bottom] where top < bottom (y=0 at top)
39
+ # pypdf wants [left, bottom, right, top] where bottom < top (y=0 at bottom)
40
+ pypdf_top = pdf_height - bbox[1] # flip the "top" value
41
+ pypdf_bottom = pdf_height - bbox[3] # flip the "bottom" value
42
+
43
+ return left, pypdf_bottom, right, pypdf_top
44
+
45
+
46
+ def fill_pdf_form(input_pdf_path, fields_json_path, output_pdf_path):
47
+ """Fill the PDF form with data from fields.json"""
48
+
49
+ # `fields.json` format described in FORMS.md.
50
+ with open(fields_json_path, "r") as f:
51
+ fields_data = json.load(f)
52
+
53
+ # Open the PDF
54
+ reader = PdfReader(input_pdf_path)
55
+ writer = PdfWriter()
56
+
57
+ # Copy all pages to writer
58
+ writer.append(reader)
59
+
60
+ # Get PDF dimensions for each page
61
+ pdf_dimensions = {}
62
+ for i, page in enumerate(reader.pages):
63
+ mediabox = page.mediabox
64
+ pdf_dimensions[i + 1] = [mediabox.width, mediabox.height]
65
+
66
+ # Process each form field
67
+ annotations = []
68
+ for field in fields_data["form_fields"]:
69
+ page_num = field["page_number"]
70
+
71
+ # Get page dimensions and transform coordinates.
72
+ page_info = next(p for p in fields_data["pages"] if p["page_number"] == page_num)
73
+ pdf_width, pdf_height = pdf_dimensions[page_num]
74
+
75
+ # Detect coordinate system: pdf_width/pdf_height = PDF coords, image_width/image_height = image coords
76
+ if "pdf_width" in page_info:
77
+ # PDF coordinates from structure extraction (pdfplumber style)
78
+ # Only need Y-flip, no scaling
79
+ transformed_entry_box = transform_from_pdf_coords(
80
+ field["entry_bounding_box"],
81
+ float(pdf_height)
82
+ )
83
+ else:
84
+ # Image coordinates - need scaling and Y-flip
85
+ image_width = page_info["image_width"]
86
+ image_height = page_info["image_height"]
87
+ transformed_entry_box = transform_from_image_coords(
88
+ field["entry_bounding_box"],
89
+ image_width, image_height,
90
+ float(pdf_width), float(pdf_height)
91
+ )
92
+
93
+ # Skip empty fields
94
+ if "entry_text" not in field or "text" not in field["entry_text"]:
95
+ continue
96
+ entry_text = field["entry_text"]
97
+ text = entry_text["text"]
98
+ if not text:
99
+ continue
100
+
101
+ font_name = entry_text.get("font", "Arial")
102
+ font_size = str(entry_text.get("font_size", 14)) + "pt"
103
+ font_color = entry_text.get("font_color", "000000")
104
+
105
+ # Font size/color seems to not work reliably across viewers:
106
+ # https://github.com/py-pdf/pypdf/issues/2084
107
+ annotation = FreeText(
108
+ text=text,
109
+ rect=transformed_entry_box,
110
+ font=font_name,
111
+ font_size=font_size,
112
+ font_color=font_color,
113
+ border_color=None,
114
+ background_color=None,
115
+ )
116
+ annotations.append(annotation)
117
+ # page_number is 0-based for pypdf
118
+ writer.add_annotation(page_number=page_num - 1, annotation=annotation)
119
+
120
+ # Save the filled PDF
121
+ with open(output_pdf_path, "wb") as output:
122
+ writer.write(output)
123
+
124
+ print(f"Successfully filled PDF form and saved to {output_pdf_path}")
125
+ print(f"Added {len(annotations)} text annotations")
126
+
127
+
128
+ if __name__ == "__main__":
129
+ if len(sys.argv) != 4:
130
+ print("Usage: fill_pdf_form_with_annotations.py [input pdf] [fields.json] [output pdf]")
131
+ sys.exit(1)
132
+ input_pdf = sys.argv[1]
133
+ fields_json = sys.argv[2]
134
+ output_pdf = sys.argv[3]
135
+
136
+ fill_pdf_form(input_pdf, fields_json, output_pdf)
@@ -0,0 +1,171 @@
1
+ ---
2
+ name: pptx
3
+ description: "Presentation creation, editing, and analysis. When Claude needs to work with presentations (.pptx files) for: (1) Creating new presentations, (2) Modifying or editing content, (3) Working with layouts, (4) Adding comments or speaker notes, or any other presentation tasks"
4
+ license: Proprietary. LICENSE.txt has complete terms
5
+ ---
6
+
7
+ # PPTX Skill
8
+
9
+ ## Quick Reference
10
+
11
+ | Task | Guide |
12
+ |------|-------|
13
+ | Read/analyze content | `python -m markitdown presentation.pptx` |
14
+ | Edit or create from template | Read [editing.md](editing.md) (Python) |
15
+ | Create from scratch | Read [pptxgenjs.md](pptxgenjs.md) (Node.js) |
16
+
17
+ ---
18
+
19
+ ## Reading Content
20
+
21
+ ```bash
22
+ # Text extraction
23
+ python -m markitdown presentation.pptx
24
+
25
+ # Visual overview
26
+ python scripts/thumbnail.py presentation.pptx
27
+
28
+ # Raw XML
29
+ python scripts/unpack.py presentation.pptx unpacked/
30
+ ```
31
+
32
+ ---
33
+
34
+ ## Editing Workflow
35
+
36
+ **Read [editing.md](editing.md) for full details.**
37
+
38
+ 1. Analyze template with `thumbnail.py`
39
+ 2. Unpack → manipulate slides → edit content → clean → pack
40
+
41
+ ---
42
+
43
+ ## Creating from Scratch
44
+
45
+ **Read [pptxgenjs.md](pptxgenjs.md) for full details.**
46
+
47
+ Use when no template or reference presentation is available.
48
+
49
+ **Important:** PptxGenJS is a **Node.js/JavaScript** library. Create a `.js` file and run it with `node script.js`.
50
+
51
+ ---
52
+
53
+ ## Design Ideas
54
+
55
+ **Don't create boring slides.** Plain bullets on a white background won't impress anyone. Consider ideas from this list for each slide.
56
+
57
+ ### Before Starting
58
+
59
+ - **Pick a bold, content-informed color palette**: The palette should feel designed for THIS topic. If swapping your colors into a completely different presentation would still "work," you haven't made specific enough choices.
60
+ - **Dominance over equality**: One color should dominate (60-70% visual weight), with 1-2 supporting tones and one sharp accent. Never give all colors equal weight.
61
+ - **Dark/light contrast**: Dark backgrounds for title + conclusion slides, light for content ("sandwich" structure). Or commit to dark throughout for a premium feel.
62
+ - **Commit to a visual motif**: Pick ONE distinctive element and repeat it — rounded image frames, icons in colored circles, thick single-side borders. Carry it across every slide.
63
+
64
+ ### Color Palettes
65
+
66
+ Choose colors that match your topic — don't default to generic blue. Use these palettes as inspiration:
67
+
68
+ | Theme | Primary | Secondary | Accent |
69
+ |-------|---------|-----------|--------|
70
+ | **Midnight Executive** | `1E2761` (navy) | `CADCFC` (ice blue) | `FFFFFF` (white) |
71
+ | **Forest & Moss** | `2C5F2D` (forest) | `97BC62` (moss) | `F5F5F5` (cream) |
72
+ | **Coral Energy** | `F96167` (coral) | `F9E795` (gold) | `2F3C7E` (navy) |
73
+ | **Warm Terracotta** | `B85042` (terracotta) | `E7E8D1` (sand) | `A7BEAE` (sage) |
74
+ | **Ocean Gradient** | `065A82` (deep blue) | `1C7293` (teal) | `21295C` (midnight) |
75
+ | **Charcoal Minimal** | `36454F` (charcoal) | `F2F2F2` (off-white) | `212121` (black) |
76
+ | **Teal Trust** | `028090` (teal) | `00A896` (seafoam) | `02C39A` (mint) |
77
+ | **Berry & Cream** | `6D2E46` (berry) | `A26769` (dusty rose) | `ECE2D0` (cream) |
78
+ | **Sage Calm** | `84B59F` (sage) | `69A297` (eucalyptus) | `50808E` (slate) |
79
+ | **Cherry Bold** | `990011` (cherry) | `FCF6F5` (off-white) | `2F3C7E` (navy) |
80
+
81
+ ### For Each Slide
82
+
83
+ **Every slide needs a visual element** — image, chart, icon, or shape. Text-only slides are forgettable.
84
+
85
+ **Layout options:**
86
+ - Two-column (text left, illustration on right)
87
+ - Icon + text rows (icon in colored circle, bold header, description below)
88
+ - 2x2 or 2x3 grid (image on one side, grid of content blocks on other)
89
+ - Half-bleed image (full left or right side) with content overlay
90
+
91
+ **Data display:**
92
+ - Large stat callouts (big numbers 60-72pt with small labels below)
93
+ - Comparison columns (before/after, pros/cons, side-by-side options)
94
+ - Timeline or process flow (numbered steps, arrows)
95
+
96
+ **Visual polish:**
97
+ - Icons in small colored circles next to section headers
98
+ - Italic accent text for key stats or taglines
99
+
100
+ ### Typography
101
+
102
+ **Choose an interesting font pairing** — don't default to Arial. Pick a header font with personality and pair it with a clean body font.
103
+
104
+ | Header Font | Body Font |
105
+ |-------------|-----------|
106
+ | Georgia | Calibri |
107
+ | Arial Black | Arial |
108
+ | Calibri | Calibri Light |
109
+ | Cambria | Calibri |
110
+ | Trebuchet MS | Calibri |
111
+ | Impact | Arial |
112
+ | Palatino | Garamond |
113
+ | Consolas | Calibri |
114
+
115
+ | Element | Size |
116
+ |---------|------|
117
+ | Slide title | 36-44pt bold |
118
+ | Section header | 20-24pt bold |
119
+ | Body text | 14-16pt |
120
+ | Captions | 10-12pt muted |
121
+
122
+ ### Spacing
123
+
124
+ - 0.5" minimum margins
125
+ - 0.3-0.5" between content blocks
126
+ - Leave breathing room—don't fill every inch
127
+
128
+ ### Avoid (Common Mistakes)
129
+
130
+ - **Don't repeat the same layout** — vary columns, cards, and callouts across slides
131
+ - **Don't center body text** — left-align paragraphs and lists; center only titles
132
+ - **Don't skimp on size contrast** — titles need 36pt+ to stand out from 14-16pt body
133
+ - **Don't default to blue** — pick colors that reflect the specific topic
134
+ - **Don't mix spacing randomly** — choose 0.3" or 0.5" gaps and use consistently
135
+ - **Don't style one slide and leave the rest plain** — commit fully or keep it simple throughout
136
+ - **Don't create text-only slides** — add images, icons, charts, or visual elements; avoid plain title + bullets
137
+ - **Don't forget text box padding** — when aligning lines or shapes with text edges, set `margin: 0` on the text box or offset the shape to account for padding
138
+ - **Don't use low-contrast elements** — icons AND text need strong contrast against the background; avoid light text on light backgrounds or dark text on dark backgrounds
139
+ - **NEVER use horizontal lines to seperate title and body** — use whitespace or background color instead
140
+
141
+ ---
142
+
143
+ ## QA (Required)
144
+
145
+ **Assume there are problems. Your job is to find them.**
146
+
147
+ Your first render is almost never correct. Approach QA as a bug hunt, not a confirmation step. If you found zero issues on first inspection, you weren't looking hard enough.
148
+
149
+ ### Content QA
150
+
151
+ ```bash
152
+ python -m markitdown output.pptx
153
+ ```
154
+
155
+ Check for missing content, typos, wrong order.
156
+
157
+ **When using templates, check for leftover placeholder text:**
158
+
159
+ ```bash
160
+ python -m markitdown output.pptx | grep -iE "xxxx|lorem|ipsum|this.*(page|slide).*layout"
161
+ ```
162
+
163
+ If grep returns results, fix them before declaring success.
164
+
165
+ ---
166
+
167
+ ## Dependencies
168
+
169
+ - `pip install "markitdown[pptx]"` - text extraction
170
+ - `pip install Pillow` - thumbnail grids
171
+ - `npm install -g pptxgenjs` - creating from scratch