clawed 2.5.1__tar.gz → 2.5.2__tar.gz

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 (257) hide show
  1. {clawed-2.5.1 → clawed-2.5.2}/PKG-INFO +1 -1
  2. {clawed-2.5.1 → clawed-2.5.2}/clawed/__init__.py +1 -1
  3. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/generate.py +6 -2
  4. {clawed-2.5.1 → clawed-2.5.2}/clawed/compile_game.py +197 -19
  5. {clawed-2.5.1 → clawed-2.5.2}/clawed/export_docx.py +17 -3
  6. {clawed-2.5.1 → clawed-2.5.2}/clawed/export_pptx.py +369 -118
  7. {clawed-2.5.1 → clawed-2.5.2}/clawed/model_router.py +12 -13
  8. {clawed-2.5.1 → clawed-2.5.2}/clawed/multi_agent.py +2 -2
  9. {clawed-2.5.1 → clawed-2.5.2}/clawed/slide_images.py +285 -52
  10. {clawed-2.5.1 → clawed-2.5.2}/pyproject.toml +1 -1
  11. {clawed-2.5.1 → clawed-2.5.2}/.gitignore +0 -0
  12. {clawed-2.5.1 → clawed-2.5.2}/LICENSE +0 -0
  13. {clawed-2.5.1 → clawed-2.5.2}/README.md +0 -0
  14. {clawed-2.5.1 → clawed-2.5.2}/clawed/__main__.py +0 -0
  15. {clawed-2.5.1 → clawed-2.5.2}/clawed/_legacy_gateway.py +0 -0
  16. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent.py +0 -0
  17. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/__init__.py +0 -0
  18. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/approvals.py +0 -0
  19. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/autonomy.py +0 -0
  20. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/context.py +0 -0
  21. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/core.py +0 -0
  22. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/custom_tools.py +0 -0
  23. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/drive/__init__.py +0 -0
  24. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/drive/auth.py +0 -0
  25. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/drive/client.py +0 -0
  26. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/fake_llm.py +0 -0
  27. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/loop.py +0 -0
  28. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/__init__.py +0 -0
  29. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/curriculum.py +0 -0
  30. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/curriculum_kb.py +0 -0
  31. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/embeddings.py +0 -0
  32. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/episodes.py +0 -0
  33. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/identity.py +0 -0
  34. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/loader.py +0 -0
  35. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/memory/preferences.py +0 -0
  36. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/planner.py +0 -0
  37. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/prompt.py +0 -0
  38. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/scheduler.py +0 -0
  39. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/__init__.py +0 -0
  40. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/base.py +0 -0
  41. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/configure_profile.py +0 -0
  42. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/curriculum_map.py +0 -0
  43. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_create_doc.py +0 -0
  44. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_create_slides.py +0 -0
  45. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_list.py +0 -0
  46. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_organize.py +0 -0
  47. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_read.py +0 -0
  48. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/drive_upload.py +0 -0
  49. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/export_document.py +0 -0
  50. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/gap_analysis.py +0 -0
  51. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/generate_assessment.py +0 -0
  52. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/generate_lesson.py +0 -0
  53. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/generate_lesson_bundle.py +0 -0
  54. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/generate_materials.py +0 -0
  55. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/generate_unit.py +0 -0
  56. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/ingest_materials.py +0 -0
  57. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/parent_comm.py +0 -0
  58. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/read_heartbeat.py +0 -0
  59. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/read_workspace.py +0 -0
  60. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/request_approval.py +0 -0
  61. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/schedule_task.py +0 -0
  62. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/search_lessons.py +0 -0
  63. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/search_my_materials.py +0 -0
  64. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/search_standards.py +0 -0
  65. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/student_insights.py +0 -0
  66. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/sub_packet.py +0 -0
  67. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/switch_model.py +0 -0
  68. {clawed-2.5.1 → clawed-2.5.2}/clawed/agent_core/tools/update_soul.py +0 -0
  69. {clawed-2.5.1 → clawed-2.5.2}/clawed/analytics.py +0 -0
  70. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/__init__.py +0 -0
  71. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/deps.py +0 -0
  72. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/__init__.py +0 -0
  73. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/chat.py +0 -0
  74. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/export.py +0 -0
  75. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/feedback.py +0 -0
  76. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/gateway_chat.py +0 -0
  77. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/generate.py +0 -0
  78. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/ingest.py +0 -0
  79. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/lessons.py +0 -0
  80. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/school.py +0 -0
  81. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/settings.py +0 -0
  82. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/routes/tools.py +0 -0
  83. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/server.py +0 -0
  84. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/static/app.js +0 -0
  85. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/static/style.css +0 -0
  86. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/static/widget.js +0 -0
  87. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/analytics.html +0 -0
  88. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/base.html +0 -0
  89. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/dashboard.html +0 -0
  90. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/generate.html +0 -0
  91. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/index.html +0 -0
  92. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/lesson.html +0 -0
  93. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/profile.html +0 -0
  94. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/settings.html +0 -0
  95. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/stats.html +0 -0
  96. {clawed-2.5.1 → clawed-2.5.2}/clawed/api/templates/students.html +0 -0
  97. {clawed-2.5.1 → clawed-2.5.2}/clawed/assessment.py +0 -0
  98. {clawed-2.5.1 → clawed-2.5.2}/clawed/asset_registry.py +0 -0
  99. {clawed-2.5.1 → clawed-2.5.2}/clawed/async_utils.py +0 -0
  100. {clawed-2.5.1 → clawed-2.5.2}/clawed/auth/__init__.py +0 -0
  101. {clawed-2.5.1 → clawed-2.5.2}/clawed/auth/google_auth.py +0 -0
  102. {clawed-2.5.1 → clawed-2.5.2}/clawed/bot_state.py +0 -0
  103. {clawed-2.5.1 → clawed-2.5.2}/clawed/chat.py +0 -0
  104. {clawed-2.5.1 → clawed-2.5.2}/clawed/cli.py +0 -0
  105. {clawed-2.5.1 → clawed-2.5.2}/clawed/cli_chat.py +0 -0
  106. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/__init__.py +0 -0
  107. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/_helpers.py +0 -0
  108. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/bot.py +0 -0
  109. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/config.py +0 -0
  110. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/config_llm.py +0 -0
  111. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/config_profile.py +0 -0
  112. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/export.py +0 -0
  113. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/game.py +0 -0
  114. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/generate_assessment.py +0 -0
  115. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/generate_unit.py +0 -0
  116. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/queue.py +0 -0
  117. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/schedule_cmd.py +0 -0
  118. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/sub.py +0 -0
  119. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/train.py +0 -0
  120. {clawed-2.5.1 → clawed-2.5.2}/clawed/commands/workspace_cmd.py +0 -0
  121. {clawed-2.5.1 → clawed-2.5.2}/clawed/compile_slides.py +0 -0
  122. {clawed-2.5.1 → clawed-2.5.2}/clawed/compile_student.py +0 -0
  123. {clawed-2.5.1 → clawed-2.5.2}/clawed/compile_teacher.py +0 -0
  124. {clawed-2.5.1 → clawed-2.5.2}/clawed/config.py +0 -0
  125. {clawed-2.5.1 → clawed-2.5.2}/clawed/corpus.py +0 -0
  126. {clawed-2.5.1 → clawed-2.5.2}/clawed/curriculum_map.py +0 -0
  127. {clawed-2.5.1 → clawed-2.5.2}/clawed/database.py +0 -0
  128. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/__init__.py +0 -0
  129. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_assessment.json +0 -0
  130. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_formative_assessment.json +0 -0
  131. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_lesson_materials.json +0 -0
  132. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_lesson_science_g6.json +0 -0
  133. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_lesson_social_studies_g8.json +0 -0
  134. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_master_content.json +0 -0
  135. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_pacing_guide.json +0 -0
  136. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_quiz.json +0 -0
  137. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_rubric.json +0 -0
  138. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_unit_plan.json +0 -0
  139. {clawed-2.5.1 → clawed-2.5.2}/clawed/demo/demo_year_map.json +0 -0
  140. {clawed-2.5.1 → clawed-2.5.2}/clawed/differentiation.py +0 -0
  141. {clawed-2.5.1 → clawed-2.5.2}/clawed/doc_export.py +0 -0
  142. {clawed-2.5.1 → clawed-2.5.2}/clawed/drive.py +0 -0
  143. {clawed-2.5.1 → clawed-2.5.2}/clawed/evaluation.py +0 -0
  144. {clawed-2.5.1 → clawed-2.5.2}/clawed/export_handout.py +0 -0
  145. {clawed-2.5.1 → clawed-2.5.2}/clawed/export_markdown.py +0 -0
  146. {clawed-2.5.1 → clawed-2.5.2}/clawed/export_pdf.py +0 -0
  147. {clawed-2.5.1 → clawed-2.5.2}/clawed/export_templates.py +0 -0
  148. {clawed-2.5.1 → clawed-2.5.2}/clawed/export_theme.py +0 -0
  149. {clawed-2.5.1 → clawed-2.5.2}/clawed/exporter.py +0 -0
  150. {clawed-2.5.1 → clawed-2.5.2}/clawed/failure_codes.py +0 -0
  151. {clawed-2.5.1 → clawed-2.5.2}/clawed/feedback.py +0 -0
  152. {clawed-2.5.1 → clawed-2.5.2}/clawed/formats/__init__.py +0 -0
  153. {clawed-2.5.1 → clawed-2.5.2}/clawed/formats/flipchart.py +0 -0
  154. {clawed-2.5.1 → clawed-2.5.2}/clawed/formats/notebook.py +0 -0
  155. {clawed-2.5.1 → clawed-2.5.2}/clawed/formats/xbk.py +0 -0
  156. {clawed-2.5.1 → clawed-2.5.2}/clawed/gateway.py +0 -0
  157. {clawed-2.5.1 → clawed-2.5.2}/clawed/gateway_response.py +0 -0
  158. {clawed-2.5.1 → clawed-2.5.2}/clawed/generation.py +0 -0
  159. {clawed-2.5.1 → clawed-2.5.2}/clawed/generation_report.py +0 -0
  160. {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/__init__.py +0 -0
  161. {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/export.py +0 -0
  162. {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/feedback.py +0 -0
  163. {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/gaps.py +0 -0
  164. {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/generate.py +0 -0
  165. {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/ingest.py +0 -0
  166. {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/misc.py +0 -0
  167. {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/onboard.py +0 -0
  168. {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/schedule.py +0 -0
  169. {clawed-2.5.1 → clawed-2.5.2}/clawed/handlers/standards.py +0 -0
  170. {clawed-2.5.1 → clawed-2.5.2}/clawed/hermes_plugin.py +0 -0
  171. {clawed-2.5.1 → clawed-2.5.2}/clawed/image_pipeline.py +0 -0
  172. {clawed-2.5.1 → clawed-2.5.2}/clawed/improver.py +0 -0
  173. {clawed-2.5.1 → clawed-2.5.2}/clawed/ingestor.py +0 -0
  174. {clawed-2.5.1 → clawed-2.5.2}/clawed/io.py +0 -0
  175. {clawed-2.5.1 → clawed-2.5.2}/clawed/lesson.py +0 -0
  176. {clawed-2.5.1 → clawed-2.5.2}/clawed/llm.py +0 -0
  177. {clawed-2.5.1 → clawed-2.5.2}/clawed/master_content.py +0 -0
  178. {clawed-2.5.1 → clawed-2.5.2}/clawed/materials.py +0 -0
  179. {clawed-2.5.1 → clawed-2.5.2}/clawed/mcp_server.py +0 -0
  180. {clawed-2.5.1 → clawed-2.5.2}/clawed/memory_engine.py +0 -0
  181. {clawed-2.5.1 → clawed-2.5.2}/clawed/models.py +0 -0
  182. {clawed-2.5.1 → clawed-2.5.2}/clawed/onboarding.py +0 -0
  183. {clawed-2.5.1 → clawed-2.5.2}/clawed/openclaw_plugin.py +0 -0
  184. {clawed-2.5.1 → clawed-2.5.2}/clawed/parent_comm.py +0 -0
  185. {clawed-2.5.1 → clawed-2.5.2}/clawed/persona.py +0 -0
  186. {clawed-2.5.1 → clawed-2.5.2}/clawed/persona_evolution.py +0 -0
  187. {clawed-2.5.1 → clawed-2.5.2}/clawed/planner.py +0 -0
  188. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/504_accommodations.txt +0 -0
  189. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/admin_lesson_plan.txt +0 -0
  190. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/assessment.txt +0 -0
  191. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/curriculum_gaps.txt +0 -0
  192. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/dbq_assessment.txt +0 -0
  193. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/differentiation.txt +0 -0
  194. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/formative_assessment.txt +0 -0
  195. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/iep_modification.txt +0 -0
  196. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/lesson_plan.txt +0 -0
  197. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/master_content.txt +0 -0
  198. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/multi_agent_researcher.txt +0 -0
  199. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/multi_agent_reviewer.txt +0 -0
  200. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/pacing_guide.txt +0 -0
  201. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/parent_note.txt +0 -0
  202. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/persona_extract.txt +0 -0
  203. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/quiz.txt +0 -0
  204. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/rubric.txt +0 -0
  205. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/student_packet.txt +0 -0
  206. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/sub_packet.txt +0 -0
  207. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/summative_assessment.txt +0 -0
  208. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/tiered_assignments.txt +0 -0
  209. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/unit_plan.txt +0 -0
  210. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/worksheet.txt +0 -0
  211. {clawed-2.5.1 → clawed-2.5.2}/clawed/prompts/year_map.txt +0 -0
  212. {clawed-2.5.1 → clawed-2.5.2}/clawed/quality.py +0 -0
  213. {clawed-2.5.1 → clawed-2.5.2}/clawed/reading_report.py +0 -0
  214. {clawed-2.5.1 → clawed-2.5.2}/clawed/router.py +0 -0
  215. {clawed-2.5.1 → clawed-2.5.2}/clawed/sanitize.py +0 -0
  216. {clawed-2.5.1 → clawed-2.5.2}/clawed/scheduler.py +0 -0
  217. {clawed-2.5.1 → clawed-2.5.2}/clawed/school.py +0 -0
  218. {clawed-2.5.1 → clawed-2.5.2}/clawed/search.py +0 -0
  219. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/__init__.py +0 -0
  220. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/art.py +0 -0
  221. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/base.py +0 -0
  222. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/computer_science.py +0 -0
  223. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/ela.py +0 -0
  224. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/foreign_language.py +0 -0
  225. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/history.py +0 -0
  226. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/library.py +0 -0
  227. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/math.py +0 -0
  228. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/music.py +0 -0
  229. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/physical_education.py +0 -0
  230. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/science.py +0 -0
  231. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/social_studies.py +0 -0
  232. {clawed-2.5.1 → clawed-2.5.2}/clawed/skills/special_education.py +0 -0
  233. {clawed-2.5.1 → clawed-2.5.2}/clawed/standards.py +0 -0
  234. {clawed-2.5.1 → clawed-2.5.2}/clawed/state.py +0 -0
  235. {clawed-2.5.1 → clawed-2.5.2}/clawed/state_standards.py +0 -0
  236. {clawed-2.5.1 → clawed-2.5.2}/clawed/student_bot.py +0 -0
  237. {clawed-2.5.1 → clawed-2.5.2}/clawed/student_cli.py +0 -0
  238. {clawed-2.5.1 → clawed-2.5.2}/clawed/student_telegram_bot.py +0 -0
  239. {clawed-2.5.1 → clawed-2.5.2}/clawed/sub_packet.py +0 -0
  240. {clawed-2.5.1 → clawed-2.5.2}/clawed/task_queue.py +0 -0
  241. {clawed-2.5.1 → clawed-2.5.2}/clawed/templates_lib.py +0 -0
  242. {clawed-2.5.1 → clawed-2.5.2}/clawed/tools.py +0 -0
  243. {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/__init__.py +0 -0
  244. {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/cli.py +0 -0
  245. {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/hermes.py +0 -0
  246. {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/openclaw.py +0 -0
  247. {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/student_telegram.py +0 -0
  248. {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/telegram.py +0 -0
  249. {clawed-2.5.1 → clawed-2.5.2}/clawed/transports/web.py +0 -0
  250. {clawed-2.5.1 → clawed-2.5.2}/clawed/tui.py +0 -0
  251. {clawed-2.5.1 → clawed-2.5.2}/clawed/tui_chat.py +0 -0
  252. {clawed-2.5.1 → clawed-2.5.2}/clawed/validation.py +0 -0
  253. {clawed-2.5.1 → clawed-2.5.2}/clawed/voice.py +0 -0
  254. {clawed-2.5.1 → clawed-2.5.2}/clawed/voice_check.py +0 -0
  255. {clawed-2.5.1 → clawed-2.5.2}/clawed/workspace.py +0 -0
  256. {clawed-2.5.1 → clawed-2.5.2}/eduagent/__init__.py +0 -0
  257. {clawed-2.5.1 → clawed-2.5.2}/eduagent/_compat.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clawed
3
- Version: 2.5.1
3
+ Version: 2.5.2
4
4
  Summary: Free AI lesson planner for teachers. Learns your teaching voice from your curriculum files and generates complete lesson packages. Open source, runs locally, no subscription.
5
5
  Project-URL: Homepage, https://sirhanmacx.github.io/Claw-ED
6
6
  Project-URL: Documentation, https://github.com/SirhanMacx/Claw-ED#readme
@@ -17,7 +17,7 @@ if hasattr(sys.stderr, "reconfigure"):
17
17
  except Exception:
18
18
  pass
19
19
 
20
- __version__ = "2.5.1"
20
+ __version__ = "2.5.2"
21
21
  __author__ = "Jon Maccarello & Claw-ED contributors"
22
22
  __description__ = "Personal AI teaching agent. Learns your voice, works while you sleep."
23
23
 
@@ -306,7 +306,10 @@ def lesson(
306
306
  False, "--game/--no-game",
307
307
  help="Also generate an interactive HTML learning game",
308
308
  ),
309
- fmt: str = typer.Option("markdown", "--format", "-f", help="Export format: markdown, pptx, docx, pdf, handout"),
309
+ fmt: str = typer.Option(
310
+ "handout", "--format", "-f",
311
+ help="Export: handout, docx, pptx, pdf, markdown",
312
+ ),
310
313
  ):
311
314
  """Generate a detailed daily lesson plan.
312
315
 
@@ -408,13 +411,14 @@ def lesson(
408
411
  from clawed.compile_teacher import compile_teacher_view
409
412
  from clawed.multi_agent import multi_agent_generate_master_content
410
413
  console.print("[dim]Using multi-agent pipeline (researcher→writer→reviewer)...[/dim]")
414
+ _ma_config = AppConfig.load()
411
415
  master = _run_async(
412
416
  multi_agent_generate_master_content(
413
417
  topic=topic,
414
418
  grade=grade,
415
419
  subject=subject,
416
420
  persona=persona,
417
- config=None,
421
+ config=_ma_config,
418
422
  unit_context=kb_prompt_section,
419
423
  )
420
424
  )
@@ -20,6 +20,7 @@ from __future__ import annotations
20
20
  import logging
21
21
  import re
22
22
  from pathlib import Path
23
+ from typing import Any
23
24
 
24
25
  from clawed.io import safe_filename
25
26
  from clawed.llm import LLMClient
@@ -36,6 +37,11 @@ mechanic, different visual style, different interaction pattern.
36
37
 
37
38
  RULES:
38
39
  - Output ONLY the complete HTML file. No explanation, no markdown fencing.
40
+ - The file MUST start with <!DOCTYPE html> then <html>, then <head>, then <body>. \
41
+ Always use this exact structure — never put CSS or text directly inside <html> \
42
+ without a <head> or <body> wrapper.
43
+ - The <head> MUST contain: <meta charset="UTF-8">, a <title> tag, and a <style> block.
44
+ - The <body> contains all visible elements and <script> tags.
39
45
  - The file must be self-contained: all CSS and JS inline.
40
46
  - You may use a Three.js CDN link for 3D: \
41
47
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
@@ -59,12 +65,15 @@ WHAT MAKES A GREAT LEARNING GAME:
59
65
 
60
66
 
61
67
  def _extract_game_content(master: MasterContent) -> str:
62
- """Extract educational content from MasterContent for game generation."""
68
+ """Extract educational content from MasterContent for game generation.
69
+
70
+ Accepts MasterContent or DailyLesson (single-agent path lacks subject/grade_level).
71
+ """
63
72
  parts = [
64
73
  f"LESSON: {master.title}",
65
- f"SUBJECT: {master.subject}",
66
- f"GRADE: {master.grade_level}",
67
- f"TOPIC: {master.topic}",
74
+ f"SUBJECT: {getattr(master, 'subject', 'Social Studies')}",
75
+ f"GRADE: {getattr(master, 'grade_level', '')}",
76
+ f"TOPIC: {getattr(master, 'topic', master.title)}",
68
77
  f"OBJECTIVE: {master.objective}",
69
78
  ]
70
79
 
@@ -76,36 +85,187 @@ def _extract_game_content(master: MasterContent) -> str:
76
85
  + (f" (context: {v.context_sentence})" if v.context_sentence else "")
77
86
  )
78
87
 
79
- if master.guided_notes:
88
+ guided_notes = getattr(master, "guided_notes", None)
89
+ if guided_notes:
80
90
  parts.append("\nKEY FACTS (fill-in-the-blank):")
81
- for note in master.guided_notes:
91
+ for note in guided_notes:
82
92
  parts.append(f" - Q: {note.prompt} → A: {note.answer}")
83
93
 
84
- if master.exit_ticket:
94
+ exit_ticket = getattr(master, "exit_ticket", None)
95
+ if exit_ticket:
85
96
  parts.append("\nQUIZ QUESTIONS:")
86
- for i, q in enumerate(master.exit_ticket, 1):
87
- parts.append(f" {i}. {q.question}")
97
+ # DailyLesson exit_ticket is a list of ExitTicketQuestion (has .question)
98
+ # MasterContent exit_ticket may differ — handle both
99
+ for i, q in enumerate(exit_ticket if isinstance(exit_ticket, list) else [], 1):
100
+ question_text = getattr(q, "question", str(q))
101
+ parts.append(f" {i}. {question_text}")
88
102
  if hasattr(q, "expected_answer") and q.expected_answer:
89
103
  parts.append(f" Answer: {q.expected_answer}")
90
104
 
91
- if master.primary_sources:
105
+ primary_sources = getattr(master, "primary_sources", None)
106
+ if primary_sources:
92
107
  parts.append("\nPRIMARY SOURCES:")
93
- for src in master.primary_sources:
94
- parts.append(f" - {src.title} ({src.source_type})")
108
+ for src in primary_sources:
109
+ src_title = getattr(src, "title", str(src))
110
+ src_type = getattr(src, "source_type", "")
111
+ parts.append(f" - {src_title} ({src_type})")
95
112
  if hasattr(src, "content_text") and src.content_text:
96
113
  parts.append(f" Text: {src.content_text[:300]}...")
97
114
 
98
- if master.direct_instruction:
115
+ direct_instruction = getattr(master, "direct_instruction", None)
116
+ if direct_instruction and not isinstance(direct_instruction, str):
99
117
  parts.append("\nKEY CONCEPTS:")
100
- for section in master.direct_instruction:
101
- parts.append(f" - {section.heading}")
118
+ for section in direct_instruction:
119
+ parts.append(f" - {getattr(section, 'heading', str(section))}")
102
120
  if hasattr(section, "key_points") and section.key_points:
103
121
  for pt in section.key_points[:3]:
104
122
  parts.append(f" • {pt}")
123
+ elif isinstance(direct_instruction, str) and direct_instruction:
124
+ parts.append(f"\nKEY CONCEPTS:\n{direct_instruction[:500]}")
105
125
 
106
126
  return "\n".join(parts)
107
127
 
108
128
 
129
+ def _repair_html_structure(html: str) -> str:
130
+ """Repair common LLM HTML generation failures.
131
+
132
+ LLMs sometimes emit:
133
+ - CSS rules directly after <!DOCTYPE html> with no <head> or <body>
134
+ - <title> text as bare text instead of inside a <title> tag
135
+ - <style> content without the wrapping <style> tag
136
+ - Missing </html> closer
137
+
138
+ This function detects those patterns and wraps the content into a
139
+ valid HTML skeleton, preserving all the CSS and JS the LLM generated.
140
+ """
141
+ html_lower = html.lower()
142
+
143
+ # Strip duplicate DOCTYPE/html tags (LLM sometimes nests two documents)
144
+ if html.count("<!DOCTYPE") > 1:
145
+ # Keep only the content between the LAST <!DOCTYPE and end
146
+ # Actually: strip all but the first DOCTYPE
147
+ parts = html.split("<!DOCTYPE")
148
+ html = "<!DOCTYPE" + parts[1] # keep first occurrence + content
149
+ for extra in parts[2:]:
150
+ # Append content after stripping the duplicate preamble
151
+ extra_content = re.sub(
152
+ r"^[^>]*>\s*<html[^>]*>\s*", "", extra, flags=re.IGNORECASE
153
+ )
154
+ html += extra_content
155
+ html_lower = html.lower()
156
+
157
+ # Check for bare JS not wrapped in <script> tags
158
+ has_script_tags = "<script" in html_lower
159
+ has_js_code = bool(re.search(
160
+ r"(?:function\s+\w+|const\s+\w+\s*=|let\s+\w+\s*=|document\.|addEventListener)",
161
+ html
162
+ ))
163
+ if not has_script_tags and has_js_code:
164
+ # Find where JS starts (after the last </div> or after CSS)
165
+ # Look for first function/const/let/document line
166
+ js_start = re.search(
167
+ r"\n((?:function |const |let |var |document\.|//\s*[-=]|class\s+\w+\s*\{))",
168
+ html
169
+ )
170
+ if js_start:
171
+ before_js = html[:js_start.start()]
172
+ js_code = html[js_start.start():]
173
+ # Strip trailing </body></html> from JS if present
174
+ js_code = re.sub(r"\s*</body>\s*</html>\s*$", "", js_code, flags=re.IGNORECASE)
175
+ html = before_js + "\n<script>\n" + js_code + "\n</script>\n</body>\n</html>"
176
+ html_lower = html.lower()
177
+
178
+ # Already well-formed — has <head>, <body>, and <script>
179
+ if "<head>" in html_lower and "<body>" in html_lower and (has_script_tags or "<script" in html_lower):
180
+ return html
181
+
182
+ # Has <head> but no <body> — unusual, leave it
183
+ if "<head>" in html_lower:
184
+ return html
185
+
186
+ # Missing <head>/<body> — LLM dumped CSS/JS straight into <html>
187
+ # Strategy: find the first <script or first element tag to split on,
188
+ # put everything before the first block element into <head>,
189
+ # everything else into <body>.
190
+
191
+ # Collect lines after <!DOCTYPE html><html ...>
192
+ # Find where the html tag ends
193
+ html_tag_end = html_lower.find(">", html_lower.find("<html"))
194
+ if html_tag_end == -1:
195
+ # No <html> tag at all — wrap everything
196
+ title_match = re.search(r"^([^\n<@{]+)", html.strip())
197
+ title_text = title_match.group(1).strip() if title_match else "Learning Game"
198
+ return (
199
+ f"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n"
200
+ f"<meta charset=\"UTF-8\">\n"
201
+ f"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
202
+ f"<title>{title_text}</title>\n"
203
+ f"<style>\n{html}\n</style>\n</head>\n<body></body>\n</html>"
204
+ )
205
+
206
+ _preamble = html[:html_tag_end + 1] # noqa: F841 # everything up to and including <html ...>
207
+ rest = html[html_tag_end + 1:].strip()
208
+
209
+ # Extract bare title text (first non-tag, non-CSS line after <html>)
210
+ # This catches lines like "Age of Exploration: Chart Your Course" emitted before the CSS
211
+ title_text = "Learning Game"
212
+ title_match = re.match(r"^\s*([A-Za-z][^\n@{<]{3,120})\n", rest)
213
+ if title_match:
214
+ candidate = title_match.group(1).strip()
215
+ # Accept as title if it looks like a human-readable title (not CSS)
216
+ if not candidate.startswith(("@", "{", ".", "#", "*", ":", "/")):
217
+ title_text = candidate
218
+ rest = rest[title_match.end():]
219
+
220
+ # Split: CSS/meta goes in <head>, script/div/etc goes in <body>
221
+ head_parts: list[str] = []
222
+ body_parts: list[str] = [] # noqa: F841
223
+
224
+ # Collect <meta> tags floating outside <head>
225
+ meta_tags = re.findall(r"<meta[^>]*>", rest, re.IGNORECASE)
226
+ for m in meta_tags:
227
+ head_parts.append(m)
228
+ rest = rest.replace(m, "", 1)
229
+
230
+ # Wrap bare CSS (starts with @import, :root, or selector{) in <style>
231
+ # Find contiguous CSS blocks before the first <script or <div
232
+ first_script = re.search(r"<script|<div|<section|<main|<canvas", rest, re.IGNORECASE)
233
+ split_at = first_script.start() if first_script else len(rest)
234
+
235
+ css_block = rest[:split_at].strip()
236
+ body_block = rest[split_at:].strip()
237
+
238
+ if css_block:
239
+ # Check if it's raw CSS (no surrounding <style> tags)
240
+ if not re.search(r"<style", css_block, re.IGNORECASE):
241
+ head_parts.append(f"<style>\n{css_block}\n</style>")
242
+ else:
243
+ head_parts.append(css_block)
244
+
245
+ head_html = "\n".join(head_parts)
246
+ body_html = body_block
247
+
248
+ # Ensure </html> at end
249
+ if not body_html.rstrip().endswith("</html>"):
250
+ body_html = body_html.rstrip()
251
+ if not body_html.endswith("</body>"):
252
+ body_html += "\n</body>"
253
+ body_html += "\n</html>"
254
+
255
+ repaired = (
256
+ f"<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n"
257
+ f"<meta charset=\"UTF-8\">\n"
258
+ f"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n"
259
+ f"<title>{title_text}</title>\n"
260
+ f"{head_html}\n"
261
+ f"</head>\n<body>\n"
262
+ f"{body_html}"
263
+ )
264
+
265
+ logger.info("Repaired HTML structure (was missing <head>/<body>)")
266
+ return repaired
267
+
268
+
109
269
  def _validate_game_html(html: str, master: MasterContent) -> list[str]:
110
270
  """Validate that the generated HTML is a working game."""
111
271
  issues = []
@@ -116,6 +276,12 @@ def _validate_game_html(html: str, master: MasterContent) -> list[str]:
116
276
  if "<html" not in html.lower():
117
277
  issues.append("Missing <html> tag")
118
278
 
279
+ if "<head>" not in html.lower():
280
+ issues.append("Missing <head> tag — CSS may render broken")
281
+
282
+ if "<body>" not in html.lower():
283
+ issues.append("Missing <body> tag — content structure invalid")
284
+
119
285
  if "<script" not in html.lower():
120
286
  issues.append("Missing <script> tag — no JavaScript")
121
287
 
@@ -123,7 +289,7 @@ def _validate_game_html(html: str, master: MasterContent) -> list[str]:
123
289
  issues.append("No JavaScript functions found")
124
290
 
125
291
  # Check that educational content is embedded
126
- topic_words = master.topic.lower().split()[:3]
292
+ topic_words = getattr(master, "topic", master.title).lower().split()[:3]
127
293
  html_lower = html.lower()
128
294
  found_topic = any(w in html_lower for w in topic_words if len(w) > 3)
129
295
  if not found_topic:
@@ -142,7 +308,7 @@ def _validate_game_html(html: str, master: MasterContent) -> list[str]:
142
308
 
143
309
 
144
310
  async def compile_game(
145
- master: MasterContent,
311
+ master: "MasterContent | Any",
146
312
  persona: TeacherPersona | None = None,
147
313
  output_dir: Path | None = None,
148
314
  config: AppConfig | None = None,
@@ -172,7 +338,15 @@ async def compile_game(
172
338
  output_dir.mkdir(parents=True, exist_ok=True)
173
339
 
174
340
  config = config or AppConfig.load()
175
- config = route_model("game_generate", config)
341
+ # Games require maximum intelligence — always Opus.
342
+ from clawed.config import get_api_key
343
+ from clawed.models import LLMProvider
344
+ anthropic_key = get_api_key("anthropic")
345
+ if anthropic_key:
346
+ config.provider = LLMProvider.ANTHROPIC
347
+ config.anthropic_model = "claude-opus-4-6"
348
+ else:
349
+ config = route_model("game_generate", config)
176
350
  client = LLMClient(config)
177
351
 
178
352
  # Build the game generation prompt
@@ -209,7 +383,7 @@ async def compile_game(
209
383
  if persona:
210
384
  prompt_parts.append(
211
385
  f"\nTEACHER'S STYLE: {persona.tone or 'engaging'}\n"
212
- f"Grade level: {master.grade_level}\n"
386
+ f"Grade level: {getattr(master, 'grade_level', '')}\n"
213
387
  "Match the difficulty and humor to this teacher and grade."
214
388
  )
215
389
 
@@ -233,6 +407,10 @@ async def compile_game(
233
407
  html = re.sub(r"\n?```$", "", html)
234
408
  html = html.strip()
235
409
 
410
+ # Structural repair: LLMs sometimes emit CSS/content outside <head>/<body>.
411
+ # Detect and wrap into a valid HTML skeleton if needed.
412
+ html = _repair_html_structure(html)
413
+
236
414
  # Validate
237
415
  issues = _validate_game_html(html, master)
238
416
  if issues and attempt < max_attempts - 1:
@@ -457,6 +457,8 @@ def export_student_handout(
457
457
 
458
458
  Returns the path to the saved .docx file.
459
459
  """
460
+ import re as _re
461
+
460
462
  from docx import Document
461
463
  from docx.enum.table import WD_TABLE_ALIGNMENT
462
464
  from docx.enum.text import WD_ALIGN_PARAGRAPH
@@ -464,15 +466,27 @@ def export_student_handout(
464
466
 
465
467
  from clawed.sanitize import sanitize_text
466
468
 
469
+ def _strip_answers(text: str) -> str:
470
+ """Remove answer keys from student-facing content."""
471
+ # Strip (Answer: ...) patterns
472
+ text = _re.sub(r"\s*\(Answer:\s*[^)]*\)", "", text)
473
+ # Strip (answer: ...) lowercase variant
474
+ text = _re.sub(r"\s*\(answer:\s*[^)]*\)", "", text)
475
+ # Strip "Answer: ..." at end of lines
476
+ text = _re.sub(r"\s*Answer:\s*.+$", "", text, flags=_re.MULTILINE)
477
+ # Strip "Expected: ..." patterns
478
+ text = _re.sub(r"\s*\*?Expected:?\*?\s*.+$", "", text, flags=_re.MULTILINE)
479
+ return text.strip()
480
+
467
481
  doc = Document()
468
482
 
469
- # Sanitize all lesson text fields for the handout
483
+ # Sanitize AND strip answers from all student-facing text
470
484
  lesson.title = sanitize_text(lesson.title)
471
485
  lesson.objective = sanitize_text(lesson.objective)
472
486
  lesson.do_now = sanitize_text(lesson.do_now) if lesson.do_now else ""
473
487
  lesson.direct_instruction = sanitize_text(lesson.direct_instruction) if lesson.direct_instruction else ""
474
- lesson.guided_practice = sanitize_text(lesson.guided_practice) if lesson.guided_practice else ""
475
- lesson.independent_work = sanitize_text(lesson.independent_work) if lesson.independent_work else ""
488
+ lesson.guided_practice = _strip_answers(sanitize_text(lesson.guided_practice)) if lesson.guided_practice else ""
489
+ lesson.independent_work = _strip_answers(sanitize_text(lesson.independent_work)) if lesson.independent_work else ""
476
490
  for q in lesson.exit_ticket:
477
491
  q.question = sanitize_text(q.question)
478
492