docagent-cli 0.0.35__py3-none-any.whl

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 (300) hide show
  1. docagent_cli/__init__.py +36 -0
  2. docagent_cli/__main__.py +6 -0
  3. docagent_cli/_ask_user_types.py +90 -0
  4. docagent_cli/_cli_context.py +27 -0
  5. docagent_cli/_debug.py +52 -0
  6. docagent_cli/_env_vars.py +56 -0
  7. docagent_cli/_server_config.py +352 -0
  8. docagent_cli/_session_stats.py +114 -0
  9. docagent_cli/_testing_models.py +144 -0
  10. docagent_cli/_version.py +17 -0
  11. docagent_cli/agent.py +1193 -0
  12. docagent_cli/app.py +4979 -0
  13. docagent_cli/app.tcss +283 -0
  14. docagent_cli/ask_user.py +301 -0
  15. docagent_cli/built_in_skills/__init__.py +5 -0
  16. docagent_cli/built_in_skills/doc-coauthoring/SKILL.md +375 -0
  17. docagent_cli/built_in_skills/docx/LICENSE.txt +30 -0
  18. docagent_cli/built_in_skills/docx/SKILL.md +590 -0
  19. docagent_cli/built_in_skills/docx/scripts/__init__.py +1 -0
  20. docagent_cli/built_in_skills/docx/scripts/accept_changes.py +135 -0
  21. docagent_cli/built_in_skills/docx/scripts/comment.py +318 -0
  22. docagent_cli/built_in_skills/docx/scripts/office/helpers/__init__.py +0 -0
  23. docagent_cli/built_in_skills/docx/scripts/office/helpers/merge_runs.py +199 -0
  24. docagent_cli/built_in_skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
  25. docagent_cli/built_in_skills/docx/scripts/office/pack.py +159 -0
  26. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  27. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  28. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  29. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  30. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  31. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  32. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  33. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  34. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  35. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  36. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  37. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  38. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  39. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  40. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  41. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  42. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  43. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  44. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  45. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  46. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  47. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  48. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  49. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  50. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  51. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  52. docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  53. docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  54. docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  55. docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  56. docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  57. docagent_cli/built_in_skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
  58. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  59. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  60. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  61. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  62. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  63. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  64. docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  65. docagent_cli/built_in_skills/docx/scripts/office/soffice.py +183 -0
  66. docagent_cli/built_in_skills/docx/scripts/office/unpack.py +132 -0
  67. docagent_cli/built_in_skills/docx/scripts/office/validate.py +111 -0
  68. docagent_cli/built_in_skills/docx/scripts/office/validators/__init__.py +15 -0
  69. docagent_cli/built_in_skills/docx/scripts/office/validators/base.py +847 -0
  70. docagent_cli/built_in_skills/docx/scripts/office/validators/docx.py +446 -0
  71. docagent_cli/built_in_skills/docx/scripts/office/validators/pptx.py +275 -0
  72. docagent_cli/built_in_skills/docx/scripts/office/validators/redlining.py +247 -0
  73. docagent_cli/built_in_skills/docx/scripts/templates/comments.xml +3 -0
  74. docagent_cli/built_in_skills/docx/scripts/templates/commentsExtended.xml +3 -0
  75. docagent_cli/built_in_skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  76. docagent_cli/built_in_skills/docx/scripts/templates/commentsIds.xml +3 -0
  77. docagent_cli/built_in_skills/docx/scripts/templates/people.xml +3 -0
  78. docagent_cli/built_in_skills/pdf/LICENSE.txt +30 -0
  79. docagent_cli/built_in_skills/pdf/SKILL.md +314 -0
  80. docagent_cli/built_in_skills/pdf/forms.md +294 -0
  81. docagent_cli/built_in_skills/pdf/reference.md +612 -0
  82. docagent_cli/built_in_skills/pdf/scripts/check_bounding_boxes.py +65 -0
  83. docagent_cli/built_in_skills/pdf/scripts/check_fillable_fields.py +11 -0
  84. docagent_cli/built_in_skills/pdf/scripts/convert_pdf_to_images.py +33 -0
  85. docagent_cli/built_in_skills/pdf/scripts/create_validation_image.py +37 -0
  86. docagent_cli/built_in_skills/pdf/scripts/extract_form_field_info.py +122 -0
  87. docagent_cli/built_in_skills/pdf/scripts/extract_form_structure.py +115 -0
  88. docagent_cli/built_in_skills/pdf/scripts/fill_fillable_fields.py +98 -0
  89. docagent_cli/built_in_skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
  90. docagent_cli/built_in_skills/pptx/LICENSE.txt +30 -0
  91. docagent_cli/built_in_skills/pptx/SKILL.md +232 -0
  92. docagent_cli/built_in_skills/pptx/editing.md +205 -0
  93. docagent_cli/built_in_skills/pptx/pptxgenjs.md +420 -0
  94. docagent_cli/built_in_skills/pptx/scripts/__init__.py +0 -0
  95. docagent_cli/built_in_skills/pptx/scripts/add_slide.py +195 -0
  96. docagent_cli/built_in_skills/pptx/scripts/clean.py +286 -0
  97. docagent_cli/built_in_skills/pptx/scripts/office/helpers/__init__.py +0 -0
  98. docagent_cli/built_in_skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
  99. docagent_cli/built_in_skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
  100. docagent_cli/built_in_skills/pptx/scripts/office/pack.py +159 -0
  101. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  102. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  103. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  104. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  105. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  106. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  107. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  108. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  109. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  110. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  111. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  112. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  113. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  114. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  115. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  116. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  117. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  118. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  119. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  120. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  121. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  122. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  123. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  124. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  125. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  126. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  127. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  128. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  129. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  130. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  131. docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  132. docagent_cli/built_in_skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
  133. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  134. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  135. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  136. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  137. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  138. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  139. docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  140. docagent_cli/built_in_skills/pptx/scripts/office/soffice.py +183 -0
  141. docagent_cli/built_in_skills/pptx/scripts/office/unpack.py +132 -0
  142. docagent_cli/built_in_skills/pptx/scripts/office/validate.py +111 -0
  143. docagent_cli/built_in_skills/pptx/scripts/office/validators/__init__.py +15 -0
  144. docagent_cli/built_in_skills/pptx/scripts/office/validators/base.py +847 -0
  145. docagent_cli/built_in_skills/pptx/scripts/office/validators/docx.py +446 -0
  146. docagent_cli/built_in_skills/pptx/scripts/office/validators/pptx.py +275 -0
  147. docagent_cli/built_in_skills/pptx/scripts/office/validators/redlining.py +247 -0
  148. docagent_cli/built_in_skills/pptx/scripts/thumbnail.py +289 -0
  149. docagent_cli/built_in_skills/remember/SKILL.md +118 -0
  150. docagent_cli/built_in_skills/skill-creator/LICENSE.txt +202 -0
  151. docagent_cli/built_in_skills/skill-creator/SKILL.md +485 -0
  152. docagent_cli/built_in_skills/skill-creator/agents/analyzer.md +274 -0
  153. docagent_cli/built_in_skills/skill-creator/agents/comparator.md +202 -0
  154. docagent_cli/built_in_skills/skill-creator/agents/grader.md +223 -0
  155. docagent_cli/built_in_skills/skill-creator/assets/eval_review.html +146 -0
  156. docagent_cli/built_in_skills/skill-creator/eval-viewer/generate_review.py +471 -0
  157. docagent_cli/built_in_skills/skill-creator/eval-viewer/viewer.html +1325 -0
  158. docagent_cli/built_in_skills/skill-creator/references/schemas.md +430 -0
  159. docagent_cli/built_in_skills/skill-creator/scripts/__init__.py +0 -0
  160. docagent_cli/built_in_skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
  161. docagent_cli/built_in_skills/skill-creator/scripts/generate_report.py +326 -0
  162. docagent_cli/built_in_skills/skill-creator/scripts/improve_description.py +247 -0
  163. docagent_cli/built_in_skills/skill-creator/scripts/package_skill.py +136 -0
  164. docagent_cli/built_in_skills/skill-creator/scripts/quick_validate.py +103 -0
  165. docagent_cli/built_in_skills/skill-creator/scripts/run_eval.py +310 -0
  166. docagent_cli/built_in_skills/skill-creator/scripts/run_loop.py +328 -0
  167. docagent_cli/built_in_skills/skill-creator/scripts/utils.py +47 -0
  168. docagent_cli/built_in_skills/theme-factory/LICENSE.txt +202 -0
  169. docagent_cli/built_in_skills/theme-factory/SKILL.md +59 -0
  170. docagent_cli/built_in_skills/theme-factory/theme-showcase.pdf +0 -0
  171. docagent_cli/built_in_skills/theme-factory/themes/arctic-frost.md +19 -0
  172. docagent_cli/built_in_skills/theme-factory/themes/botanical-garden.md +19 -0
  173. docagent_cli/built_in_skills/theme-factory/themes/desert-rose.md +19 -0
  174. docagent_cli/built_in_skills/theme-factory/themes/forest-canopy.md +19 -0
  175. docagent_cli/built_in_skills/theme-factory/themes/golden-hour.md +19 -0
  176. docagent_cli/built_in_skills/theme-factory/themes/midnight-galaxy.md +19 -0
  177. docagent_cli/built_in_skills/theme-factory/themes/modern-minimalist.md +19 -0
  178. docagent_cli/built_in_skills/theme-factory/themes/ocean-depths.md +19 -0
  179. docagent_cli/built_in_skills/theme-factory/themes/sunset-boulevard.md +19 -0
  180. docagent_cli/built_in_skills/theme-factory/themes/tech-innovation.md +19 -0
  181. docagent_cli/built_in_skills/xlsx/LICENSE.txt +30 -0
  182. docagent_cli/built_in_skills/xlsx/SKILL.md +292 -0
  183. docagent_cli/built_in_skills/xlsx/scripts/office/helpers/__init__.py +0 -0
  184. docagent_cli/built_in_skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
  185. docagent_cli/built_in_skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
  186. docagent_cli/built_in_skills/xlsx/scripts/office/pack.py +159 -0
  187. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  188. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  189. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  190. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  191. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  192. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  193. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  194. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  195. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  196. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  197. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  198. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  199. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  200. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  201. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  202. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  203. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  204. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  205. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  206. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  207. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  208. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  209. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  210. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  211. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  212. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  213. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  214. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  215. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  216. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  217. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  218. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
  219. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
  220. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
  221. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
  222. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
  223. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
  224. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  225. docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
  226. docagent_cli/built_in_skills/xlsx/scripts/office/soffice.py +183 -0
  227. docagent_cli/built_in_skills/xlsx/scripts/office/unpack.py +132 -0
  228. docagent_cli/built_in_skills/xlsx/scripts/office/validate.py +111 -0
  229. docagent_cli/built_in_skills/xlsx/scripts/office/validators/__init__.py +15 -0
  230. docagent_cli/built_in_skills/xlsx/scripts/office/validators/base.py +847 -0
  231. docagent_cli/built_in_skills/xlsx/scripts/office/validators/docx.py +446 -0
  232. docagent_cli/built_in_skills/xlsx/scripts/office/validators/pptx.py +275 -0
  233. docagent_cli/built_in_skills/xlsx/scripts/office/validators/redlining.py +247 -0
  234. docagent_cli/built_in_skills/xlsx/scripts/recalc.py +184 -0
  235. docagent_cli/clipboard.py +128 -0
  236. docagent_cli/command_registry.py +284 -0
  237. docagent_cli/config.py +2418 -0
  238. docagent_cli/configurable_model.py +162 -0
  239. docagent_cli/default_agent_prompt.md +12 -0
  240. docagent_cli/editor.py +142 -0
  241. docagent_cli/file_ops.py +473 -0
  242. docagent_cli/formatting.py +28 -0
  243. docagent_cli/hooks.py +206 -0
  244. docagent_cli/input.py +787 -0
  245. docagent_cli/integrations/__init__.py +1 -0
  246. docagent_cli/integrations/sandbox_factory.py +873 -0
  247. docagent_cli/integrations/sandbox_provider.py +71 -0
  248. docagent_cli/local_context.py +718 -0
  249. docagent_cli/main.py +1641 -0
  250. docagent_cli/mcp_tools.py +707 -0
  251. docagent_cli/mcp_trust.py +168 -0
  252. docagent_cli/media_utils.py +478 -0
  253. docagent_cli/model_config.py +1620 -0
  254. docagent_cli/non_interactive.py +948 -0
  255. docagent_cli/offload.py +371 -0
  256. docagent_cli/output.py +69 -0
  257. docagent_cli/project_utils.py +188 -0
  258. docagent_cli/py.typed +0 -0
  259. docagent_cli/remote_client.py +515 -0
  260. docagent_cli/server.py +520 -0
  261. docagent_cli/server_graph.py +196 -0
  262. docagent_cli/server_manager.py +365 -0
  263. docagent_cli/sessions.py +1262 -0
  264. docagent_cli/skills/__init__.py +18 -0
  265. docagent_cli/skills/commands.py +1090 -0
  266. docagent_cli/skills/load.py +192 -0
  267. docagent_cli/subagents.py +173 -0
  268. docagent_cli/system_prompt.md +247 -0
  269. docagent_cli/textual_adapter.py +1352 -0
  270. docagent_cli/theme.py +842 -0
  271. docagent_cli/token_state.py +31 -0
  272. docagent_cli/tool_display.py +298 -0
  273. docagent_cli/tools.py +236 -0
  274. docagent_cli/ui.py +420 -0
  275. docagent_cli/unicode_security.py +516 -0
  276. docagent_cli/update_check.py +454 -0
  277. docagent_cli/widgets/__init__.py +9 -0
  278. docagent_cli/widgets/_links.py +63 -0
  279. docagent_cli/widgets/approval.py +442 -0
  280. docagent_cli/widgets/ask_user.py +398 -0
  281. docagent_cli/widgets/autocomplete.py +691 -0
  282. docagent_cli/widgets/chat_input.py +1827 -0
  283. docagent_cli/widgets/diff.py +248 -0
  284. docagent_cli/widgets/history.py +188 -0
  285. docagent_cli/widgets/loading.py +177 -0
  286. docagent_cli/widgets/mcp_viewer.py +362 -0
  287. docagent_cli/widgets/message_store.py +675 -0
  288. docagent_cli/widgets/messages.py +1751 -0
  289. docagent_cli/widgets/model_selector.py +964 -0
  290. docagent_cli/widgets/status.py +372 -0
  291. docagent_cli/widgets/theme_selector.py +164 -0
  292. docagent_cli/widgets/thread_selector.py +1905 -0
  293. docagent_cli/widgets/tool_renderers.py +148 -0
  294. docagent_cli/widgets/tool_widgets.py +274 -0
  295. docagent_cli/widgets/welcome.py +339 -0
  296. docagent_cli-0.0.35.data/data/docagent_cli/default_agent_prompt.md +12 -0
  297. docagent_cli-0.0.35.dist-info/METADATA +200 -0
  298. docagent_cli-0.0.35.dist-info/RECORD +300 -0
  299. docagent_cli-0.0.35.dist-info/WHEEL +4 -0
  300. docagent_cli-0.0.35.dist-info/entry_points.txt +3 -0
docagent_cli/input.py ADDED
@@ -0,0 +1,787 @@
1
+ """Input handling utilities including image/video tracking and file mention parsing."""
2
+
3
+ import logging
4
+ import re
5
+ import shlex
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Literal
9
+ from urllib.parse import unquote, urlparse
10
+
11
+ from rich.markup import escape as escape_markup
12
+
13
+ from docagent_cli.config import console
14
+ from docagent_cli.media_utils import ImageData, VideoData
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ PATH_CHAR_CLASS = r"A-Za-z0-9._~/\\:-"
19
+ """Characters allowed in file paths.
20
+
21
+ Includes alphanumeric, period, underscore, tilde (home), forward/back slashes
22
+ (path separators), colon (Windows drive letters), and hyphen.
23
+ """
24
+
25
+ FILE_MENTION_PATTERN = re.compile(r"@(?P<path>(?:\\.|[" + PATH_CHAR_CLASS + r"])+)")
26
+ """Pattern for extracting `@file` mentions from input text.
27
+
28
+ Matches `@` followed by one or more path characters or escaped character
29
+ pairs (backslash + any character, e.g., `\\ ` for spaces in paths).
30
+
31
+ Uses `+` (not `*`) because a bare `@` without a path is not a valid
32
+ file reference.
33
+ """
34
+
35
+ EMAIL_PREFIX_PATTERN = re.compile(r"[a-zA-Z0-9._%+-]$")
36
+ """Pattern to detect email-like text preceding an `@` symbol.
37
+
38
+ If the character immediately before `@` matches this pattern, the `@mention`
39
+ is likely part of an email address (e.g., `user@example.com`) rather than
40
+ a file reference.
41
+ """
42
+
43
+ INPUT_HIGHLIGHT_PATTERN = re.compile(
44
+ r"(^\/[a-zA-Z0-9_-]+|@(?:\\.|[" + PATH_CHAR_CLASS + r"])+)"
45
+ )
46
+ """Pattern for highlighting `@mentions` and `/commands` in rendered
47
+ user messages.
48
+
49
+ Matches either:
50
+ - Slash commands at the start of the string (e.g., `/help`)
51
+ - `@file` mentions anywhere in the text (e.g., `@README.md`)
52
+
53
+ Note: The `^` anchor matches start of string, not start of line. The consumer
54
+ in `UserMessage.compose()` additionally checks `start == 0` before styling
55
+ slash commands, so a `/` mid-string is not highlighted.
56
+ """
57
+
58
+ MediaKind = Literal["image", "video"]
59
+ """Accepted values for the `kind` parameter in `MediaTracker` methods."""
60
+
61
+ IMAGE_PLACEHOLDER_PATTERN = re.compile(r"\[image (?P<id>\d+)\]")
62
+ """Pattern for image placeholders with a named `id` capture group.
63
+
64
+ Used to extract numeric IDs from placeholder tokens so the tracker can prune
65
+ stale entries and compute the next available ID.
66
+ """
67
+
68
+ VIDEO_PLACEHOLDER_PATTERN = re.compile(r"\[video (?P<id>\d+)\]")
69
+ """Pattern for video placeholders with a named `id` capture group.
70
+
71
+ Used to extract numeric IDs from placeholder tokens so the tracker can prune
72
+ stale entries and compute the next available ID.
73
+ """
74
+
75
+ _UNICODE_SPACE_EQUIVALENTS = str.maketrans(
76
+ {
77
+ "\u00a0": " ", # NO-BREAK SPACE
78
+ "\u202f": " ", # NARROW NO-BREAK SPACE
79
+ }
80
+ )
81
+ """Translation table used to normalize Unicode space variants.
82
+
83
+ Some macOS-generated filenames (for example screenshots) may contain non-ASCII
84
+ space code points that look identical to normal spaces when pasted.
85
+ """
86
+
87
+ _WINDOWS_DRIVE_PATH_PATTERN = re.compile(r"^[A-Za-z]:[\\/]")
88
+ """Pattern for Windows drive-letter paths like `C:\\Users\\...`."""
89
+
90
+
91
+ @dataclass(frozen=True)
92
+ class ParsedPastedPathPayload:
93
+ """Unified parse result for dropped-path payload detection.
94
+
95
+ Attributes:
96
+ paths: Resolved file paths parsed from the input payload.
97
+ token_end: End index (exclusive) of the parsed leading token when the
98
+ payload starts with a path followed by trailing text.
99
+
100
+ `None` means the entire payload was parsed as path-only content.
101
+ """
102
+
103
+ paths: list[Path]
104
+ token_end: int | None = None
105
+
106
+
107
+ class MediaTracker:
108
+ """Track pasted images and videos in the current conversation."""
109
+
110
+ def __init__(self) -> None:
111
+ """Initialize an empty media tracker.
112
+
113
+ Sets up empty lists to store images and videos, and initializes the
114
+ ID counters to 1 for generating unique placeholder identifiers.
115
+ """
116
+ self.images: list[ImageData] = []
117
+ self.videos: list[VideoData] = []
118
+ self.next_image_id: int = 1
119
+ self.next_video_id: int = 1
120
+
121
+ def add_media(self, data: ImageData | VideoData, kind: MediaKind) -> str:
122
+ """Add a media item and return its placeholder text.
123
+
124
+ Args:
125
+ data: The image or video data to track.
126
+ kind: Media type key.
127
+
128
+ Returns:
129
+ Placeholder string like "[image 1]" or "[video 1]".
130
+ """
131
+ if kind == "image":
132
+ placeholder = f"[image {self.next_image_id}]"
133
+ data.placeholder = placeholder
134
+ self.images.append(data) # type: ignore[arg-type]
135
+ self.next_image_id += 1
136
+ else:
137
+ placeholder = f"[video {self.next_video_id}]"
138
+ data.placeholder = placeholder
139
+ self.videos.append(data) # type: ignore[arg-type]
140
+ self.next_video_id += 1
141
+ return placeholder
142
+
143
+ def add_image(self, image_data: ImageData) -> str:
144
+ """Add an image and return its placeholder text.
145
+
146
+ Args:
147
+ image_data: The image data to track.
148
+
149
+ Returns:
150
+ Placeholder string like "[image 1]".
151
+ """
152
+ return self.add_media(image_data, "image")
153
+
154
+ def add_video(self, video_data: VideoData) -> str:
155
+ """Add a video and return its placeholder text.
156
+
157
+ Args:
158
+ video_data: The video data to track.
159
+
160
+ Returns:
161
+ Placeholder string like "[video 1]".
162
+ """
163
+ return self.add_media(video_data, "video")
164
+
165
+ def get_media(self, kind: MediaKind) -> list[ImageData] | list[VideoData]:
166
+ """Get all tracked media of a given type.
167
+
168
+ Args:
169
+ kind: Media type key.
170
+
171
+ Returns:
172
+ Copy of the list of tracked media items.
173
+ """
174
+ if kind == "image":
175
+ return list(self.images)
176
+ return list(self.videos)
177
+
178
+ def get_images(self) -> list[ImageData]:
179
+ """Get all tracked images.
180
+
181
+ Returns:
182
+ Copy of the list of tracked images.
183
+ """
184
+ return list(self.images)
185
+
186
+ def get_videos(self) -> list[VideoData]:
187
+ """Get all tracked videos.
188
+
189
+ Returns:
190
+ Copy of the list of tracked videos.
191
+ """
192
+ return list(self.videos)
193
+
194
+ def clear(self) -> None:
195
+ """Clear all tracked media and reset counters."""
196
+ self.images.clear()
197
+ self.videos.clear()
198
+ self.next_image_id = 1
199
+ self.next_video_id = 1
200
+
201
+ def sync_to_text(self, text: str) -> None:
202
+ """Retain only media still referenced by placeholders in current text.
203
+
204
+ Args:
205
+ text: Current input text shown to the user.
206
+ """
207
+ img_found = self._sync_kind_images(text)
208
+ vid_found = self._sync_kind_videos(text)
209
+ if not img_found and not vid_found:
210
+ self.clear()
211
+
212
+ def _sync_kind_images(self, text: str) -> bool:
213
+ """Sync image list to surviving placeholders in text.
214
+
215
+ Args:
216
+ text: Current input text.
217
+
218
+ Returns:
219
+ Whether any image placeholders were found.
220
+ """
221
+ placeholders = {m.group(0) for m in IMAGE_PLACEHOLDER_PATTERN.finditer(text)}
222
+ self.images = [img for img in self.images if img.placeholder in placeholders]
223
+ if not self.images:
224
+ self.next_image_id = 1
225
+ else:
226
+ self.next_image_id = self._max_placeholder_id(
227
+ self.images, IMAGE_PLACEHOLDER_PATTERN, len(self.images)
228
+ )
229
+ return bool(placeholders)
230
+
231
+ def _sync_kind_videos(self, text: str) -> bool:
232
+ """Sync video list to surviving placeholders in text.
233
+
234
+ Args:
235
+ text: Current input text.
236
+
237
+ Returns:
238
+ Whether any video placeholders were found.
239
+ """
240
+ placeholders = {m.group(0) for m in VIDEO_PLACEHOLDER_PATTERN.finditer(text)}
241
+ self.videos = [vid for vid in self.videos if vid.placeholder in placeholders]
242
+ if not self.videos:
243
+ self.next_video_id = 1
244
+ else:
245
+ self.next_video_id = self._max_placeholder_id(
246
+ self.videos, VIDEO_PLACEHOLDER_PATTERN, len(self.videos)
247
+ )
248
+ return bool(placeholders)
249
+
250
+ @staticmethod
251
+ def _max_placeholder_id(
252
+ items: list[ImageData] | list[VideoData],
253
+ pattern: re.Pattern[str],
254
+ fallback_count: int,
255
+ ) -> int:
256
+ """Compute next ID from the highest surviving placeholder.
257
+
258
+ Args:
259
+ items: Surviving media items.
260
+ pattern: Placeholder regex with an `id` group.
261
+ fallback_count: Fallback when no IDs can be parsed.
262
+
263
+ Returns:
264
+ Next ID value (max_id + 1).
265
+ """
266
+ max_id = 0
267
+ for item in items:
268
+ match = pattern.fullmatch(item.placeholder)
269
+ if match is not None:
270
+ max_id = max(max_id, int(match.group("id")))
271
+ return max_id + 1 if max_id else fallback_count + 1
272
+
273
+
274
+ def parse_file_mentions(text: str) -> tuple[str, list[Path]]:
275
+ r"""Extract `@file` mentions and return the text with resolved file paths.
276
+
277
+ Parses `@file` mentions from the input text and resolves them to absolute
278
+ file paths. Files that do not exist or cannot be resolved are excluded with
279
+ a warning printed to the console.
280
+
281
+ Email addresses (e.g., `user@example.com`) are automatically excluded by
282
+ detecting email-like characters before the `@` symbol.
283
+
284
+ Backslash-escaped spaces in paths (e.g., `@my\ folder/file.txt`) are
285
+ unescaped before resolution. Tilde paths (e.g., `@~/file.txt`) are expanded
286
+ via `Path.expanduser()`. Only regular files are returned; directories are
287
+ excluded.
288
+
289
+ This function does not raise exceptions; invalid paths are handled
290
+ internally with a console warning.
291
+
292
+ Args:
293
+ text: Input text potentially containing `@file` mentions.
294
+
295
+ Returns:
296
+ Tuple of (original text unchanged, list of resolved file paths that exist).
297
+ """
298
+ matches = FILE_MENTION_PATTERN.finditer(text)
299
+
300
+ files = []
301
+ for match in matches:
302
+ # Skip if this looks like an email address
303
+ text_before = text[: match.start()]
304
+ if text_before and EMAIL_PREFIX_PATTERN.search(text_before):
305
+ continue
306
+
307
+ raw_path = match.group("path")
308
+ clean_path = raw_path.replace("\\ ", " ")
309
+
310
+ try:
311
+ path = Path(clean_path).expanduser()
312
+
313
+ if not path.is_absolute():
314
+ path = Path.cwd() / path
315
+
316
+ resolved = path.resolve()
317
+ if resolved.exists() and resolved.is_file():
318
+ files.append(resolved)
319
+ else:
320
+ console.print(
321
+ f"[yellow]Warning: File not found: "
322
+ f"{escape_markup(raw_path)}[/yellow]"
323
+ )
324
+ except (OSError, RuntimeError) as e:
325
+ console.print(
326
+ f"[yellow]Warning: Invalid path "
327
+ f"{escape_markup(raw_path)}: "
328
+ f"{escape_markup(str(e))}[/yellow]"
329
+ )
330
+
331
+ return text, files
332
+
333
+
334
+ def parse_pasted_file_paths(text: str) -> list[Path]:
335
+ r"""Parse a paste payload that may contain dragged-and-dropped file paths.
336
+
337
+ The parser is strict on purpose: it only returns paths when the entire paste
338
+ payload can be interpreted as one or more existing files. Any invalid token
339
+ falls back to normal text paste behavior by returning an empty list.
340
+
341
+ Supports common dropped-path formats:
342
+
343
+ - Absolute/relative paths
344
+ - POSIX shell quoting and escaping
345
+ - `file://` URLs
346
+
347
+ Args:
348
+ text: Raw paste payload from the terminal.
349
+
350
+ Returns:
351
+ List of resolved file paths, or an empty list when parsing fails.
352
+ """
353
+ payload = text.strip()
354
+ if not payload:
355
+ return []
356
+
357
+ tokens: list[str] = []
358
+ for raw_line in payload.splitlines():
359
+ line = raw_line.strip()
360
+ if not line:
361
+ continue
362
+ line_tokens = _split_paste_line(line)
363
+ if not line_tokens:
364
+ return []
365
+ tokens.extend(line_tokens)
366
+
367
+ if not tokens:
368
+ return []
369
+
370
+ paths: list[Path] = []
371
+ for token in tokens:
372
+ path = _token_to_path(token)
373
+ if path is None:
374
+ return []
375
+ resolved = _resolve_existing_pasted_path(path)
376
+ if resolved is None:
377
+ return []
378
+ paths.append(resolved)
379
+
380
+ return paths
381
+
382
+
383
+ def parse_pasted_path_payload(
384
+ text: str, *, allow_leading_path: bool = False
385
+ ) -> ParsedPastedPathPayload | None:
386
+ """Parse dropped-path payload variants through one entrypoint.
387
+
388
+ Parsing order is:
389
+ 1. strict multi-path payload parsing (`parse_pasted_file_paths`)
390
+ 2. single-path normalization/parsing (`parse_single_pasted_file_path`)
391
+ 3. optional leading-path extraction (`extract_leading_pasted_file_path`)
392
+
393
+ Args:
394
+ text: Input payload to parse.
395
+ allow_leading_path: Whether to parse a leading path token followed by
396
+ trailing prompt text.
397
+
398
+ Returns:
399
+ Parsed payload details, otherwise `None`.
400
+ """
401
+ paths = parse_pasted_file_paths(text)
402
+ if paths:
403
+ return ParsedPastedPathPayload(paths=paths)
404
+
405
+ single_path = parse_single_pasted_file_path(text)
406
+ if single_path is not None:
407
+ return ParsedPastedPathPayload(paths=[single_path])
408
+
409
+ if not allow_leading_path:
410
+ return None
411
+
412
+ leading = extract_leading_pasted_file_path(text)
413
+ if leading is None:
414
+ return None
415
+
416
+ path, token_end = leading
417
+ return ParsedPastedPathPayload(paths=[path], token_end=token_end)
418
+
419
+
420
+ def parse_single_pasted_file_path(text: str) -> Path | None:
421
+ """Parse and resolve a single pasted path payload.
422
+
423
+ Unlike `parse_pasted_file_paths`, this helper only accepts one path token
424
+ and is intended for fallback handling when a paste event carries a
425
+ single path representation.
426
+
427
+ Args:
428
+ text: Raw pasted text payload.
429
+
430
+ Returns:
431
+ Resolved path when payload is a single existing file, otherwise `None`.
432
+ """
433
+ candidate = normalize_pasted_path(text)
434
+ if candidate is None:
435
+ return None
436
+ return _resolve_existing_pasted_path(candidate)
437
+
438
+
439
+ def extract_leading_pasted_file_path(text: str) -> tuple[Path, int] | None:
440
+ """Extract and resolve a leading pasted path token from input text.
441
+
442
+ This is used for submit-time recovery when a user message starts with a
443
+ path token followed by additional prompt text.
444
+
445
+ Args:
446
+ text: Input text to inspect.
447
+
448
+ Returns:
449
+ Tuple of `(resolved_path, token_end_index)` or `None` when no valid
450
+ leading file path token exists.
451
+ """
452
+ if not text:
453
+ return None
454
+
455
+ start = len(text) - len(text.lstrip())
456
+ payload = text[start:]
457
+ token_end = _leading_token_end(payload)
458
+ if token_end is None:
459
+ return None
460
+
461
+ token_text = payload[:token_end]
462
+ path = parse_single_pasted_file_path(token_text)
463
+ if path is None:
464
+ spaced = _extract_unquoted_leading_path_with_spaces(payload)
465
+ if spaced is None:
466
+ return None
467
+ spaced_path, spaced_end = spaced
468
+ return spaced_path, start + spaced_end
469
+
470
+ return path, start + token_end
471
+
472
+
473
+ def normalize_pasted_path(text: str) -> Path | None:
474
+ """Normalize pasted text that may represent a single filesystem path.
475
+
476
+ Supports:
477
+
478
+ - quoted and shell-escaped single paths
479
+ - `file://` URLs
480
+ - Windows drive-letter and UNC paths
481
+
482
+ Args:
483
+ text: Raw pasted text payload.
484
+
485
+ Returns:
486
+ Parsed `Path` if payload is a single path token, otherwise `None`.
487
+ """
488
+ payload = text.strip()
489
+ if not payload:
490
+ return None
491
+
492
+ unquoted = (
493
+ payload.removeprefix('"').removesuffix('"')
494
+ if payload.startswith('"') and payload.endswith('"')
495
+ else payload
496
+ )
497
+ unquoted = (
498
+ unquoted.removeprefix("'").removesuffix("'")
499
+ if unquoted.startswith("'") and unquoted.endswith("'")
500
+ else unquoted
501
+ )
502
+
503
+ if unquoted.startswith("file://"):
504
+ return _token_to_path(unquoted)
505
+
506
+ windows_path = _normalize_windows_pasted_path(unquoted)
507
+ if windows_path is not None:
508
+ return windows_path
509
+
510
+ posix_path = _normalize_posix_pasted_path(unquoted)
511
+ if posix_path is not None:
512
+ return posix_path
513
+
514
+ parts = _split_paste_line(payload)
515
+ if len(parts) != 1:
516
+ return None
517
+ token = parts[0]
518
+ path = _token_to_path(token)
519
+ if path is None:
520
+ return None
521
+ windows_token_path = _normalize_windows_pasted_path(str(path))
522
+ if windows_token_path is not None:
523
+ return windows_token_path
524
+ return path
525
+
526
+
527
+ def _split_paste_line(line: str) -> list[str]:
528
+ """Split a single pasted line into path-like tokens.
529
+
530
+ Args:
531
+ line: A single line from the paste payload.
532
+
533
+ Returns:
534
+ Parsed shell-like tokens, or an empty list when parsing fails.
535
+ """
536
+ try:
537
+ return shlex.split(line, posix=True)
538
+ except ValueError:
539
+ # Unbalanced quotes or other tokenization errors: treat as plain text.
540
+ return []
541
+
542
+
543
+ def _token_to_path(token: str) -> Path | None:
544
+ """Convert a pasted token into a path candidate.
545
+
546
+ Args:
547
+ token: A single shell-split token from the paste payload.
548
+
549
+ Returns:
550
+ A parsed path candidate, or `None` when token parsing fails.
551
+ """
552
+ value = token.strip()
553
+ if not value:
554
+ return None
555
+
556
+ if value.startswith("<") and value.endswith(">"):
557
+ value = value[1:-1].strip()
558
+ if not value:
559
+ return None
560
+
561
+ if value.startswith("file://"):
562
+ parsed = urlparse(value)
563
+ path_text = unquote(parsed.path or "")
564
+ if parsed.netloc and parsed.netloc != "localhost":
565
+ path_text = f"//{parsed.netloc}{path_text}"
566
+ if (
567
+ path_text.startswith("/")
568
+ and len(path_text) > 2 # noqa: PLR2004 # '/C:' minimum for Windows file URI
569
+ and path_text[2] == ":"
570
+ and path_text[1].isalpha()
571
+ ):
572
+ # `file:///C:/...` on Windows includes an extra leading slash.
573
+ path_text = path_text[1:]
574
+ if not path_text:
575
+ return None
576
+ return Path(path_text)
577
+
578
+ return Path(value)
579
+
580
+
581
+ def _leading_token_end(text: str) -> int | None:
582
+ """Return the end index of the first shell-like token.
583
+
584
+ Args:
585
+ text: Input text beginning with a token.
586
+
587
+ Returns:
588
+ End index (exclusive), or `None` when token parsing fails.
589
+ """
590
+ if not text:
591
+ return None
592
+
593
+ if text[0] in {'"', "'"}:
594
+ quote = text[0]
595
+ escaped = False
596
+ for index in range(1, len(text)):
597
+ char = text[index]
598
+ if char == "\\" and not escaped:
599
+ escaped = True
600
+ continue
601
+ if char == quote and not escaped:
602
+ return index + 1
603
+ escaped = False
604
+ return None
605
+
606
+ escaped = False
607
+ for index, char in enumerate(text):
608
+ if char == "\\" and not escaped:
609
+ escaped = True
610
+ continue
611
+ if char.isspace() and not escaped:
612
+ return index
613
+ escaped = False
614
+ return len(text)
615
+
616
+
617
+ def _extract_unquoted_leading_path_with_spaces(text: str) -> tuple[Path, int] | None:
618
+ """Extract a leading unquoted path that may contain spaces.
619
+
620
+ This fallback is intentionally POSIX-oriented (`/` and `~/`) because the
621
+ slash-command conflict it addresses is specific to inputs that begin with
622
+ `/`.
623
+
624
+ Args:
625
+ text: Input text beginning with a potential path.
626
+
627
+ Returns:
628
+ Tuple of `(resolved_path, token_end_index)` or `None` when no matching
629
+ leading path prefix resolves to an existing file.
630
+ """
631
+ if not text or ("\n" in text or "\r" in text):
632
+ return None
633
+ if not text.startswith(("/", "~/")):
634
+ return None
635
+ if " " not in text and "\u00a0" not in text and "\u202f" not in text:
636
+ return None
637
+
638
+ boundaries = [index for index, char in enumerate(text) if char.isspace()]
639
+ boundaries.append(len(text))
640
+ for end in reversed(boundaries):
641
+ candidate = text[:end].rstrip()
642
+ if not candidate:
643
+ continue
644
+ path = parse_single_pasted_file_path(candidate)
645
+ if path is not None:
646
+ return path, len(candidate)
647
+ return None
648
+
649
+
650
+ def _normalize_windows_pasted_path(text: str) -> Path | None:
651
+ """Return a `Path` for unquoted Windows drive/UNC path inputs.
652
+
653
+ Args:
654
+ text: Potential Windows path input.
655
+
656
+ Returns:
657
+ Parsed `Path` when `text` is Windows drive-letter or UNC style,
658
+ otherwise `None`.
659
+ """
660
+ if _WINDOWS_DRIVE_PATH_PATTERN.match(text) or text.startswith("\\\\"):
661
+ return Path(text)
662
+ return None
663
+
664
+
665
+ def _normalize_posix_pasted_path(text: str) -> Path | None:
666
+ """Return a `Path` for likely POSIX absolute/home path payloads.
667
+
668
+ Some terminals paste dropped absolute paths with spaces as raw text without
669
+ quoting/escaping. In that case shell tokenization splits on spaces even
670
+ though the full payload is intended to be a single path.
671
+
672
+ Args:
673
+ text: Potential POSIX path input.
674
+
675
+ Returns:
676
+ Parsed `Path` when `text` looks like a raw POSIX absolute/home path,
677
+ otherwise `None`.
678
+ """
679
+ if "\n" in text or "\r" in text:
680
+ return None
681
+ if text.startswith("~/"):
682
+ return Path(text)
683
+ if text.startswith("/") and "/" in text[1:]:
684
+ return Path(text)
685
+ return None
686
+
687
+
688
+ def _resolve_existing_pasted_path(path: Path) -> Path | None:
689
+ """Resolve a pasted path candidate to an existing file.
690
+
691
+ Performs an exact resolution first, then a Unicode-space-tolerant lookup.
692
+
693
+ Args:
694
+ path: Parsed path candidate.
695
+
696
+ Returns:
697
+ Resolved existing file path, otherwise `None`.
698
+ """
699
+ try:
700
+ resolved = path.expanduser().resolve()
701
+ except (OSError, RuntimeError) as e:
702
+ logger.debug("Path resolution failed for %r: %s", path, e)
703
+ return None
704
+ if resolved.exists() and resolved.is_file():
705
+ return resolved
706
+
707
+ fuzzy = _resolve_with_unicode_space_variants(path)
708
+ if fuzzy is None:
709
+ return None
710
+ try:
711
+ resolved_fuzzy = fuzzy.resolve()
712
+ except (OSError, RuntimeError) as e:
713
+ logger.debug("Unicode-space resolution failed for %r: %s", fuzzy, e)
714
+ return None
715
+ if resolved_fuzzy.exists() and resolved_fuzzy.is_file():
716
+ return resolved_fuzzy
717
+ return None
718
+
719
+
720
+ def _normalize_unicode_spaces(text: str) -> str:
721
+ """Normalize Unicode lookalike spaces to ASCII spaces.
722
+
723
+ Args:
724
+ text: Text to normalize.
725
+
726
+ Returns:
727
+ Normalized text with Unicode-space variants converted to ASCII spaces.
728
+ """
729
+ return text.translate(_UNICODE_SPACE_EQUIVALENTS)
730
+
731
+
732
+ def _resolve_with_unicode_space_variants(path: Path) -> Path | None:
733
+ """Resolve path by matching filename segments with Unicode space variants.
734
+
735
+ Args:
736
+ path: Path candidate that may differ from disk by space code points.
737
+
738
+ Returns:
739
+ Matching filesystem path, or `None` when no variant match exists.
740
+ """
741
+ expanded = path.expanduser()
742
+ if expanded.is_absolute():
743
+ current = Path(expanded.anchor)
744
+ parts = expanded.parts[1:]
745
+ else:
746
+ current = Path.cwd()
747
+ parts = expanded.parts
748
+
749
+ for index, part in enumerate(parts):
750
+ candidate = current / part
751
+ if candidate.exists():
752
+ current = candidate
753
+ continue
754
+
755
+ if not current.exists() or not current.is_dir():
756
+ return None
757
+ if " " not in part and "\u00a0" not in part and "\u202f" not in part:
758
+ return None
759
+
760
+ normalized_part = _normalize_unicode_spaces(part)
761
+ try:
762
+ matches = [
763
+ entry
764
+ for entry in current.iterdir()
765
+ if _normalize_unicode_spaces(entry.name) == normalized_part
766
+ ]
767
+ except OSError as e:
768
+ logger.debug("Failed listing %s for Unicode-space lookup: %s", current, e)
769
+ return None
770
+
771
+ if not matches:
772
+ return None
773
+
774
+ is_last = index == len(parts) - 1
775
+ if is_last:
776
+ file_matches = [entry for entry in matches if entry.is_file()]
777
+ if file_matches:
778
+ matches = file_matches
779
+ else:
780
+ dir_matches = [entry for entry in matches if entry.is_dir()]
781
+ if dir_matches:
782
+ matches = dir_matches
783
+
784
+ matches.sort(key=lambda entry: entry.name)
785
+ current = matches[0]
786
+
787
+ return current