mednotes-opencode 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (430) hide show
  1. package/.opencode/agents/med-chat-triager.md +204 -0
  2. package/.opencode/agents/med-flashcard-maker.md +63 -0
  3. package/.opencode/agents/med-knowledge-architect.md +230 -0
  4. package/.opencode/agents/med-link-graph-curator.md +177 -0
  5. package/.opencode/agents/med-publish-guard.md +62 -0
  6. package/.opencode/commands/flashcards.md +25 -0
  7. package/.opencode/commands/mednotes/create.md +25 -0
  8. package/.opencode/commands/mednotes/enrich.md +27 -0
  9. package/.opencode/commands/mednotes/fix-wiki.md +27 -0
  10. package/.opencode/commands/mednotes/history.md +22 -0
  11. package/.opencode/commands/mednotes/link-body.md +25 -0
  12. package/.opencode/commands/mednotes/link-related.md +27 -0
  13. package/.opencode/commands/mednotes/link.md +27 -0
  14. package/.opencode/commands/mednotes/pdf-library.md +27 -0
  15. package/.opencode/commands/mednotes/process-chats.md +23 -0
  16. package/.opencode/commands/mednotes/setup.md +21 -0
  17. package/.opencode/commands/mednotes/status.md +27 -0
  18. package/.opencode/commands/mednotes/telemetry.md +27 -0
  19. package/.opencode/commands/report.md +26 -0
  20. package/.opencode/mednotes/AGENTS.md +57 -0
  21. package/.opencode/mednotes/agents/med-chat-triager.md +197 -0
  22. package/.opencode/mednotes/agents/med-flashcard-maker.md +56 -0
  23. package/.opencode/mednotes/agents/med-knowledge-architect.md +224 -0
  24. package/.opencode/mednotes/agents/med-link-graph-curator.md +171 -0
  25. package/.opencode/mednotes/agents/med-publish-guard.md +55 -0
  26. package/.opencode/mednotes/contracts/.gitkeep +1 -0
  27. package/.opencode/mednotes/contracts/agents.json +116 -0
  28. package/.opencode/mednotes/contracts/opencode-plugin.json +70 -0
  29. package/.opencode/mednotes/docs/agent-prompt-hardening.md +567 -0
  30. package/.opencode/mednotes/docs/agent-role-contracts.md +94 -0
  31. package/.opencode/mednotes/docs/anki-mcp-twenty-rules.md +214 -0
  32. package/.opencode/mednotes/docs/anki-templates/README.md +39 -0
  33. package/.opencode/mednotes/docs/anki-templates/cloze.back.html +23 -0
  34. package/.opencode/mednotes/docs/anki-templates/cloze.front.html +14 -0
  35. package/.opencode/mednotes/docs/anki-templates/qa.back.html +24 -0
  36. package/.opencode/mednotes/docs/anki-templates/qa.front.html +14 -0
  37. package/.opencode/mednotes/docs/anki-templates/style.css +182 -0
  38. package/.opencode/mednotes/docs/atomicity-splitting-policy.md +113 -0
  39. package/.opencode/mednotes/docs/extension-docs.md +40 -0
  40. package/.opencode/mednotes/docs/flashcard-ingestion.md +278 -0
  41. package/.opencode/mednotes/docs/knowledge-architect.md +208 -0
  42. package/.opencode/mednotes/docs/merge-policy.md +110 -0
  43. package/.opencode/mednotes/docs/public-vocabulary.md +104 -0
  44. package/.opencode/mednotes/docs/semantic-linker.md +141 -0
  45. package/.opencode/mednotes/docs/taxonomy-policy.md +90 -0
  46. package/.opencode/mednotes/docs/triage-policy.md +187 -0
  47. package/.opencode/mednotes/docs/vault-version-control.md +758 -0
  48. package/.opencode/mednotes/docs/vocabulary-db-recovery.md +58 -0
  49. package/.opencode/mednotes/docs/workflow-output-contract.md +779 -0
  50. package/.opencode/mednotes/hooks/hooks.json +79 -0
  51. package/.opencode/mednotes/package-lock.json +6361 -0
  52. package/.opencode/mednotes/package.json +15 -0
  53. package/.opencode/mednotes/pyproject.toml +48 -0
  54. package/.opencode/mednotes/scripts/bootstrap_windows_python_uv.cmd +13 -0
  55. package/.opencode/mednotes/scripts/bootstrap_windows_python_uv.ps1 +172 -0
  56. package/.opencode/mednotes/scripts/enrich_notes.py +23 -0
  57. package/.opencode/mednotes/scripts/full_reset_windows_python_uv.cmd +13 -0
  58. package/.opencode/mednotes/scripts/hooks/antigravity_hook_status.mjs +212 -0
  59. package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/antigravity.mjs +169 -0
  60. package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/harness_payload.mjs +103 -0
  61. package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/opencode_plugin.mjs +341 -0
  62. package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/opencode_user_config_sync.mjs +177 -0
  63. package/.opencode/mednotes/scripts/hooks/mednotes_hook/anki_preflight.mjs +214 -0
  64. package/.opencode/mednotes/scripts/hooks/mednotes_hook/cli.mjs +143 -0
  65. package/.opencode/mednotes/scripts/hooks/mednotes_hook/diagnostics.mjs +11 -0
  66. package/.opencode/mednotes/scripts/hooks/mednotes_hook/domain/agent_directive_core.mjs +160 -0
  67. package/.opencode/mednotes/scripts/hooks/mednotes_hook/fsm_directive.mjs +1470 -0
  68. package/.opencode/mednotes/scripts/hooks/mednotes_hook/hook_errors.mjs +120 -0
  69. package/.opencode/mednotes/scripts/hooks/mednotes_hook/retention.mjs +114 -0
  70. package/.opencode/mednotes/scripts/hooks/mednotes_hook/runtime.mjs +174 -0
  71. package/.opencode/mednotes/scripts/hooks/mednotes_hook/telemetry_capture.mjs +511 -0
  72. package/.opencode/mednotes/scripts/hooks/mednotes_hook/vault_guard.mjs +624 -0
  73. package/.opencode/mednotes/scripts/hooks/mednotes_hook.mjs +5 -0
  74. package/.opencode/mednotes/scripts/mednotes/_runtime_paths.py +24 -0
  75. package/.opencode/mednotes/scripts/mednotes/anki_model_validator.py +18 -0
  76. package/.opencode/mednotes/scripts/mednotes/capture_extension_diff.py +1562 -0
  77. package/.opencode/mednotes/scripts/mednotes/feedback_report.py +16 -0
  78. package/.opencode/mednotes/scripts/mednotes/flashcard_index.py +18 -0
  79. package/.opencode/mednotes/scripts/mednotes/flashcard_pipeline.py +18 -0
  80. package/.opencode/mednotes/scripts/mednotes/flashcard_report.py +18 -0
  81. package/.opencode/mednotes/scripts/mednotes/flashcard_sources.py +18 -0
  82. package/.opencode/mednotes/scripts/mednotes/obsidian/README.md +6 -0
  83. package/.opencode/mednotes/scripts/mednotes/obsidian_note_utils.py +20 -0
  84. package/.opencode/mednotes/scripts/mednotes/pdf_library/cli.py +16 -0
  85. package/.opencode/mednotes/scripts/mednotes/project_fsm.py +229 -0
  86. package/.opencode/mednotes/scripts/mednotes/setup_telemetry_email.py +404 -0
  87. package/.opencode/mednotes/scripts/mednotes/sync_anki_twenty_rules.py +18 -0
  88. package/.opencode/mednotes/scripts/mednotes/sync_opencode_user_config.py +36 -0
  89. package/.opencode/mednotes/scripts/mednotes/wiki/cli.py +20 -0
  90. package/.opencode/mednotes/scripts/mednotes/wiki_graph.py +18 -0
  91. package/.opencode/mednotes/scripts/mednotes/wiki_tree.py +134 -0
  92. package/.opencode/mednotes/scripts/reset_windows_python_uv.ps1 +625 -0
  93. package/.opencode/mednotes/scripts/run_python.mjs +109 -0
  94. package/.opencode/mednotes/scripts/vault/vault_commit.ps1 +19 -0
  95. package/.opencode/mednotes/scripts/vault/vault_commit.sh +18 -0
  96. package/.opencode/mednotes/scripts/vault/vault_git.ps1 +19 -0
  97. package/.opencode/mednotes/scripts/vault/vault_git.py +3107 -0
  98. package/.opencode/mednotes/scripts/vault/vault_git.sh +18 -0
  99. package/.opencode/mednotes/scripts/vault/vault_precommit.ps1 +19 -0
  100. package/.opencode/mednotes/scripts/vault/vault_precommit.sh +18 -0
  101. package/.opencode/mednotes/skills/THIRD_PARTY_NOTICES.md +45 -0
  102. package/.opencode/mednotes/skills/create-medical-flashcards/SKILL.md +113 -0
  103. package/.opencode/mednotes/skills/create-medical-note/SKILL.md +90 -0
  104. package/.opencode/mednotes/skills/enrich-medical-note/SKILL.md +120 -0
  105. package/.opencode/mednotes/skills/fix-medical-wiki/SKILL.md +559 -0
  106. package/.opencode/mednotes/skills/link-medical-wiki/SKILL.md +224 -0
  107. package/.opencode/mednotes/skills/obsidian-cli/SKILL.md +118 -0
  108. package/.opencode/mednotes/skills/obsidian-markdown/SKILL.md +207 -0
  109. package/.opencode/mednotes/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
  110. package/.opencode/mednotes/skills/obsidian-markdown/references/EMBEDS.md +63 -0
  111. package/.opencode/mednotes/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
  112. package/.opencode/mednotes/skills/obsidian-ops/SKILL.md +136 -0
  113. package/.opencode/mednotes/skills/pdf-library/SKILL.md +45 -0
  114. package/.opencode/mednotes/skills/process-medical-chats/SKILL.md +246 -0
  115. package/.opencode/mednotes/skills/workflow-report/SKILL.md +100 -0
  116. package/.opencode/mednotes/src/mednotes/__init__.py +5 -0
  117. package/.opencode/mednotes/src/mednotes/domains/__init__.py +5 -0
  118. package/.opencode/mednotes/src/mednotes/domains/flashcards/README.md +26 -0
  119. package/.opencode/mednotes/src/mednotes/domains/flashcards/__init__.py +2 -0
  120. package/.opencode/mednotes/src/mednotes/domains/flashcards/build_demo_apkg.py +177 -0
  121. package/.opencode/mednotes/src/mednotes/domains/flashcards/contracts.py +385 -0
  122. package/.opencode/mednotes/src/mednotes/domains/flashcards/flashcards_machine.py +522 -0
  123. package/.opencode/mednotes/src/mednotes/domains/flashcards/fsm.py +817 -0
  124. package/.opencode/mednotes/src/mednotes/domains/flashcards/index.py +630 -0
  125. package/.opencode/mednotes/src/mednotes/domains/flashcards/install_models.py +445 -0
  126. package/.opencode/mednotes/src/mednotes/domains/flashcards/model.py +359 -0
  127. package/.opencode/mednotes/src/mednotes/domains/flashcards/obsidian_links.py +135 -0
  128. package/.opencode/mednotes/src/mednotes/domains/flashcards/obsidian_note_utils.py +546 -0
  129. package/.opencode/mednotes/src/mednotes/domains/flashcards/pipeline.py +580 -0
  130. package/.opencode/mednotes/src/mednotes/domains/flashcards/report.py +510 -0
  131. package/.opencode/mednotes/src/mednotes/domains/flashcards/sources.py +682 -0
  132. package/.opencode/mednotes/src/mednotes/domains/flashcards/sync_rules.py +184 -0
  133. package/.opencode/mednotes/src/mednotes/domains/history/__init__.py +1 -0
  134. package/.opencode/mednotes/src/mednotes/domains/history/history_fsm.py +852 -0
  135. package/.opencode/mednotes/src/mednotes/domains/history/history_machine.py +453 -0
  136. package/.opencode/mednotes/src/mednotes/domains/setup/__init__.py +7 -0
  137. package/.opencode/mednotes/src/mednotes/domains/setup/setup_fsm.py +808 -0
  138. package/.opencode/mednotes/src/mednotes/domains/setup/setup_machine.py +973 -0
  139. package/.opencode/mednotes/src/mednotes/domains/wiki/README.md +64 -0
  140. package/.opencode/mednotes/src/mednotes/domains/wiki/__init__.py +1 -0
  141. package/.opencode/mednotes/src/mednotes/domains/wiki/api.py +668 -0
  142. package/.opencode/mednotes/src/mednotes/domains/wiki/batch_state.py +102 -0
  143. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/__init__.py +1 -0
  144. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/atomicity/__init__.py +1 -0
  145. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/atomicity/atomicity.py +877 -0
  146. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/body_link/__init__.py +1 -0
  147. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/body_link/body_linker.py +1562 -0
  148. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/__init__.py +1 -0
  149. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/effect_adapters.py +949 -0
  150. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/fix_wiki_runtime_adapters.py +433 -0
  151. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/__init__.py +1 -0
  152. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/coverage.py +413 -0
  153. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/graph.py +396 -0
  154. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/graph_fixes.py +161 -0
  155. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/hygiene/__init__.py +1 -0
  156. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/hygiene/hygiene.py +483 -0
  157. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/__init__.py +2 -0
  158. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/anchors.py +185 -0
  159. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/__init__.py +0 -0
  160. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/cache.py +223 -0
  161. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/config.py +131 -0
  162. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/download.py +224 -0
  163. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/frontmatter.py +59 -0
  164. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/insert.py +227 -0
  165. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/local_import.py +54 -0
  166. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/__init__.py +42 -0
  167. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/web_profiles.py +99 -0
  168. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/web_search.py +203 -0
  169. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/wikimedia.py +102 -0
  170. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/__init__.py +1 -0
  171. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_db_adapter.mjs +434 -0
  172. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_node_runtime.py +274 -0
  173. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_query.py +227 -0
  174. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/__init__.py +1 -0
  175. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/artifacts.py +605 -0
  176. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/canonical_merge.py +277 -0
  177. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/markdown_zones.py +85 -0
  178. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/meaning_planner.py +307 -0
  179. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_iter.py +67 -0
  180. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_merge.py +278 -0
  181. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_plan.py +409 -0
  182. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_policy.py +22 -0
  183. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/__init__.py +79 -0
  184. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/fixes.py +264 -0
  185. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/frontmatter.py +435 -0
  186. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/models.py +208 -0
  187. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/prompts.py +37 -0
  188. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/tables.py +236 -0
  189. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/validate.py +404 -0
  190. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/provenance.py +478 -0
  191. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/raw_chats.py +273 -0
  192. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/sources_backfill.py +235 -0
  193. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/__init__.py +10 -0
  194. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/anchors.py +16 -0
  195. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/captions.py +47 -0
  196. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/cli.py +179 -0
  197. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/cloud.py +52 -0
  198. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/config.py +196 -0
  199. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/context_packets.py +76 -0
  200. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/db.py +81 -0
  201. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/doctor.py +102 -0
  202. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/figure_ids.py +42 -0
  203. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/ingest.py +326 -0
  204. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/insert.py +316 -0
  205. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/mentions.py +57 -0
  206. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/ocr.py +71 -0
  207. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/paths.py +35 -0
  208. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/pdf_engine.py +77 -0
  209. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/schema.py +155 -0
  210. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/search.py +188 -0
  211. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/__init__.py +1 -0
  212. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/app.py +89 -0
  213. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/image_backend.py +29 -0
  214. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/state.py +65 -0
  215. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/__init__.py +1 -0
  216. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish.py +1139 -0
  217. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish_receipts.py +365 -0
  218. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish_recovery.py +240 -0
  219. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/__init__.py +1 -0
  220. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_behavior_corpus.py +2069 -0
  221. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_report_validation.py +4448 -0
  222. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_run_audit.py +852 -0
  223. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/architect_prompt_eval.py +341 -0
  224. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/body_linker_eval.py +240 -0
  225. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/curator_output_validation.py +175 -0
  226. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/curator_prompt_eval.py +865 -0
  227. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/triager_prompt_eval.py +1295 -0
  228. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/__init__.py +1 -0
  229. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/related_notes.py +1920 -0
  230. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/related_notes_headless.py +1186 -0
  231. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/__init__.py +1 -0
  232. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/plan_attestation.py +148 -0
  233. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_receipts.py +360 -0
  234. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_runtime.py +52 -0
  235. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_task_runner.py +2470 -0
  236. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/style/__init__.py +1 -0
  237. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/style/style.py +1952 -0
  238. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/subagents/__init__.py +1 -0
  239. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/subagents/agents.py +1767 -0
  240. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/__init__.py +1 -0
  241. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/alias_projection.py +331 -0
  242. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/link_terms.py +151 -0
  243. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/llm_disambiguation.py +182 -0
  244. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/__init__.py +116 -0
  245. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/audit.py +201 -0
  246. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/migration.py +314 -0
  247. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/normalize.py +72 -0
  248. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/policy.py +135 -0
  249. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/resolve.py +413 -0
  250. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/schema.py +157 -0
  251. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/status.py +137 -0
  252. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_bootstrap.py +509 -0
  253. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_curator_batch.py +1115 -0
  254. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_ingestion.py +632 -0
  255. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_map.py +930 -0
  256. package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_recovery.py +1388 -0
  257. package/.opencode/mednotes/src/mednotes/domains/wiki/cli.py +6665 -0
  258. package/.opencode/mednotes/src/mednotes/domains/wiki/common.py +69 -0
  259. package/.opencode/mednotes/src/mednotes/domains/wiki/config.py +210 -0
  260. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/__init__.py +74 -0
  261. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agent_report.py +242 -0
  262. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agent_run_audit.py +196 -0
  263. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agents.py +601 -0
  264. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/curator.py +256 -0
  265. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/effect_payloads.py +519 -0
  266. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/happy_path.py +190 -0
  267. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/link_git.py +110 -0
  268. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/link_runtime_artifact.py +52 -0
  269. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/note_plan.py +75 -0
  270. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/paths.py +114 -0
  271. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/public_report.py +53 -0
  272. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/publish.py +111 -0
  273. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/raw_coverage.py +217 -0
  274. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes.py +136 -0
  275. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes_headless.py +153 -0
  276. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes_runtime.py +395 -0
  277. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/schema_registry.py +637 -0
  278. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/specialist.py +432 -0
  279. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/status.py +62 -0
  280. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/style_rewrite.py +568 -0
  281. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/vocabulary_ingestion.py +223 -0
  282. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_blockers.py +510 -0
  283. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_guardrails.py +637 -0
  284. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_outcomes.py +121 -0
  285. package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_receipts.py +100 -0
  286. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/__init__.py +1 -0
  287. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/__init__.py +1 -0
  288. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/__main__.py +4 -0
  289. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/cli.py +275 -0
  290. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/__init__.py +2 -0
  291. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/candidates.py +193 -0
  292. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/cli.py +189 -0
  293. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/gemini.py +220 -0
  294. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/inputs.py +120 -0
  295. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/models.py +34 -0
  296. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/parsing.py +48 -0
  297. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/prompts.py +216 -0
  298. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/quality.py +54 -0
  299. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/reporting.py +24 -0
  300. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/runner.py +433 -0
  301. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/utils.py +39 -0
  302. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/vault_guard_bridge.py +17 -0
  303. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/__init__.py +1 -0
  304. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_context_packets.py +454 -0
  305. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_decision_projection.py +133 -0
  306. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_effects.py +1260 -0
  307. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_fsm.py +2768 -0
  308. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_machine.py +1588 -0
  309. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_plan.py +306 -0
  310. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_primary_objective.py +316 -0
  311. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_problem.py +153 -0
  312. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_receipt_evidence.py +306 -0
  313. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_states.py +290 -0
  314. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_user_report.py +342 -0
  315. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/health.py +6332 -0
  316. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/__init__.py +1 -0
  317. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_fsm.py +1119 -0
  318. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_git.py +638 -0
  319. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_machine.py +1106 -0
  320. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_retry_governance.py +374 -0
  321. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_runtime_result.py +485 -0
  322. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_triggers.py +183 -0
  323. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/linking.py +2758 -0
  324. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/reference_repair.py +718 -0
  325. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/related_notes_fsm.py +1855 -0
  326. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link_related/__init__.py +1 -0
  327. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link_related/link_related_machine.py +834 -0
  328. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/__init__.py +1 -0
  329. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_fsm.py +1592 -0
  330. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_machine.py +3097 -0
  331. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_primary_objective.py +28 -0
  332. package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_runtime_result.py +185 -0
  333. package/.opencode/mednotes/src/mednotes/domains/wiki/performance.py +97 -0
  334. package/.opencode/mednotes/src/mednotes/kernel/__init__.py +6 -0
  335. package/.opencode/mednotes/src/mednotes/kernel/agent_directive.py +336 -0
  336. package/.opencode/mednotes/src/mednotes/kernel/base.py +51 -0
  337. package/.opencode/mednotes/src/mednotes/kernel/blockers.py +39 -0
  338. package/.opencode/mednotes/src/mednotes/kernel/effect_executor.py +55 -0
  339. package/.opencode/mednotes/src/mednotes/kernel/effect_intent.py +69 -0
  340. package/.opencode/mednotes/src/mednotes/kernel/effects.py +160 -0
  341. package/.opencode/mednotes/src/mednotes/kernel/errors.py +38 -0
  342. package/.opencode/mednotes/src/mednotes/kernel/fsm_event.py +35 -0
  343. package/.opencode/mednotes/src/mednotes/kernel/fsm_model.py +55 -0
  344. package/.opencode/mednotes/src/mednotes/kernel/fsm_transition_result.py +75 -0
  345. package/.opencode/mednotes/src/mednotes/kernel/guardrails.py +188 -0
  346. package/.opencode/mednotes/src/mednotes/kernel/progress.py +319 -0
  347. package/.opencode/mednotes/src/mednotes/kernel/public_report.py +346 -0
  348. package/.opencode/mednotes/src/mednotes/kernel/state_machine.py +164 -0
  349. package/.opencode/mednotes/src/mednotes/kernel/workflow.py +619 -0
  350. package/.opencode/mednotes/src/mednotes/platform/__init__.py +5 -0
  351. package/.opencode/mednotes/src/mednotes/platform/backup_policy.py +382 -0
  352. package/.opencode/mednotes/src/mednotes/platform/feedback/__init__.py +62 -0
  353. package/.opencode/mednotes/src/mednotes/platform/feedback/cli.py +275 -0
  354. package/.opencode/mednotes/src/mednotes/platform/feedback/contracts.py +83 -0
  355. package/.opencode/mednotes/src/mednotes/platform/feedback/core.py +4168 -0
  356. package/.opencode/mednotes/src/mednotes/platform/feedback/integrity.py +989 -0
  357. package/.opencode/mednotes/src/mednotes/platform/feedback/operational_contract.py +2293 -0
  358. package/.opencode/mednotes/src/mednotes/platform/feedback/telemetry.py +875 -0
  359. package/.opencode/mednotes/src/mednotes/platform/feedback/telemetry_config.py +65 -0
  360. package/.opencode/mednotes/src/mednotes/platform/opencode_runtime_config.py +182 -0
  361. package/.opencode/mednotes/src/mednotes/platform/paths/__init__.py +1560 -0
  362. package/.opencode/mednotes/src/mednotes/platform/secrets.py +89 -0
  363. package/.opencode/mednotes/src/mednotes/platform/user_config.py +103 -0
  364. package/.opencode/mednotes/src/mednotes/platform/vault_guard.py +214 -0
  365. package/.opencode/mednotes/uv.lock +932 -0
  366. package/.opencode/mednotes.generated.json +395 -0
  367. package/.opencode/opencode.json +31 -0
  368. package/.opencode/plugins/mednotes-fsm.mjs +7 -0
  369. package/.opencode/plugins/mednotes_hook/adapters/antigravity.mjs +169 -0
  370. package/.opencode/plugins/mednotes_hook/adapters/harness_payload.mjs +103 -0
  371. package/.opencode/plugins/mednotes_hook/adapters/opencode_plugin.mjs +341 -0
  372. package/.opencode/plugins/mednotes_hook/adapters/opencode_user_config_sync.mjs +177 -0
  373. package/.opencode/plugins/mednotes_hook/anki_preflight.mjs +214 -0
  374. package/.opencode/plugins/mednotes_hook/cli.mjs +143 -0
  375. package/.opencode/plugins/mednotes_hook/diagnostics.mjs +11 -0
  376. package/.opencode/plugins/mednotes_hook/domain/agent_directive_core.mjs +160 -0
  377. package/.opencode/plugins/mednotes_hook/fsm_directive.mjs +1470 -0
  378. package/.opencode/plugins/mednotes_hook/hook_errors.mjs +120 -0
  379. package/.opencode/plugins/mednotes_hook/retention.mjs +114 -0
  380. package/.opencode/plugins/mednotes_hook/runtime.mjs +174 -0
  381. package/.opencode/plugins/mednotes_hook/telemetry_capture.mjs +511 -0
  382. package/.opencode/plugins/mednotes_hook/vault_guard.mjs +624 -0
  383. package/AGENTS.md +57 -0
  384. package/README.md +194 -0
  385. package/adapters/antigravity/agents.json +80 -0
  386. package/adapters/antigravity/templates/med-chat-triager.md +214 -0
  387. package/adapters/antigravity/templates/med-flashcard-maker.md +72 -0
  388. package/adapters/antigravity/templates/med-knowledge-architect.md +241 -0
  389. package/adapters/antigravity/templates/med-link-graph-curator.md +187 -0
  390. package/adapters/antigravity/templates/med-publish-guard.md +71 -0
  391. package/adapters/gemini-cli/gemini-extension.json +14 -0
  392. package/adapters/gemini-cli/package.json +15 -0
  393. package/adapters/gemini-cli/pyproject.toml +48 -0
  394. package/bin/mednotes-opencode.mjs +155 -0
  395. package/contracts/agents.json +116 -0
  396. package/core/agents/med-chat-triager.md +197 -0
  397. package/core/agents/med-flashcard-maker.md +56 -0
  398. package/core/agents/med-knowledge-architect.md +224 -0
  399. package/core/agents/med-link-graph-curator.md +171 -0
  400. package/core/agents/med-publish-guard.md +55 -0
  401. package/core/commands/flashcards.toml +22 -0
  402. package/core/commands/mednotes/create.toml +22 -0
  403. package/core/commands/mednotes/enrich.toml +24 -0
  404. package/core/commands/mednotes/fix-wiki.toml +24 -0
  405. package/core/commands/mednotes/history.toml +19 -0
  406. package/core/commands/mednotes/link-body.toml +22 -0
  407. package/core/commands/mednotes/link-related.toml +24 -0
  408. package/core/commands/mednotes/link.toml +24 -0
  409. package/core/commands/mednotes/pdf-library.toml +24 -0
  410. package/core/commands/mednotes/process-chats.toml +20 -0
  411. package/core/commands/mednotes/setup.toml +18 -0
  412. package/core/commands/mednotes/status.toml +24 -0
  413. package/core/commands/mednotes/telemetry.toml +24 -0
  414. package/core/commands/report.toml +23 -0
  415. package/core/skills/THIRD_PARTY_NOTICES.md +45 -0
  416. package/core/skills/create-medical-flashcards/SKILL.md +113 -0
  417. package/core/skills/create-medical-note/SKILL.md +90 -0
  418. package/core/skills/enrich-medical-note/SKILL.md +120 -0
  419. package/core/skills/fix-medical-wiki/SKILL.md +559 -0
  420. package/core/skills/link-medical-wiki/SKILL.md +224 -0
  421. package/core/skills/obsidian-cli/SKILL.md +118 -0
  422. package/core/skills/obsidian-markdown/SKILL.md +207 -0
  423. package/core/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
  424. package/core/skills/obsidian-markdown/references/EMBEDS.md +63 -0
  425. package/core/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
  426. package/core/skills/obsidian-ops/SKILL.md +136 -0
  427. package/core/skills/pdf-library/SKILL.md +45 -0
  428. package/core/skills/process-medical-chats/SKILL.md +246 -0
  429. package/core/skills/workflow-report/SKILL.md +100 -0
  430. package/package.json +45 -0
@@ -0,0 +1,404 @@
1
+ #!/usr/bin/env python3
2
+ """Guided maintainer setup for email-based workflow telemetry."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import getpass
7
+ import json
8
+ import os
9
+ import re
10
+ import secrets
11
+ import shutil
12
+ import subprocess
13
+ import sys
14
+ import urllib.error
15
+ import urllib.request
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ ROOT = Path(__file__).resolve().parents[2]
20
+ DISTRIBUTION_ROOT = ROOT / "extension" if (ROOT / "extension").is_dir() else ROOT
21
+ EXAMPLE_DIR = DISTRIBUTION_ROOT / "examples" / "telemetry-email-worker"
22
+ DEFAULT_HOME = Path.home() / ".gemini" / "medical-notes-workbench"
23
+ DEFAULT_WORKER_NAME = "medical-notes-workbench-telemetry"
24
+ DEFAULT_PAYLOAD_LEVEL = "trusted_extension_debug"
25
+ PAYLOAD_LEVELS = {"diagnostic_redacted", "full_logs", "trusted_extension_debug"}
26
+ REMOTE_TELEMETRY_SETUP_DISABLED = True
27
+
28
+
29
+ def main(argv: list[str] | None = None) -> int:
30
+ parser = build_parser()
31
+ args = parser.parse_args(argv)
32
+ try:
33
+ result = setup_receiver(args)
34
+ except SetupError as exc:
35
+ print(json.dumps({"ok": False, "error": str(exc), "next_action": exc.next_action}, ensure_ascii=False, indent=2))
36
+ return 2
37
+
38
+ if args.format == "json":
39
+ print(json.dumps(result, ensure_ascii=False, indent=2))
40
+ else:
41
+ print(_render_text_result(result))
42
+ return 0
43
+
44
+
45
+ def build_parser() -> argparse.ArgumentParser:
46
+ parser = argparse.ArgumentParser(
47
+ description=(
48
+ "Configure Cloudflare Worker + Resend so workflow telemetry arrives "
49
+ "as actionable emails."
50
+ )
51
+ )
52
+ parser.add_argument("--to-email", help="Email that receives telemetry reports.")
53
+ parser.add_argument("--from-email", help="Verified Resend sender, for example telemetry@your-domain.com.")
54
+ parser.add_argument("--resend-api-key", help="Resend API key. Omit to type it securely.")
55
+ parser.add_argument("--ingest-token", help="Shared ingest token. Omit to generate a strong token.")
56
+ parser.add_argument("--worker-name", default=DEFAULT_WORKER_NAME, help=f"Cloudflare Worker name. Default: {DEFAULT_WORKER_NAME}")
57
+ parser.add_argument("--payload-level", choices=sorted(PAYLOAD_LEVELS), default=DEFAULT_PAYLOAD_LEVEL)
58
+ parser.add_argument("--home", type=Path, default=DEFAULT_HOME, help="Local setup/receipt directory.")
59
+ parser.add_argument("--activate-local", action="store_true", help="Enable telemetry for this local checkout after deploy.")
60
+ parser.add_argument(
61
+ "--no-distribution-defaults",
62
+ action="store_true",
63
+ help="Compatibility no-op: distribution telemetry defaults are disabled for this project.",
64
+ )
65
+ parser.add_argument("--skip-test-email", action="store_true", help="Do not send a test telemetry email after deploy.")
66
+ parser.add_argument("--dry-run", action="store_true", help="Prepare files and print commands without calling wrangler.")
67
+ parser.add_argument("--format", choices=("text", "json"), default="text")
68
+ return parser
69
+
70
+
71
+ def setup_receiver(args: argparse.Namespace) -> dict[str, Any]:
72
+ if REMOTE_TELEMETRY_SETUP_DISABLED:
73
+ raise SetupError(
74
+ "remote telemetry setup is disabled for this project",
75
+ "Use controlled experiment reports and local workflow feedback instead of email telemetry.",
76
+ )
77
+ if not EXAMPLE_DIR.exists():
78
+ raise SetupError("worker template not found", "Check examples/telemetry-email-worker in this checkout.")
79
+ to_email = args.to_email or _prompt("Email que vai receber os reports")
80
+ from_email = args.from_email or _prompt("Remetente verificado no Resend")
81
+ resend_api_key = args.resend_api_key or os.getenv("RESEND_API_KEY") or getpass.getpass("Resend API key: ").strip()
82
+ ingest_token = args.ingest_token or secrets.token_urlsafe(32)
83
+ if not _looks_like_email(to_email):
84
+ raise SetupError("--to-email does not look like an email", "Pass a valid email address.")
85
+ if not _looks_like_sender(from_email):
86
+ raise SetupError("--from-email does not look valid", "Use a verified Resend sender such as telemetry@your-domain.com.")
87
+ if not resend_api_key:
88
+ raise SetupError("missing Resend API key", "Create a Resend API key and pass it through the prompt or --resend-api-key.")
89
+ if not ingest_token:
90
+ raise SetupError("missing ingest token", "Omit --ingest-token to generate one automatically.")
91
+
92
+ work_dir = args.home.expanduser() / "telemetry-email-worker"
93
+ receipt_path = args.home.expanduser() / "telemetry-receiver.json"
94
+ _prepare_worker_dir(work_dir, args.worker_name)
95
+
96
+ endpoint_url = ""
97
+ kv_namespace = ""
98
+ deploy_output = ""
99
+ if args.dry_run:
100
+ endpoint_url = f"https://{args.worker_name}.<your-workers-subdomain>.workers.dev/v1/telemetry/workflow-runs"
101
+ else:
102
+ _require_command("npm", "Install Node.js/npm or run this from an environment where npm is available.")
103
+ kv_namespace = _configure_digest_kv(work_dir)
104
+ _put_secret(work_dir, "INGEST_TOKEN", ingest_token)
105
+ _put_secret(work_dir, "RESEND_API_KEY", resend_api_key)
106
+ _put_secret(work_dir, "TO_EMAIL", to_email)
107
+ _put_secret(work_dir, "FROM_EMAIL", from_email)
108
+ deploy = _run(["npm", "exec", "--yes", "wrangler", "deploy"], cwd=work_dir)
109
+ deploy_output = deploy.stdout + deploy.stderr
110
+ endpoint_url = _extract_worker_url(deploy_output)
111
+ if not endpoint_url:
112
+ raise SetupError(
113
+ "could not detect Worker URL from wrangler deploy output",
114
+ "Open the Cloudflare Workers dashboard, copy the worker URL, then run telemetry enable with that endpoint.",
115
+ )
116
+
117
+ if endpoint_url and not endpoint_url.endswith("/v1/telemetry/workflow-runs"):
118
+ endpoint_url = endpoint_url.rstrip("/") + "/v1/telemetry/workflow-runs"
119
+
120
+ enable_command = _enable_command(endpoint_url=endpoint_url, token=ingest_token, payload_level=args.payload_level)
121
+ defaults_path = DISTRIBUTION_ROOT / ".telemetry-defaults.json"
122
+ result = {
123
+ "ok": True,
124
+ "worker_dir": str(work_dir),
125
+ "receipt_path": str(receipt_path),
126
+ "distribution_defaults_path": str(defaults_path),
127
+ "endpoint_url": endpoint_url,
128
+ "to_email": to_email,
129
+ "from_email": from_email,
130
+ "worker_name": args.worker_name,
131
+ "payload_level": args.payload_level,
132
+ "user_enable_command": enable_command,
133
+ "dry_run": bool(args.dry_run),
134
+ "digest_window_minutes": 60,
135
+ "digest_min_interval_minutes": 60,
136
+ "deploy_output_excerpt": deploy_output[-1200:] if deploy_output else "",
137
+ }
138
+ if not args.dry_run:
139
+ result["kv_namespace"] = kv_namespace
140
+
141
+ if not args.dry_run:
142
+ if not args.skip_test_email:
143
+ result["test_email"] = _send_test_email(endpoint_url=endpoint_url, token=ingest_token)
144
+ receipt = {**result, "ingest_token": ingest_token}
145
+ receipt_path.parent.mkdir(parents=True, exist_ok=True)
146
+ receipt_path.write_text(json.dumps(receipt, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
147
+ try:
148
+ receipt_path.chmod(0o600)
149
+ except OSError:
150
+ pass
151
+ if not args.no_distribution_defaults:
152
+ _write_distribution_defaults(
153
+ defaults_path,
154
+ endpoint_url=endpoint_url,
155
+ token=ingest_token,
156
+ payload_level=args.payload_level,
157
+ )
158
+
159
+ if args.activate_local and not args.dry_run:
160
+ activation = _run(
161
+ [
162
+ sys.executable,
163
+ str(ROOT / "scripts" / "mednotes" / "feedback_report.py"),
164
+ "telemetry",
165
+ "enable",
166
+ "--endpoint",
167
+ endpoint_url,
168
+ "--token",
169
+ ingest_token,
170
+ "--payload-level",
171
+ args.payload_level,
172
+ ],
173
+ cwd=ROOT,
174
+ )
175
+ result["local_activation"] = _try_json(activation.stdout) or {"stdout": activation.stdout.strip()}
176
+
177
+ return result
178
+
179
+
180
+ def _prepare_worker_dir(work_dir: Path, worker_name: str) -> None:
181
+ work_dir.mkdir(parents=True, exist_ok=True)
182
+ shutil.copy2(EXAMPLE_DIR / "worker.js", work_dir / "worker.js")
183
+ wrangler = (EXAMPLE_DIR / "wrangler.toml.example").read_text(encoding="utf-8")
184
+ wrangler = re.sub(r'^name = ".*"$', f'name = "{worker_name}"', wrangler, flags=re.M)
185
+ (work_dir / "wrangler.toml").write_text(wrangler, encoding="utf-8")
186
+
187
+
188
+ def _configure_digest_kv(work_dir: Path) -> dict[str, Any]:
189
+ try:
190
+ created = _run(["npm", "exec", "--yes", "wrangler", "kv", "namespace", "create", "TELEMETRY_BUFFER"], cwd=work_dir)
191
+ created_preview = _run(
192
+ ["npm", "exec", "--yes", "wrangler", "kv", "namespace", "create", "TELEMETRY_BUFFER", "--preview"],
193
+ cwd=work_dir,
194
+ )
195
+ namespace_id = _extract_kv_id(created.stdout + created.stderr)
196
+ preview_id = _extract_kv_id(created_preview.stdout + created_preview.stderr) or namespace_id
197
+ if not namespace_id:
198
+ raise SetupError(
199
+ "could not detect KV namespace id",
200
+ "Create a KV namespace manually, update wrangler.toml, then run wrangler deploy.",
201
+ )
202
+ _patch_kv_ids(work_dir / "wrangler.toml", namespace_id=namespace_id, preview_id=preview_id)
203
+ return {"ok": True, "id": namespace_id, "preview_id": preview_id}
204
+ except SetupError as exc:
205
+ raise SetupError(
206
+ "could not configure telemetry digest KV",
207
+ (
208
+ f"{exc.next_action} Sem KV o Worker cairia para email imediato por envelope, "
209
+ "o que pode estourar a quota do Resend."
210
+ ),
211
+ ) from exc
212
+
213
+
214
+ def _extract_kv_id(output: str) -> str:
215
+ match = re.search(r'id\s*=\s*"([^"]+)"', output)
216
+ if match:
217
+ return match.group(1)
218
+ match = re.search(r"\b([0-9a-f]{32})\b", output, flags=re.I)
219
+ return match.group(1) if match else ""
220
+
221
+
222
+ def _patch_kv_ids(path: Path, *, namespace_id: str, preview_id: str) -> None:
223
+ text = path.read_text(encoding="utf-8")
224
+ text = text.replace("REPLACE_WITH_KV_NAMESPACE_ID", namespace_id)
225
+ text = text.replace("REPLACE_WITH_PREVIEW_KV_NAMESPACE_ID", preview_id)
226
+ path.write_text(text, encoding="utf-8")
227
+
228
+
229
+ def _remove_kv_binding(path: Path) -> None:
230
+ text = path.read_text(encoding="utf-8")
231
+ text = re.sub(
232
+ r"\n# Optional but recommended for digest emails\..*?\[\[kv_namespaces\]\]\n.*?(?=\n# Secrets|\Z)",
233
+ "\n",
234
+ text,
235
+ flags=re.S,
236
+ )
237
+ path.write_text(text, encoding="utf-8")
238
+
239
+
240
+ def _put_secret(cwd: Path, name: str, value: str) -> None:
241
+ _run(["npm", "exec", "--yes", "wrangler", "secret", "put", name], cwd=cwd, input_text=value + "\n")
242
+
243
+
244
+ def _run(command: list[str], *, cwd: Path, input_text: str | None = None) -> subprocess.CompletedProcess[str]:
245
+ result = subprocess.run(
246
+ command,
247
+ cwd=cwd,
248
+ input=input_text,
249
+ text=True,
250
+ capture_output=True,
251
+ check=False,
252
+ )
253
+ if result.returncode != 0:
254
+ detail = (result.stderr or result.stdout).strip()
255
+ raise SetupError(
256
+ f"command failed: {' '.join(command)}",
257
+ detail[-1200:] or "Run `npm exec --yes wrangler login` and try again.",
258
+ )
259
+ return result
260
+
261
+
262
+ def _require_command(command: str, next_action: str) -> None:
263
+ if shutil.which(command) is None:
264
+ raise SetupError(f"missing command: {command}", next_action)
265
+
266
+
267
+ def _extract_worker_url(output: str) -> str:
268
+ matches = re.findall(r"https://[^\s]+?\.workers\.dev(?:/[^\s]*)?", output)
269
+ return matches[-1].rstrip(".,") if matches else ""
270
+
271
+
272
+ def _send_test_email(*, endpoint_url: str, token: str) -> dict[str, Any]:
273
+ envelope = {
274
+ "schema": "medical-notes-workbench.workflow-telemetry-envelope.v1",
275
+ "envelope_id": f"setup-test-{secrets.token_hex(8)}",
276
+ "generated_at": "setup-test",
277
+ "install_id": "setup-test",
278
+ "payload_level": DEFAULT_PAYLOAD_LEVEL,
279
+ "client": {
280
+ "app": "medical-notes-workbench",
281
+ "source": "setup_telemetry_email.py",
282
+ },
283
+ "records": [
284
+ {
285
+ "run_id": "setup-test",
286
+ "workflow": "/mednotes:telemetry",
287
+ "status": "completed",
288
+ "phase": "setup-email",
289
+ "blocked_reason": None,
290
+ "next_action": "Telemetry receiver is configured. Ignore this setup test.",
291
+ "payload_summary": {
292
+ "counts": {"test_records": 1},
293
+ "warnings": [],
294
+ },
295
+ "diagnostic_snippets": ["setup email delivery test"],
296
+ }
297
+ ],
298
+ "limits": {"max_envelope_bytes": 1048576 if DEFAULT_PAYLOAD_LEVEL == "trusted_extension_debug" else 262144},
299
+ "truncated": False,
300
+ }
301
+ body = json.dumps(envelope, ensure_ascii=False).encode("utf-8")
302
+ request = urllib.request.Request(
303
+ endpoint_url,
304
+ data=body,
305
+ headers={
306
+ "Authorization": f"Bearer {token}",
307
+ "Content-Type": "application/json",
308
+ },
309
+ method="POST",
310
+ )
311
+ try:
312
+ with urllib.request.urlopen(request, timeout=20) as response:
313
+ response_body = response.read().decode("utf-8", errors="replace")
314
+ return {"ok": True, "status": response.status, "response": _try_json(response_body) or response_body[:500]}
315
+ except urllib.error.HTTPError as exc:
316
+ detail = exc.read().decode("utf-8", errors="replace")
317
+ raise SetupError(
318
+ "test telemetry email failed",
319
+ f"HTTP {exc.code}: {detail[:800]}. Check Resend sender/domain, RESEND_API_KEY, TO_EMAIL and FROM_EMAIL.",
320
+ ) from exc
321
+ except OSError as exc:
322
+ raise SetupError(
323
+ "could not reach telemetry Worker for test email",
324
+ f"{exc}. Check network access and the Worker endpoint.",
325
+ ) from exc
326
+
327
+
328
+ def _enable_command(*, endpoint_url: str, token: str, payload_level: str) -> str:
329
+ return (
330
+ "uv run python scripts/mednotes/feedback_report.py telemetry enable "
331
+ f'--endpoint "{endpoint_url}" '
332
+ f'--token "{token}" '
333
+ f"--payload-level {payload_level}"
334
+ )
335
+
336
+
337
+ def _write_distribution_defaults(path: Path, *, endpoint_url: str, token: str, payload_level: str) -> None:
338
+ payload = {
339
+ "schema": "medical-notes-workbench.telemetry-defaults.v1",
340
+ "enabled": True,
341
+ "endpoint_url": endpoint_url,
342
+ "auth_token": token,
343
+ "payload_level": payload_level,
344
+ "max_envelope_bytes": 1048576 if payload_level == "trusted_extension_debug" else 262144,
345
+ }
346
+ path.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
347
+ try:
348
+ path.chmod(0o600)
349
+ except OSError:
350
+ pass
351
+
352
+
353
+ def _render_text_result(result: dict[str, Any]) -> str:
354
+ lines = [
355
+ "Telemetria por email configurada.",
356
+ "",
357
+ f"Endpoint: {result['endpoint_url']}",
358
+ f"Reports chegam em: {result['to_email']}",
359
+ f"Remetente: {result['from_email']}",
360
+ f"Recibo local: {result['receipt_path']}",
361
+ f"Defaults para build privado: {result['distribution_defaults_path']}",
362
+ "",
363
+ "Comando manual de override, caso algum usuário precise reativar:",
364
+ "",
365
+ result["user_enable_command"],
366
+ ]
367
+ if result.get("dry_run"):
368
+ lines.insert(1, "DRY RUN: nada foi enviado ao Cloudflare.")
369
+ if result.get("local_activation"):
370
+ lines.extend(["", "Esta instalação local já foi ativada."])
371
+ if not result.get("dry_run"):
372
+ lines.extend(["", "Builds distribuídos não autoativam telemetria neste projeto."])
373
+ return "\n".join(lines)
374
+
375
+
376
+ def _prompt(label: str) -> str:
377
+ return input(f"{label}: ").strip()
378
+
379
+
380
+ def _looks_like_email(value: str) -> bool:
381
+ return bool(re.match(r"^[^@\s]+@[^@\s]+\.[^@\s]+$", value.strip()))
382
+
383
+
384
+ def _looks_like_sender(value: str) -> bool:
385
+ match = re.search(r"<([^>]+)>", value)
386
+ email = match.group(1) if match else value
387
+ return _looks_like_email(email.strip())
388
+
389
+
390
+ def _try_json(value: str) -> Any:
391
+ try:
392
+ return json.loads(value)
393
+ except json.JSONDecodeError:
394
+ return None
395
+
396
+
397
+ class SetupError(RuntimeError):
398
+ def __init__(self, message: str, next_action: str) -> None:
399
+ super().__init__(message)
400
+ self.next_action = next_action
401
+
402
+
403
+ if __name__ == "__main__":
404
+ raise SystemExit(main())
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env python3
2
+ """Public CLI alias for syncing the vendored Anki Twenty Rules prompt."""
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ SCRIPT_DIR = Path(__file__).resolve().parent
9
+ if str(SCRIPT_DIR) not in sys.path:
10
+ sys.path.insert(0, str(SCRIPT_DIR))
11
+ from _runtime_paths import ensure_runtime_paths # noqa: E402
12
+
13
+ ensure_runtime_paths()
14
+
15
+ from mednotes.domains.flashcards.sync_rules import main # noqa: E402
16
+
17
+ if __name__ == "__main__":
18
+ raise SystemExit(main())
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ EXTENSION_ROOT = Path(__file__).resolve().parents[2]
10
+ BUNDLE_SRC = EXTENSION_ROOT / "src"
11
+ if str(BUNDLE_SRC) not in sys.path:
12
+ sys.path.insert(0, str(BUNDLE_SRC))
13
+
14
+ from mednotes.platform.opencode_runtime_config import sync_opencode_user_config # noqa: E402
15
+
16
+
17
+ def main() -> int:
18
+ parser = argparse.ArgumentParser(description="Sync MedNotes TOML model settings into an OpenCode project.")
19
+ parser.add_argument("--project", type=Path, default=Path.cwd())
20
+ parser.add_argument("--user-config", type=Path, default=None)
21
+ args = parser.parse_args()
22
+
23
+ try:
24
+ payload = sync_opencode_user_config(
25
+ project_root=args.project,
26
+ user_config_path=args.user_config,
27
+ )
28
+ except ValueError as exc:
29
+ print(str(exc), file=sys.stderr)
30
+ return 1
31
+ print(json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True))
32
+ return 0
33
+
34
+
35
+ if __name__ == "__main__":
36
+ raise SystemExit(main())
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env python3
2
+ """Entry-point fino do Wiki CLI. A lógica mora em mednotes.domains.wiki.cli.
3
+
4
+ Skills/commands invocam este caminho (scripts/mednotes/wiki/cli.py); aqui só
5
+ bootstrapamos o sys.path (bundle/src) e delegamos.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
13
+ from _runtime_paths import ensure_runtime_paths
14
+
15
+ ensure_runtime_paths()
16
+
17
+ from mednotes.domains.wiki.cli import main # noqa: E402
18
+
19
+ if __name__ == "__main__":
20
+ raise SystemExit(main())
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env python3
2
+ """Compatibility CLI alias for Wiki graph audit."""
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ SCRIPT_DIR = Path(__file__).resolve().parent
9
+ if str(SCRIPT_DIR) not in sys.path:
10
+ sys.path.insert(0, str(SCRIPT_DIR))
11
+ from _runtime_paths import ensure_runtime_paths # noqa: E402
12
+
13
+ ensure_runtime_paths()
14
+
15
+ from mednotes.domains.wiki.api import graph_main as main # noqa: E402
16
+
17
+ if __name__ == "__main__":
18
+ raise SystemExit(main())
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env python3
2
+ """Emit the current Wiki_Medicina folder tree with canonical taxonomy context."""
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ SCRIPT_DIR = Path(__file__).resolve().parent
12
+ if str(SCRIPT_DIR) not in sys.path:
13
+ sys.path.insert(0, str(SCRIPT_DIR))
14
+ from _runtime_paths import ensure_runtime_paths # noqa: E402
15
+
16
+ ensure_runtime_paths()
17
+
18
+ from mednotes.domains.wiki import api as wiki_api # noqa: E402
19
+
20
+
21
+ def build_parser() -> argparse.ArgumentParser:
22
+ parser = argparse.ArgumentParser(description="Print Wiki_Medicina taxonomy context as JSON.")
23
+ parser.add_argument("--config", help="Optional config.toml. Reads [chat_processor].")
24
+ parser.add_argument("--raw-dir", help="Override Chats_Raw directory.")
25
+ parser.add_argument("--wiki-dir", help="Override Wiki_Medicina directory.")
26
+ parser.add_argument("--catalog-path", help="Override CATALOGO_WIKI.json path.")
27
+ parser.add_argument("--max-depth", type=int, default=4, help="Current tree depth; 0 means all depths.")
28
+ parser.add_argument("--audit", action="store_true", help="Include dry-run audit against the canonical taxonomy.")
29
+ parser.add_argument("--format", choices=("json", "text"), default="json", help="Output format.")
30
+ parser.add_argument("--text", action="store_true", help="Shortcut for --format text.")
31
+ return parser
32
+
33
+
34
+ def taxonomy_context(args: argparse.Namespace) -> dict[str, Any]:
35
+ config = wiki_api.resolve_config(args)
36
+ payload = {
37
+ "wiki_dir": str(config.wiki_dir),
38
+ "canonical_taxonomy": wiki_api.canonical_taxonomy_tree(),
39
+ "current_tree": wiki_api.taxonomy_tree(config.wiki_dir, max_depth=args.max_depth),
40
+ }
41
+ if args.audit:
42
+ payload["audit"] = wiki_api.taxonomy_audit(config.wiki_dir)
43
+ return payload
44
+
45
+
46
+ def _format_canonical_taxonomy(canonical: dict[str, Any]) -> list[str]:
47
+ lines = ["Taxonomia canônica:"]
48
+ for area in canonical.get("areas", []):
49
+ lines.append(f"- {area['area']}/")
50
+ for specialty in area.get("specialties", []):
51
+ lines.append(f" - {specialty}/")
52
+ return lines
53
+
54
+
55
+ def _format_current_tree(tree: dict[str, Any]) -> list[str]:
56
+ lines = [f"Árvore atual: {tree.get('wiki_dir', '')}"]
57
+ directories = tree.get("directories", [])
58
+ if not directories:
59
+ lines.append("- <sem pastas>")
60
+ return lines
61
+ for item in directories:
62
+ parts = item.get("parts", [])
63
+ if not parts:
64
+ continue
65
+ depth = int(item.get("depth", len(parts)))
66
+ indent = " " * max(depth - 1, 0)
67
+ direct_notes = int(item.get("direct_note_count", 0))
68
+ child_dirs = int(item.get("child_dir_count", 0))
69
+ details = []
70
+ if direct_notes:
71
+ details.append(f"{direct_notes} nota{'s' if direct_notes != 1 else ''}")
72
+ if child_dirs:
73
+ details.append(f"{child_dirs} pasta{'s' if child_dirs != 1 else ''}")
74
+ suffix = f" ({', '.join(details)})" if details else ""
75
+ lines.append(f"{indent}- {parts[-1]}/{suffix}")
76
+ return lines
77
+
78
+
79
+ def _format_audit(audit: dict[str, Any]) -> list[str]:
80
+ lines = ["Auditoria dry-run:"]
81
+ summary = (
82
+ ("missing_canonical_dirs", "pastas canônicas ausentes"),
83
+ ("proposed_moves", "movimentos propostos"),
84
+ ("unmapped_top_level_dirs", "pastas de topo sem mapeamento"),
85
+ ("duplicate_destinations", "destinos duplicados"),
86
+ ("duplicate_directory_groups", "grupos duplicados"),
87
+ ("root_notes", "notas na raiz"),
88
+ )
89
+ for key, label in summary:
90
+ items = audit.get(key, [])
91
+ lines.append(f"- {label}: {len(items)}")
92
+ for item in items[:12]:
93
+ if isinstance(item, dict) and "source" in item and "destination" in item:
94
+ lines.append(f" - {item['source']} -> {item['destination']} ({item.get('reason', 'review')})")
95
+ else:
96
+ lines.append(f" - {item}")
97
+ if len(items) > 12:
98
+ lines.append(f" - ... +{len(items) - 12}")
99
+ lines.append(f"- requer revisão: {bool(audit.get('requires_review'))}")
100
+ return lines
101
+
102
+
103
+ def taxonomy_context_text(payload: dict[str, Any]) -> str:
104
+ sections = [
105
+ _format_canonical_taxonomy(payload["canonical_taxonomy"]),
106
+ _format_current_tree(payload["current_tree"]),
107
+ ]
108
+ if "audit" in payload:
109
+ sections.append(_format_audit(payload["audit"]))
110
+ return "\n\n".join("\n".join(section) for section in sections) + "\n"
111
+
112
+
113
+ def main(argv: list[str] | None = None) -> int:
114
+ parser = build_parser()
115
+ args = parser.parse_args(argv)
116
+ try:
117
+ if args.text:
118
+ args.format = "text"
119
+ payload = taxonomy_context(args)
120
+ if args.format == "text":
121
+ print(taxonomy_context_text(payload), end="")
122
+ else:
123
+ print(json.dumps(payload, ensure_ascii=False, indent=2))
124
+ return wiki_api.EXIT_OK
125
+ except wiki_api.WikiPathResolutionError as exc:
126
+ print(json.dumps(exc.payload(phase="taxonomy_context_path_resolution"), ensure_ascii=False, indent=2))
127
+ return exc.exit_code
128
+ except wiki_api.MedOpsError as exc:
129
+ print(str(exc), file=sys.stderr)
130
+ return exc.exit_code
131
+
132
+
133
+ if __name__ == "__main__":
134
+ raise SystemExit(main())