clawed 2.3.8__tar.gz → 2.3.9__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 (256) hide show
  1. {clawed-2.3.8 → clawed-2.3.9}/PKG-INFO +46 -9
  2. {clawed-2.3.8 → clawed-2.3.9}/README.md +34 -4
  3. {clawed-2.3.8 → clawed-2.3.9}/clawed/__init__.py +1 -1
  4. {clawed-2.3.8 → clawed-2.3.9}/clawed/_legacy_gateway.py +1 -1
  5. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent.py +24 -5
  6. {clawed-2.3.8 → clawed-2.3.9}/clawed/cli.py +2 -0
  7. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/generate.py +44 -9
  8. clawed-2.3.9/clawed/commands/train.py +258 -0
  9. {clawed-2.3.8 → clawed-2.3.9}/clawed/config.py +34 -6
  10. {clawed-2.3.8 → clawed-2.3.9}/clawed/generation.py +199 -27
  11. {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/generate.py +1 -1
  12. clawed-2.3.8/clawed/openclaw_plugin.py → clawed-2.3.9/clawed/hermes_plugin.py +1 -1
  13. {clawed-2.3.8 → clawed-2.3.9}/clawed/lesson.py +14 -0
  14. {clawed-2.3.8 → clawed-2.3.9}/clawed/llm.py +2 -5
  15. {clawed-2.3.8 → clawed-2.3.9}/clawed/mcp_server.py +1 -1
  16. clawed-2.3.9/clawed/multi_agent.py +302 -0
  17. clawed-2.3.9/clawed/openclaw_plugin.py +24 -0
  18. clawed-2.3.9/clawed/persona.py +166 -0
  19. clawed-2.3.9/clawed/prompts/multi_agent_researcher.txt +69 -0
  20. clawed-2.3.9/clawed/prompts/multi_agent_reviewer.txt +58 -0
  21. {clawed-2.3.8 → clawed-2.3.9}/clawed/scheduler.py +1 -1
  22. clawed-2.3.8/clawed/transports/openclaw.py → clawed-2.3.9/clawed/transports/hermes.py +15 -15
  23. clawed-2.3.9/clawed/transports/openclaw.py +11 -0
  24. {clawed-2.3.8 → clawed-2.3.9}/clawed/workspace.py +1 -1
  25. {clawed-2.3.8 → clawed-2.3.9}/pyproject.toml +18 -5
  26. clawed-2.3.8/clawed/persona.py +0 -85
  27. {clawed-2.3.8 → clawed-2.3.9}/.gitignore +0 -0
  28. {clawed-2.3.8 → clawed-2.3.9}/LICENSE +0 -0
  29. {clawed-2.3.8 → clawed-2.3.9}/clawed/__main__.py +0 -0
  30. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/__init__.py +0 -0
  31. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/approvals.py +0 -0
  32. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/autonomy.py +0 -0
  33. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/context.py +0 -0
  34. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/core.py +0 -0
  35. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/custom_tools.py +0 -0
  36. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/drive/__init__.py +0 -0
  37. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/drive/auth.py +0 -0
  38. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/drive/client.py +0 -0
  39. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/fake_llm.py +0 -0
  40. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/loop.py +0 -0
  41. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/__init__.py +0 -0
  42. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/curriculum.py +0 -0
  43. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/curriculum_kb.py +0 -0
  44. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/embeddings.py +0 -0
  45. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/episodes.py +0 -0
  46. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/identity.py +0 -0
  47. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/loader.py +0 -0
  48. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/memory/preferences.py +0 -0
  49. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/planner.py +0 -0
  50. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/prompt.py +0 -0
  51. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/scheduler.py +0 -0
  52. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/__init__.py +0 -0
  53. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/base.py +0 -0
  54. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/configure_profile.py +0 -0
  55. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/curriculum_map.py +0 -0
  56. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_create_doc.py +0 -0
  57. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_create_slides.py +0 -0
  58. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_list.py +0 -0
  59. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_organize.py +0 -0
  60. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_read.py +0 -0
  61. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/drive_upload.py +0 -0
  62. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/export_document.py +0 -0
  63. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/gap_analysis.py +0 -0
  64. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/generate_assessment.py +0 -0
  65. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/generate_lesson.py +0 -0
  66. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/generate_lesson_bundle.py +0 -0
  67. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/generate_materials.py +0 -0
  68. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/generate_unit.py +0 -0
  69. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/ingest_materials.py +0 -0
  70. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/parent_comm.py +0 -0
  71. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/read_heartbeat.py +0 -0
  72. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/read_workspace.py +0 -0
  73. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/request_approval.py +0 -0
  74. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/schedule_task.py +0 -0
  75. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/search_lessons.py +0 -0
  76. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/search_my_materials.py +0 -0
  77. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/search_standards.py +0 -0
  78. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/student_insights.py +0 -0
  79. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/sub_packet.py +0 -0
  80. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/switch_model.py +0 -0
  81. {clawed-2.3.8 → clawed-2.3.9}/clawed/agent_core/tools/update_soul.py +0 -0
  82. {clawed-2.3.8 → clawed-2.3.9}/clawed/analytics.py +0 -0
  83. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/__init__.py +0 -0
  84. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/deps.py +0 -0
  85. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/__init__.py +0 -0
  86. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/chat.py +0 -0
  87. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/export.py +0 -0
  88. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/feedback.py +0 -0
  89. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/gateway_chat.py +0 -0
  90. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/generate.py +0 -0
  91. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/ingest.py +0 -0
  92. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/lessons.py +0 -0
  93. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/school.py +0 -0
  94. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/settings.py +0 -0
  95. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/routes/tools.py +0 -0
  96. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/server.py +0 -0
  97. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/static/app.js +0 -0
  98. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/static/style.css +0 -0
  99. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/static/widget.js +0 -0
  100. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/analytics.html +0 -0
  101. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/base.html +0 -0
  102. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/dashboard.html +0 -0
  103. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/generate.html +0 -0
  104. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/index.html +0 -0
  105. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/lesson.html +0 -0
  106. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/profile.html +0 -0
  107. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/settings.html +0 -0
  108. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/stats.html +0 -0
  109. {clawed-2.3.8 → clawed-2.3.9}/clawed/api/templates/students.html +0 -0
  110. {clawed-2.3.8 → clawed-2.3.9}/clawed/assessment.py +0 -0
  111. {clawed-2.3.8 → clawed-2.3.9}/clawed/asset_registry.py +0 -0
  112. {clawed-2.3.8 → clawed-2.3.9}/clawed/async_utils.py +0 -0
  113. {clawed-2.3.8 → clawed-2.3.9}/clawed/auth/__init__.py +0 -0
  114. {clawed-2.3.8 → clawed-2.3.9}/clawed/auth/google_auth.py +0 -0
  115. {clawed-2.3.8 → clawed-2.3.9}/clawed/bot_state.py +0 -0
  116. {clawed-2.3.8 → clawed-2.3.9}/clawed/chat.py +0 -0
  117. {clawed-2.3.8 → clawed-2.3.9}/clawed/cli_chat.py +0 -0
  118. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/__init__.py +0 -0
  119. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/_helpers.py +0 -0
  120. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/bot.py +0 -0
  121. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/config.py +0 -0
  122. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/config_llm.py +0 -0
  123. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/config_profile.py +0 -0
  124. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/export.py +0 -0
  125. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/generate_assessment.py +0 -0
  126. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/generate_unit.py +0 -0
  127. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/queue.py +0 -0
  128. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/schedule_cmd.py +0 -0
  129. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/sub.py +0 -0
  130. {clawed-2.3.8 → clawed-2.3.9}/clawed/commands/workspace_cmd.py +0 -0
  131. {clawed-2.3.8 → clawed-2.3.9}/clawed/compile_slides.py +0 -0
  132. {clawed-2.3.8 → clawed-2.3.9}/clawed/compile_student.py +0 -0
  133. {clawed-2.3.8 → clawed-2.3.9}/clawed/compile_teacher.py +0 -0
  134. {clawed-2.3.8 → clawed-2.3.9}/clawed/corpus.py +0 -0
  135. {clawed-2.3.8 → clawed-2.3.9}/clawed/curriculum_map.py +0 -0
  136. {clawed-2.3.8 → clawed-2.3.9}/clawed/database.py +0 -0
  137. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/__init__.py +0 -0
  138. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_assessment.json +0 -0
  139. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_formative_assessment.json +0 -0
  140. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_lesson_materials.json +0 -0
  141. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_lesson_science_g6.json +0 -0
  142. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_lesson_social_studies_g8.json +0 -0
  143. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_master_content.json +0 -0
  144. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_pacing_guide.json +0 -0
  145. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_quiz.json +0 -0
  146. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_rubric.json +0 -0
  147. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_unit_plan.json +0 -0
  148. {clawed-2.3.8 → clawed-2.3.9}/clawed/demo/demo_year_map.json +0 -0
  149. {clawed-2.3.8 → clawed-2.3.9}/clawed/differentiation.py +0 -0
  150. {clawed-2.3.8 → clawed-2.3.9}/clawed/doc_export.py +0 -0
  151. {clawed-2.3.8 → clawed-2.3.9}/clawed/drive.py +0 -0
  152. {clawed-2.3.8 → clawed-2.3.9}/clawed/evaluation.py +0 -0
  153. {clawed-2.3.8 → clawed-2.3.9}/clawed/export_docx.py +0 -0
  154. {clawed-2.3.8 → clawed-2.3.9}/clawed/export_handout.py +0 -0
  155. {clawed-2.3.8 → clawed-2.3.9}/clawed/export_markdown.py +0 -0
  156. {clawed-2.3.8 → clawed-2.3.9}/clawed/export_pdf.py +0 -0
  157. {clawed-2.3.8 → clawed-2.3.9}/clawed/export_pptx.py +0 -0
  158. {clawed-2.3.8 → clawed-2.3.9}/clawed/export_templates.py +0 -0
  159. {clawed-2.3.8 → clawed-2.3.9}/clawed/export_theme.py +0 -0
  160. {clawed-2.3.8 → clawed-2.3.9}/clawed/exporter.py +0 -0
  161. {clawed-2.3.8 → clawed-2.3.9}/clawed/failure_codes.py +0 -0
  162. {clawed-2.3.8 → clawed-2.3.9}/clawed/feedback.py +0 -0
  163. {clawed-2.3.8 → clawed-2.3.9}/clawed/formats/__init__.py +0 -0
  164. {clawed-2.3.8 → clawed-2.3.9}/clawed/formats/flipchart.py +0 -0
  165. {clawed-2.3.8 → clawed-2.3.9}/clawed/formats/notebook.py +0 -0
  166. {clawed-2.3.8 → clawed-2.3.9}/clawed/formats/xbk.py +0 -0
  167. {clawed-2.3.8 → clawed-2.3.9}/clawed/gateway.py +0 -0
  168. {clawed-2.3.8 → clawed-2.3.9}/clawed/gateway_response.py +0 -0
  169. {clawed-2.3.8 → clawed-2.3.9}/clawed/generation_report.py +0 -0
  170. {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/__init__.py +0 -0
  171. {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/export.py +0 -0
  172. {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/feedback.py +0 -0
  173. {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/gaps.py +0 -0
  174. {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/ingest.py +0 -0
  175. {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/misc.py +0 -0
  176. {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/onboard.py +0 -0
  177. {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/schedule.py +0 -0
  178. {clawed-2.3.8 → clawed-2.3.9}/clawed/handlers/standards.py +0 -0
  179. {clawed-2.3.8 → clawed-2.3.9}/clawed/image_pipeline.py +0 -0
  180. {clawed-2.3.8 → clawed-2.3.9}/clawed/improver.py +0 -0
  181. {clawed-2.3.8 → clawed-2.3.9}/clawed/ingestor.py +0 -0
  182. {clawed-2.3.8 → clawed-2.3.9}/clawed/io.py +0 -0
  183. {clawed-2.3.8 → clawed-2.3.9}/clawed/master_content.py +0 -0
  184. {clawed-2.3.8 → clawed-2.3.9}/clawed/materials.py +0 -0
  185. {clawed-2.3.8 → clawed-2.3.9}/clawed/memory_engine.py +0 -0
  186. {clawed-2.3.8 → clawed-2.3.9}/clawed/model_router.py +0 -0
  187. {clawed-2.3.8 → clawed-2.3.9}/clawed/models.py +0 -0
  188. {clawed-2.3.8 → clawed-2.3.9}/clawed/onboarding.py +0 -0
  189. {clawed-2.3.8 → clawed-2.3.9}/clawed/parent_comm.py +0 -0
  190. {clawed-2.3.8 → clawed-2.3.9}/clawed/persona_evolution.py +0 -0
  191. {clawed-2.3.8 → clawed-2.3.9}/clawed/planner.py +0 -0
  192. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/504_accommodations.txt +0 -0
  193. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/admin_lesson_plan.txt +0 -0
  194. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/assessment.txt +0 -0
  195. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/curriculum_gaps.txt +0 -0
  196. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/dbq_assessment.txt +0 -0
  197. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/differentiation.txt +0 -0
  198. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/formative_assessment.txt +0 -0
  199. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/iep_modification.txt +0 -0
  200. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/lesson_plan.txt +0 -0
  201. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/master_content.txt +0 -0
  202. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/pacing_guide.txt +0 -0
  203. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/parent_note.txt +0 -0
  204. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/persona_extract.txt +0 -0
  205. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/quiz.txt +0 -0
  206. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/rubric.txt +0 -0
  207. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/student_packet.txt +0 -0
  208. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/sub_packet.txt +0 -0
  209. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/summative_assessment.txt +0 -0
  210. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/tiered_assignments.txt +0 -0
  211. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/unit_plan.txt +0 -0
  212. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/worksheet.txt +0 -0
  213. {clawed-2.3.8 → clawed-2.3.9}/clawed/prompts/year_map.txt +0 -0
  214. {clawed-2.3.8 → clawed-2.3.9}/clawed/quality.py +0 -0
  215. {clawed-2.3.8 → clawed-2.3.9}/clawed/reading_report.py +0 -0
  216. {clawed-2.3.8 → clawed-2.3.9}/clawed/router.py +0 -0
  217. {clawed-2.3.8 → clawed-2.3.9}/clawed/sanitize.py +0 -0
  218. {clawed-2.3.8 → clawed-2.3.9}/clawed/school.py +0 -0
  219. {clawed-2.3.8 → clawed-2.3.9}/clawed/search.py +0 -0
  220. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/__init__.py +0 -0
  221. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/art.py +0 -0
  222. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/base.py +0 -0
  223. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/computer_science.py +0 -0
  224. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/ela.py +0 -0
  225. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/foreign_language.py +0 -0
  226. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/history.py +0 -0
  227. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/library.py +0 -0
  228. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/math.py +0 -0
  229. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/music.py +0 -0
  230. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/physical_education.py +0 -0
  231. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/science.py +0 -0
  232. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/social_studies.py +0 -0
  233. {clawed-2.3.8 → clawed-2.3.9}/clawed/skills/special_education.py +0 -0
  234. {clawed-2.3.8 → clawed-2.3.9}/clawed/slide_images.py +0 -0
  235. {clawed-2.3.8 → clawed-2.3.9}/clawed/standards.py +0 -0
  236. {clawed-2.3.8 → clawed-2.3.9}/clawed/state.py +0 -0
  237. {clawed-2.3.8 → clawed-2.3.9}/clawed/state_standards.py +0 -0
  238. {clawed-2.3.8 → clawed-2.3.9}/clawed/student_bot.py +0 -0
  239. {clawed-2.3.8 → clawed-2.3.9}/clawed/student_cli.py +0 -0
  240. {clawed-2.3.8 → clawed-2.3.9}/clawed/student_telegram_bot.py +0 -0
  241. {clawed-2.3.8 → clawed-2.3.9}/clawed/sub_packet.py +0 -0
  242. {clawed-2.3.8 → clawed-2.3.9}/clawed/task_queue.py +0 -0
  243. {clawed-2.3.8 → clawed-2.3.9}/clawed/templates_lib.py +0 -0
  244. {clawed-2.3.8 → clawed-2.3.9}/clawed/tools.py +0 -0
  245. {clawed-2.3.8 → clawed-2.3.9}/clawed/transports/__init__.py +0 -0
  246. {clawed-2.3.8 → clawed-2.3.9}/clawed/transports/cli.py +0 -0
  247. {clawed-2.3.8 → clawed-2.3.9}/clawed/transports/student_telegram.py +0 -0
  248. {clawed-2.3.8 → clawed-2.3.9}/clawed/transports/telegram.py +0 -0
  249. {clawed-2.3.8 → clawed-2.3.9}/clawed/transports/web.py +0 -0
  250. {clawed-2.3.8 → clawed-2.3.9}/clawed/tui.py +0 -0
  251. {clawed-2.3.8 → clawed-2.3.9}/clawed/tui_chat.py +0 -0
  252. {clawed-2.3.8 → clawed-2.3.9}/clawed/validation.py +0 -0
  253. {clawed-2.3.8 → clawed-2.3.9}/clawed/voice.py +0 -0
  254. {clawed-2.3.8 → clawed-2.3.9}/clawed/voice_check.py +0 -0
  255. {clawed-2.3.8 → clawed-2.3.9}/eduagent/__init__.py +0 -0
  256. {clawed-2.3.8 → clawed-2.3.9}/eduagent/_compat.py +0 -0
@@ -1,22 +1,29 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clawed
3
- Version: 2.3.8
4
- Summary: Claw-ED personal AI teaching agent. Learns your voice, works while you sleep.
5
- Project-URL: Homepage, https://github.com/SirhanMacx/Claw-ED
3
+ Version: 2.3.9
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
+ Project-URL: Homepage, https://sirhanmacx.github.io/Claw-ED
6
6
  Project-URL: Documentation, https://github.com/SirhanMacx/Claw-ED#readme
7
- Project-URL: Issues, https://github.com/SirhanMacx/Claw-ED/issues
7
+ Project-URL: Repository, https://github.com/SirhanMacx/Claw-ED
8
+ Project-URL: Bug Tracker, https://github.com/SirhanMacx/Claw-ED/issues
9
+ Project-URL: Changelog, https://github.com/SirhanMacx/Claw-ED/blob/main/CHANGELOG.md
10
+ Project-URL: PyPI, https://pypi.org/project/clawed/
8
11
  Author: Jon Maccarello & Claw-ED contributors
9
12
  License-Expression: MIT
10
13
  License-File: LICENSE
11
- Keywords: ai,claw-ed,clawed,education,lesson-plans,teachers,teaching
14
+ Keywords: ai,ai-for-teachers,claw-ed,clawed,curriculum,edtech,education,hermes-agent,k12,lesson-plan-generator,lesson-planner,lesson-planning,lesson-plans,open-source-education,pedagogy,teacher-tools,teachers,teaching,teaching-assistant
12
15
  Classifier: Development Status :: 3 - Alpha
16
+ Classifier: Environment :: Console
13
17
  Classifier: Intended Audience :: Education
14
18
  Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Operating System :: OS Independent
15
20
  Classifier: Programming Language :: Python :: 3
16
21
  Classifier: Programming Language :: Python :: 3.10
17
22
  Classifier: Programming Language :: Python :: 3.11
18
23
  Classifier: Programming Language :: Python :: 3.12
19
24
  Classifier: Topic :: Education
25
+ Classifier: Topic :: Education :: Computer Aided Instruction (CAI)
26
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
27
  Requires-Python: >=3.10
21
28
  Requires-Dist: apscheduler<4.0,>=3.10.0
22
29
  Requires-Dist: fastapi<1.0,>=0.110.0
@@ -71,17 +78,47 @@ Description-Content-Type: text/markdown
71
78
 
72
79
  Your personal AI teaching agent. Runs on your machine, learns your voice, works while you sleep.
73
80
 
74
- Built on the OpenClaw agent framework. Open source. MIT license.
81
+ Built on the Hermes Agent framework (NousResearch). Open source. MIT license.
75
82
 
76
83
  <p align="center">
77
- <a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/v/clawed?color=blue" alt="PyPI"></a>
78
- <a href="https://python.org"><img src="https://img.shields.io/badge/Python-3.10+-blue" alt="Python 3.10+"></a>
84
+ <a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/v/clawed?color=blue" alt="PyPI version"></a>
85
+ <a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/pyversions/clawed" alt="Python versions"></a>
79
86
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="MIT License"></a>
80
87
  <a href="https://github.com/SirhanMacx/Claw-ED/stargazers"><img src="https://img.shields.io/github/stars/SirhanMacx/Claw-ED" alt="GitHub stars"></a>
88
+ <a href="https://pepy.tech/project/clawed"><img src="https://static.pepy.tech/badge/clawed" alt="Downloads"></a>
81
89
  </p>
82
90
 
83
91
  ---
84
92
 
93
+ ## What's new in v2.3.9
94
+
95
+ **Multi-agent lesson generation.** Instead of one LLM call doing everything, `--multi-agent` spawns a team of three specialized agents: a Researcher who finds primary sources and historical context, a Writer who drafts the lesson in your voice, and a Reviewer who scores quality on voice fidelity, pedagogy, and differentiation — sending it back for revision if any dimension scores below 7/10. The result: higher-quality lessons with better sources and stronger voice matching.
96
+
97
+ ```bash
98
+ clawed lesson "The Missouri Compromise" -g 8 --multi-agent
99
+ ```
100
+
101
+ **Claude Code integration.** Claw-ED now auto-reads OAuth tokens from Claude Code's credential store (`~/.claude/.credentials.json`). If you have Claude Code installed, Claw-ED just works — no separate API key setup needed, and tokens auto-refresh. Claw-ED also works as an MCP server that Claude Code can use directly:
102
+
103
+ ```bash
104
+ clawed mcp-server # Add to Claude Code as an MCP tool
105
+ ```
106
+
107
+ **Continuous improvement pipeline.** The new `clawed train` command lets your AI fleet (or you) continuously improve lesson quality:
108
+
109
+ ```bash
110
+ clawed train --drive # Ingest new files from Google Drive, refine persona
111
+ clawed train --path ./files # Ingest local curriculum files
112
+ clawed train --benchmark -n 5 # Generate 5 lessons and score them
113
+ clawed train --full # Drive ingest + benchmark
114
+ ```
115
+
116
+ Persona refinement is now incremental — new voice markers merge with existing ones instead of overwriting. Your teaching identity gets richer over time.
117
+
118
+ **OAuth auth fix.** All Anthropic API calls now correctly detect OAuth tokens and use Bearer authentication with Claude Code identity headers. Fixes the 401 errors that affected fleet agents and Claw-ED direct usage.
119
+
120
+ ---
121
+
85
122
  ## What's new in v2.3.8
86
123
 
87
124
  **No more silent failures.** Every step of lesson generation now reports what happened — persona loading, material search, quality review, voice matching. If something fails, you'll know exactly what and why, with structured NLAH failure codes. Quality review runs automatically on every generated lesson and fails closed: if the review itself crashes, it reports failure instead of silently passing.
@@ -116,7 +153,7 @@ Built on the OpenClaw agent framework. Open source. MIT license.
116
153
 
117
154
  ## What is this?
118
155
 
119
- Claw-ED is a personal AI agent for teachers. It uses the same architecture as other OpenClaw agents -- SOUL.md for identity, layered memory, a workspace, pulse jobs, tool use, and autonomous operation. The difference is what it knows: your lesson plans, your teaching style, your standards, your students.
156
+ Claw-ED is a personal AI agent for teachers. It uses the same architecture as Hermes Agent -- SOUL.md for identity, layered memory, a workspace, cron jobs, tool use, and autonomous operation. The difference is what it knows: your lesson plans, your teaching style, your standards, your students.
120
157
 
121
158
  You feed it your curriculum files and it learns your voice. Not a generic "teacher tone" -- your actual patterns, your vocabulary, the way you structure a Do Now or frame an essential question. Then it works: drafting lessons that sound like you wrote them, organizing your Google Drive, answering student questions in your voice, prepping your week before you wake up.
122
159
 
@@ -2,17 +2,47 @@
2
2
 
3
3
  Your personal AI teaching agent. Runs on your machine, learns your voice, works while you sleep.
4
4
 
5
- Built on the OpenClaw agent framework. Open source. MIT license.
5
+ Built on the Hermes Agent framework (NousResearch). Open source. MIT license.
6
6
 
7
7
  <p align="center">
8
- <a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/v/clawed?color=blue" alt="PyPI"></a>
9
- <a href="https://python.org"><img src="https://img.shields.io/badge/Python-3.10+-blue" alt="Python 3.10+"></a>
8
+ <a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/v/clawed?color=blue" alt="PyPI version"></a>
9
+ <a href="https://pypi.org/project/clawed/"><img src="https://img.shields.io/pypi/pyversions/clawed" alt="Python versions"></a>
10
10
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="MIT License"></a>
11
11
  <a href="https://github.com/SirhanMacx/Claw-ED/stargazers"><img src="https://img.shields.io/github/stars/SirhanMacx/Claw-ED" alt="GitHub stars"></a>
12
+ <a href="https://pepy.tech/project/clawed"><img src="https://static.pepy.tech/badge/clawed" alt="Downloads"></a>
12
13
  </p>
13
14
 
14
15
  ---
15
16
 
17
+ ## What's new in v2.3.9
18
+
19
+ **Multi-agent lesson generation.** Instead of one LLM call doing everything, `--multi-agent` spawns a team of three specialized agents: a Researcher who finds primary sources and historical context, a Writer who drafts the lesson in your voice, and a Reviewer who scores quality on voice fidelity, pedagogy, and differentiation — sending it back for revision if any dimension scores below 7/10. The result: higher-quality lessons with better sources and stronger voice matching.
20
+
21
+ ```bash
22
+ clawed lesson "The Missouri Compromise" -g 8 --multi-agent
23
+ ```
24
+
25
+ **Claude Code integration.** Claw-ED now auto-reads OAuth tokens from Claude Code's credential store (`~/.claude/.credentials.json`). If you have Claude Code installed, Claw-ED just works — no separate API key setup needed, and tokens auto-refresh. Claw-ED also works as an MCP server that Claude Code can use directly:
26
+
27
+ ```bash
28
+ clawed mcp-server # Add to Claude Code as an MCP tool
29
+ ```
30
+
31
+ **Continuous improvement pipeline.** The new `clawed train` command lets your AI fleet (or you) continuously improve lesson quality:
32
+
33
+ ```bash
34
+ clawed train --drive # Ingest new files from Google Drive, refine persona
35
+ clawed train --path ./files # Ingest local curriculum files
36
+ clawed train --benchmark -n 5 # Generate 5 lessons and score them
37
+ clawed train --full # Drive ingest + benchmark
38
+ ```
39
+
40
+ Persona refinement is now incremental — new voice markers merge with existing ones instead of overwriting. Your teaching identity gets richer over time.
41
+
42
+ **OAuth auth fix.** All Anthropic API calls now correctly detect OAuth tokens and use Bearer authentication with Claude Code identity headers. Fixes the 401 errors that affected fleet agents and Claw-ED direct usage.
43
+
44
+ ---
45
+
16
46
  ## What's new in v2.3.8
17
47
 
18
48
  **No more silent failures.** Every step of lesson generation now reports what happened — persona loading, material search, quality review, voice matching. If something fails, you'll know exactly what and why, with structured NLAH failure codes. Quality review runs automatically on every generated lesson and fails closed: if the review itself crashes, it reports failure instead of silently passing.
@@ -47,7 +77,7 @@ Built on the OpenClaw agent framework. Open source. MIT license.
47
77
 
48
78
  ## What is this?
49
79
 
50
- Claw-ED is a personal AI agent for teachers. It uses the same architecture as other OpenClaw agents -- SOUL.md for identity, layered memory, a workspace, pulse jobs, tool use, and autonomous operation. The difference is what it knows: your lesson plans, your teaching style, your standards, your students.
80
+ Claw-ED is a personal AI agent for teachers. It uses the same architecture as Hermes Agent -- SOUL.md for identity, layered memory, a workspace, cron jobs, tool use, and autonomous operation. The difference is what it knows: your lesson plans, your teaching style, your standards, your students.
51
81
 
52
82
  You feed it your curriculum files and it learns your voice. Not a generic "teacher tone" -- your actual patterns, your vocabulary, the way you structure a Do Now or frame an essential question. Then it works: drafting lessons that sound like you wrote them, organizing your Google Drive, answering student questions in your voice, prepping your week before you wake up.
53
83
 
@@ -17,7 +17,7 @@ if hasattr(sys.stderr, "reconfigure"):
17
17
  except Exception:
18
18
  pass
19
19
 
20
- __version__ = "2.3.8"
20
+ __version__ = "2.3.9"
21
21
  __author__ = "Jon Maccarello & Claw-ED contributors"
22
22
  __description__ = "Personal AI teaching agent. Learns your voice, works while you sleep."
23
23
 
@@ -350,7 +350,7 @@ class Gateway:
350
350
 
351
351
  async def _status_response(self, teacher_id: str) -> GatewayResponse:
352
352
  try:
353
- from clawed.openclaw_plugin import _show_status
353
+ from clawed.hermes_plugin import _show_status
354
354
  from clawed.state import TeacherSession
355
355
  session = TeacherSession.load(teacher_id)
356
356
  return GatewayResponse(text=_show_status(session))
@@ -13,6 +13,29 @@ from __future__ import annotations
13
13
 
14
14
  import json
15
15
  import logging
16
+
17
+
18
+ def _anthropic_headers(api_key: str) -> dict[str, str]:
19
+ """Build Anthropic API headers, auto-detecting OAuth vs regular API keys.
20
+
21
+ OAuth tokens (sk-ant-oat01-*) need Bearer auth + Claude Code identity
22
+ headers. Regular API keys (sk-ant-api*) use x-api-key.
23
+ """
24
+ is_oauth = api_key.startswith("sk-ant-") and not api_key.startswith("sk-ant-api")
25
+ if is_oauth:
26
+ return {
27
+ "authorization": f"Bearer {api_key}",
28
+ "anthropic-version": "2023-06-01",
29
+ "anthropic-beta": "interleaved-thinking-2025-05-14,claude-code-20250219,oauth-2025-04-20",
30
+ "user-agent": "claude-cli/1.0.0 (external, cli)",
31
+ "x-app": "cli",
32
+ "content-type": "application/json",
33
+ }
34
+ return {
35
+ "x-api-key": api_key,
36
+ "anthropic-version": "2023-06-01",
37
+ "content-type": "application/json",
38
+ }
16
39
  from typing import Any
17
40
 
18
41
  from clawed.models import AppConfig, LLMProvider
@@ -137,11 +160,7 @@ async def _anthropic_with_tools(
137
160
  async with httpx.AsyncClient(timeout=120.0) as client:
138
161
  resp = await client.post(
139
162
  "https://api.anthropic.com/v1/messages",
140
- headers={
141
- "x-api-key": api_key,
142
- "anthropic-version": "2023-06-01",
143
- "content-type": "application/json",
144
- },
163
+ headers=_anthropic_headers(api_key),
145
164
  json={
146
165
  "model": config.anthropic_model,
147
166
  "max_tokens": 4096,
@@ -39,6 +39,7 @@ from clawed.commands.generate import generate_app
39
39
  from clawed.commands.queue import queue_app
40
40
  from clawed.commands.schedule_cmd import schedule_app
41
41
  from clawed.commands.sub import sub_app
42
+ from clawed.commands.train import train_app
42
43
  from clawed.commands.workspace_cmd import workspace_app
43
44
 
44
45
  # ── Build the main app ──────────────────────────────────────────────────
@@ -241,6 +242,7 @@ app.add_typer(class_app, name="class")
241
242
  app.add_typer(queue_app, name="queue")
242
243
  app.add_typer(workspace_app, name="workspace")
243
244
  app.add_typer(schedule_app, name="schedule")
245
+ app.add_typer(train_app, name="train")
244
246
 
245
247
  # ── Register top-level commands from sub-modules ────────────────────────
246
248
  # Commands from generate_app, export_app, and bot_app are registered as
@@ -298,6 +298,10 @@ def lesson(
298
298
  homework: bool = typer.Option(
299
299
  True, "--homework/--no-homework", help="Include homework"
300
300
  ),
301
+ multi_agent: bool = typer.Option(
302
+ False, "--multi-agent/--no-multi-agent",
303
+ help="Use multi-agent pipeline (researcher→writer→reviewer) for higher quality",
304
+ ),
301
305
  fmt: str = typer.Option("markdown", "--format", "-f", help="Export format: markdown, pptx, docx, pdf, handout"),
302
306
  ):
303
307
  """Generate a detailed daily lesson plan.
@@ -306,6 +310,9 @@ def lesson(
306
310
  Standalone mode (no unit plan needed):
307
311
  clawed lesson "The American Revolution" --grade 8 --subject "social studies"
308
312
 
313
+ Multi-agent mode (higher quality, 3x slower):
314
+ clawed lesson "The Missouri Compromise" -g 8 --multi-agent
315
+
309
316
  From a unit plan:
310
317
  clawed lesson "Photosynthesis" --unit-file output/unit_photosynthesis.json -n 1
311
318
  """
@@ -388,18 +395,46 @@ def lesson(
388
395
 
389
396
  with _safe_progress(console=console) as progress:
390
397
  task = progress.add_task(
391
- f"Generating lesson {lesson_num}...", total=None
398
+ f"Generating lesson {lesson_num}{' [multi-agent]' if multi_agent else ''}...",
399
+ total=None,
392
400
  )
393
401
  try:
394
- daily = _run_async(
395
- generate_lesson(
396
- lesson_number=lesson_num,
397
- unit=unit_plan,
398
- persona=persona,
399
- include_homework=homework,
400
- teacher_materials=kb_prompt_section,
402
+ if multi_agent:
403
+ # Multi-agent pipeline: researcher → writer → reviewer
404
+ from clawed.multi_agent import multi_agent_generate_master_content
405
+ from clawed.compile_teacher import compile_teacher_view
406
+ console.print("[dim]Using multi-agent pipeline (researcher→writer→reviewer)...[/dim]")
407
+ master = _run_async(
408
+ multi_agent_generate_master_content(
409
+ topic=topic,
410
+ grade=grade,
411
+ subject=subject,
412
+ persona=persona,
413
+ config=None,
414
+ unit_context=kb_prompt_section,
415
+ )
416
+ )
417
+ if master is None:
418
+ console.print("[yellow]Multi-agent returned None, falling back to single-agent...[/yellow]")
419
+ daily = _run_async(
420
+ generate_lesson(
421
+ lesson_number=lesson_num, unit=unit_plan,
422
+ persona=persona, include_homework=homework,
423
+ teacher_materials=kb_prompt_section,
424
+ )
425
+ )
426
+ else:
427
+ daily = compile_teacher_view(master)
428
+ else:
429
+ daily = _run_async(
430
+ generate_lesson(
431
+ lesson_number=lesson_num,
432
+ unit=unit_plan,
433
+ persona=persona,
434
+ include_homework=homework,
435
+ teacher_materials=kb_prompt_section,
436
+ )
401
437
  )
402
- )
403
438
  except (RuntimeError, ValueError) as e:
404
439
  console.print(f"[red]Generation failed:[/red] {e}")
405
440
  console.print("[dim]Run with --debug for full details[/dim]")
@@ -0,0 +1,258 @@
1
+ """Continuous improvement pipeline for Claw-ED fleet agents.
2
+
3
+ Usage:
4
+ clawed train --drive Ingest from configured Google Drive folders
5
+ clawed train --path PATH Ingest local materials and refine persona
6
+ clawed train --benchmark Generate & score N lessons for quality check
7
+ clawed train --full Drive ingest + benchmark in sequence
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import random
14
+ from datetime import date
15
+ from pathlib import Path
16
+ from typing import Optional
17
+
18
+ import typer
19
+ from rich.table import Table
20
+
21
+ from clawed.commands._helpers import console, run_async as _run_async
22
+
23
+ train_app = typer.Typer(help="Continuous improvement pipeline.")
24
+
25
+ _TRAINING_DIR = Path("~/.eduagent/training").expanduser()
26
+
27
+
28
+ # ── Persona refinement helper ───────────────────────────────────────────
29
+
30
+ async def _refine_persona(documents: list) -> None:
31
+ """Extract persona traits from new documents and merge into existing."""
32
+ from clawed.models import AppConfig, TeacherPersona
33
+ from clawed.persona import extract_persona, merge_persona, load_persona, save_persona
34
+ from clawed.commands._helpers import persona_path
35
+
36
+ config = AppConfig.load()
37
+ new_persona = await extract_persona(documents, config)
38
+
39
+ # Load existing persona if available
40
+ pp = persona_path()
41
+ if pp.exists():
42
+ existing = load_persona(pp)
43
+ else:
44
+ existing = TeacherPersona()
45
+
46
+ merged = merge_persona(existing, new_persona)
47
+ save_persona(merged, pp.parent)
48
+ console.print("[green]Persona updated.[/green]")
49
+
50
+
51
+ # ── Drive ingest ────────────────────────────────────────────────────────
52
+
53
+ async def _drive_ingest() -> list:
54
+ """Download and ingest from configured Drive URLs. Returns documents."""
55
+ from clawed.models import AppConfig
56
+ from clawed.drive import ingest_drive_folder
57
+ from clawed.ingestor import ingest_path
58
+
59
+ config = AppConfig.load()
60
+ drive_urls = config.teacher_profile.drive_urls
61
+
62
+ if not drive_urls:
63
+ console.print(
64
+ "[yellow]No Drive URLs configured.[/yellow] "
65
+ "Run [bold]clawed config profile[/bold] to add Google Drive folders."
66
+ )
67
+ return []
68
+
69
+ all_docs = []
70
+ for url in drive_urls:
71
+ console.print(f"[dim]Fetching:[/dim] {url}")
72
+ try:
73
+ docs = await ingest_drive_folder(url)
74
+ all_docs.extend(docs)
75
+ console.print(f" -> {len(docs)} documents ingested")
76
+ except Exception as exc:
77
+ console.print(f" [red]Error:[/red] {exc}")
78
+
79
+ if all_docs:
80
+ console.print(f"\n[green]{len(all_docs)} total documents from Drive.[/green]")
81
+ return all_docs
82
+
83
+
84
+ # ── Path ingest ─────────────────────────────────────────────────────────
85
+
86
+ def _path_ingest(path: Path) -> list:
87
+ """Ingest local files. Returns documents."""
88
+ from clawed.ingestor import ingest_path
89
+
90
+ if not path.exists():
91
+ console.print(f"[red]Path not found:[/red] {path}")
92
+ raise typer.Exit(1)
93
+
94
+ docs = ingest_path(path)
95
+ console.print(f"[green]{len(docs)} documents ingested from {path}[/green]")
96
+ return docs
97
+
98
+
99
+ # ── Benchmark ───────────────────────────────────────────────────────────
100
+
101
+ async def _run_benchmark(n: int) -> dict:
102
+ """Generate N lessons on random topics and score them. Returns report."""
103
+ from clawed.models import AppConfig, UnitPlan, LessonBrief, TeacherPersona
104
+ from clawed.lesson import generate_master_content
105
+ from clawed.quality import score_voice_match
106
+ from clawed.validation import validate_master_content
107
+ from clawed.llm import LLMClient
108
+ from clawed.commands._helpers import load_persona_or_exit
109
+
110
+ persona = load_persona_or_exit()
111
+ config = AppConfig.load()
112
+ client = LLMClient(config)
113
+
114
+ # Build a minimal unit for benchmarking
115
+ subjects = config.teacher_profile.subjects or ["General Studies"]
116
+ subject = random.choice(subjects)
117
+ grades = config.teacher_profile.grade_levels or persona.grade_levels or ["9"]
118
+ grade = random.choice(grades)
119
+
120
+ topics = [
121
+ "Causes of World War I", "Photosynthesis", "Linear Equations",
122
+ "The Civil Rights Movement", "Climate Change", "Shakespeare's Hamlet",
123
+ "The Water Cycle", "Fractions and Decimals", "Ancient Rome",
124
+ "Supply and Demand", "DNA and Genetics", "Poetry Analysis",
125
+ ]
126
+ sample_topics = random.sample(topics, min(n, len(topics)))
127
+
128
+ results = []
129
+ persona_ctx = persona.to_prompt_context()
130
+
131
+ for i, topic in enumerate(sample_topics, 1):
132
+ console.print(f"[dim]Generating lesson {i}/{n}: {topic}...[/dim]")
133
+ try:
134
+ unit = UnitPlan(
135
+ title=f"Benchmark: {topic}",
136
+ subject=subject,
137
+ grade_level=grade,
138
+ topic=topic,
139
+ duration_weeks=1,
140
+ overview=f"Benchmark lesson on {topic}",
141
+ daily_lessons=[LessonBrief(
142
+ lesson_number=1,
143
+ topic=topic,
144
+ description=f"Students will understand key aspects of {topic}",
145
+ )],
146
+ )
147
+ mc = await generate_master_content(
148
+ lesson_number=1, unit=unit, persona=persona, config=config,
149
+ )
150
+
151
+ # Score voice match
152
+ lesson_text = mc.model_dump_json()[:3000]
153
+ voice_score = await score_voice_match(lesson_text, persona_ctx, client)
154
+
155
+ # Validate content structure
156
+ errors = validate_master_content(mc, topic)
157
+ pedagogy_score = max(1.0, 5.0 - len(errors) * 0.5)
158
+ diff_score = round((voice_score + pedagogy_score) / 2, 1)
159
+ overall = round((voice_score + pedagogy_score + diff_score) / 3, 1)
160
+
161
+ results.append({
162
+ "topic": topic,
163
+ "voice": round(voice_score, 1),
164
+ "pedagogy": round(pedagogy_score, 1),
165
+ "differentiation": diff_score,
166
+ "overall": overall,
167
+ "errors": errors,
168
+ })
169
+ except Exception as exc:
170
+ console.print(f" [red]Failed:[/red] {exc}")
171
+ results.append({
172
+ "topic": topic,
173
+ "voice": 0.0, "pedagogy": 0.0,
174
+ "differentiation": 0.0, "overall": 0.0,
175
+ "errors": [str(exc)],
176
+ })
177
+
178
+ # Display table
179
+ table = Table(title=f"Benchmark Results ({len(results)} lessons)")
180
+ table.add_column("Topic", style="bold")
181
+ table.add_column("Voice", justify="right")
182
+ table.add_column("Pedagogy", justify="right")
183
+ table.add_column("Differentiation", justify="right")
184
+ table.add_column("Overall", justify="right")
185
+
186
+ for r in results:
187
+ table.add_row(
188
+ r["topic"][:20],
189
+ f"{r['voice']:.1f}",
190
+ f"{r['pedagogy']:.1f}",
191
+ f"{r['differentiation']:.1f}",
192
+ f"{r['overall']:.1f}",
193
+ )
194
+ console.print(table)
195
+
196
+ # Save report
197
+ report = {
198
+ "date": str(date.today()),
199
+ "n": len(results),
200
+ "results": results,
201
+ "avg_overall": round(
202
+ sum(r["overall"] for r in results) / max(len(results), 1), 2
203
+ ),
204
+ }
205
+ _TRAINING_DIR.mkdir(parents=True, exist_ok=True)
206
+ report_path = _TRAINING_DIR / f"{date.today()}_train_report.json"
207
+ report_path.write_text(json.dumps(report, indent=2), encoding="utf-8")
208
+ console.print(f"[dim]Report saved: {report_path}[/dim]")
209
+
210
+ return report
211
+
212
+
213
+ # ── Main command ────────────────────────────────────────────────────────
214
+
215
+ @train_app.callback(invoke_without_command=True)
216
+ def train(
217
+ drive: bool = typer.Option(False, "--drive", help="Ingest from Google Drive folders"),
218
+ path: Optional[str] = typer.Option(None, "--path", help="Ingest local materials"),
219
+ benchmark: bool = typer.Option(False, "--benchmark", help="Generate & score lessons"),
220
+ n: int = typer.Option(3, "-n", help="Number of benchmark lessons"),
221
+ full: bool = typer.Option(False, "--full", help="Run drive + benchmark"),
222
+ ) -> None:
223
+ """Continuous improvement pipeline for Claw-ED fleet agents."""
224
+
225
+ if not any([drive, path, benchmark, full]):
226
+ console.print(
227
+ "[yellow]Specify a mode:[/yellow] --drive, --path, --benchmark, or --full\n"
228
+ "Run [bold]clawed train --help[/bold] for details."
229
+ )
230
+ raise typer.Exit(0)
231
+
232
+ async def _execute():
233
+ docs = []
234
+
235
+ # Drive ingest
236
+ if drive or full:
237
+ docs = await _drive_ingest()
238
+ if docs:
239
+ await _refine_persona(docs)
240
+
241
+ # Path ingest
242
+ if path:
243
+ local_docs = _path_ingest(Path(path))
244
+ if local_docs:
245
+ await _refine_persona(local_docs)
246
+ docs.extend(local_docs)
247
+
248
+ # Benchmark
249
+ if benchmark or full:
250
+ await _run_benchmark(n)
251
+
252
+ if docs:
253
+ console.print(
254
+ f"\n[bold green]Training complete.[/bold green] "
255
+ f"{len(docs)} documents processed."
256
+ )
257
+
258
+ _run_async(_execute())
@@ -65,10 +65,35 @@ def _try_keyring_delete(key: str) -> bool:
65
65
  return False
66
66
 
67
67
 
68
+ def _resolve_claude_code_token() -> Optional[str]:
69
+ """Read fresh OAuth token from Claude Code credential store.
70
+
71
+ Claude Code auto-refreshes OAuth tokens and stores them in
72
+ ~/.claude/.credentials.json. Using this as a source means Claw-ED
73
+ never hits expired tokens when Claude Code is installed.
74
+ """
75
+ import json as _json
76
+ for path in [
77
+ Path.home() / ".claude" / ".credentials.json",
78
+ Path.home() / ".claude.json",
79
+ ]:
80
+ if path.exists():
81
+ try:
82
+ data = _json.loads(path.read_text(encoding="utf-8"))
83
+ oauth = data.get("claudeAiOauth", {})
84
+ token = oauth.get("accessToken", "")
85
+ if token and token.startswith("sk-ant-"):
86
+ return token
87
+ except (ValueError, KeyError, OSError):
88
+ continue
89
+ return None
90
+
91
+
68
92
  def get_api_key(provider: str) -> Optional[str]:
69
93
  """Retrieve an API key for the given provider.
70
94
 
71
- Priority: environment variable > keyring > secrets.json file.
95
+ Priority: environment variable > Claude Code credentials (anthropic only)
96
+ > keyring > secrets.json file.
72
97
  """
73
98
  env_map = {
74
99
  "anthropic": "ANTHROPIC_API_KEY",
@@ -86,6 +111,12 @@ def get_api_key(provider: str) -> Optional[str]:
86
111
  if val:
87
112
  return val
88
113
 
114
+ # Claude Code credential store — auto-refreshing OAuth tokens
115
+ if provider == "anthropic":
116
+ cc_token = _resolve_claude_code_token()
117
+ if cc_token:
118
+ return cc_token
119
+
89
120
  key_name = f"{provider}_api_key"
90
121
  val = _try_keyring_get(key_name)
91
122
  if val:
@@ -214,12 +245,9 @@ async def test_llm_connection(config: Optional[AppConfig] = None) -> dict:
214
245
  return _result(False, model, "No API key configured", is_err=True)
215
246
  import httpx
216
247
  try:
248
+ from clawed.agent import _anthropic_headers
217
249
  async with httpx.AsyncClient(timeout=15.0) as client:
218
- headers = {
219
- "x-api-key": api_key,
220
- "anthropic-version": "2023-06-01",
221
- "content-type": "application/json",
222
- }
250
+ headers = _anthropic_headers(api_key)
223
251
  body = {
224
252
  "model": model, "max_tokens": 5,
225
253
  "messages": [{"role": "user", "content": "Hi"}],