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.
- package/.opencode/agents/med-chat-triager.md +204 -0
- package/.opencode/agents/med-flashcard-maker.md +63 -0
- package/.opencode/agents/med-knowledge-architect.md +230 -0
- package/.opencode/agents/med-link-graph-curator.md +177 -0
- package/.opencode/agents/med-publish-guard.md +62 -0
- package/.opencode/commands/flashcards.md +25 -0
- package/.opencode/commands/mednotes/create.md +25 -0
- package/.opencode/commands/mednotes/enrich.md +27 -0
- package/.opencode/commands/mednotes/fix-wiki.md +27 -0
- package/.opencode/commands/mednotes/history.md +22 -0
- package/.opencode/commands/mednotes/link-body.md +25 -0
- package/.opencode/commands/mednotes/link-related.md +27 -0
- package/.opencode/commands/mednotes/link.md +27 -0
- package/.opencode/commands/mednotes/pdf-library.md +27 -0
- package/.opencode/commands/mednotes/process-chats.md +23 -0
- package/.opencode/commands/mednotes/setup.md +21 -0
- package/.opencode/commands/mednotes/status.md +27 -0
- package/.opencode/commands/mednotes/telemetry.md +27 -0
- package/.opencode/commands/report.md +26 -0
- package/.opencode/mednotes/AGENTS.md +57 -0
- package/.opencode/mednotes/agents/med-chat-triager.md +197 -0
- package/.opencode/mednotes/agents/med-flashcard-maker.md +56 -0
- package/.opencode/mednotes/agents/med-knowledge-architect.md +224 -0
- package/.opencode/mednotes/agents/med-link-graph-curator.md +171 -0
- package/.opencode/mednotes/agents/med-publish-guard.md +55 -0
- package/.opencode/mednotes/contracts/.gitkeep +1 -0
- package/.opencode/mednotes/contracts/agents.json +116 -0
- package/.opencode/mednotes/contracts/opencode-plugin.json +70 -0
- package/.opencode/mednotes/docs/agent-prompt-hardening.md +567 -0
- package/.opencode/mednotes/docs/agent-role-contracts.md +94 -0
- package/.opencode/mednotes/docs/anki-mcp-twenty-rules.md +214 -0
- package/.opencode/mednotes/docs/anki-templates/README.md +39 -0
- package/.opencode/mednotes/docs/anki-templates/cloze.back.html +23 -0
- package/.opencode/mednotes/docs/anki-templates/cloze.front.html +14 -0
- package/.opencode/mednotes/docs/anki-templates/qa.back.html +24 -0
- package/.opencode/mednotes/docs/anki-templates/qa.front.html +14 -0
- package/.opencode/mednotes/docs/anki-templates/style.css +182 -0
- package/.opencode/mednotes/docs/atomicity-splitting-policy.md +113 -0
- package/.opencode/mednotes/docs/extension-docs.md +40 -0
- package/.opencode/mednotes/docs/flashcard-ingestion.md +278 -0
- package/.opencode/mednotes/docs/knowledge-architect.md +208 -0
- package/.opencode/mednotes/docs/merge-policy.md +110 -0
- package/.opencode/mednotes/docs/public-vocabulary.md +104 -0
- package/.opencode/mednotes/docs/semantic-linker.md +141 -0
- package/.opencode/mednotes/docs/taxonomy-policy.md +90 -0
- package/.opencode/mednotes/docs/triage-policy.md +187 -0
- package/.opencode/mednotes/docs/vault-version-control.md +758 -0
- package/.opencode/mednotes/docs/vocabulary-db-recovery.md +58 -0
- package/.opencode/mednotes/docs/workflow-output-contract.md +779 -0
- package/.opencode/mednotes/hooks/hooks.json +79 -0
- package/.opencode/mednotes/package-lock.json +6361 -0
- package/.opencode/mednotes/package.json +15 -0
- package/.opencode/mednotes/pyproject.toml +48 -0
- package/.opencode/mednotes/scripts/bootstrap_windows_python_uv.cmd +13 -0
- package/.opencode/mednotes/scripts/bootstrap_windows_python_uv.ps1 +172 -0
- package/.opencode/mednotes/scripts/enrich_notes.py +23 -0
- package/.opencode/mednotes/scripts/full_reset_windows_python_uv.cmd +13 -0
- package/.opencode/mednotes/scripts/hooks/antigravity_hook_status.mjs +212 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/antigravity.mjs +169 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/harness_payload.mjs +103 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/opencode_plugin.mjs +341 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/adapters/opencode_user_config_sync.mjs +177 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/anki_preflight.mjs +214 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/cli.mjs +143 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/diagnostics.mjs +11 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/domain/agent_directive_core.mjs +160 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/fsm_directive.mjs +1470 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/hook_errors.mjs +120 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/retention.mjs +114 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/runtime.mjs +174 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/telemetry_capture.mjs +511 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook/vault_guard.mjs +624 -0
- package/.opencode/mednotes/scripts/hooks/mednotes_hook.mjs +5 -0
- package/.opencode/mednotes/scripts/mednotes/_runtime_paths.py +24 -0
- package/.opencode/mednotes/scripts/mednotes/anki_model_validator.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/capture_extension_diff.py +1562 -0
- package/.opencode/mednotes/scripts/mednotes/feedback_report.py +16 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_index.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_pipeline.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_report.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/flashcard_sources.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/obsidian/README.md +6 -0
- package/.opencode/mednotes/scripts/mednotes/obsidian_note_utils.py +20 -0
- package/.opencode/mednotes/scripts/mednotes/pdf_library/cli.py +16 -0
- package/.opencode/mednotes/scripts/mednotes/project_fsm.py +229 -0
- package/.opencode/mednotes/scripts/mednotes/setup_telemetry_email.py +404 -0
- package/.opencode/mednotes/scripts/mednotes/sync_anki_twenty_rules.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/sync_opencode_user_config.py +36 -0
- package/.opencode/mednotes/scripts/mednotes/wiki/cli.py +20 -0
- package/.opencode/mednotes/scripts/mednotes/wiki_graph.py +18 -0
- package/.opencode/mednotes/scripts/mednotes/wiki_tree.py +134 -0
- package/.opencode/mednotes/scripts/reset_windows_python_uv.ps1 +625 -0
- package/.opencode/mednotes/scripts/run_python.mjs +109 -0
- package/.opencode/mednotes/scripts/vault/vault_commit.ps1 +19 -0
- package/.opencode/mednotes/scripts/vault/vault_commit.sh +18 -0
- package/.opencode/mednotes/scripts/vault/vault_git.ps1 +19 -0
- package/.opencode/mednotes/scripts/vault/vault_git.py +3107 -0
- package/.opencode/mednotes/scripts/vault/vault_git.sh +18 -0
- package/.opencode/mednotes/scripts/vault/vault_precommit.ps1 +19 -0
- package/.opencode/mednotes/scripts/vault/vault_precommit.sh +18 -0
- package/.opencode/mednotes/skills/THIRD_PARTY_NOTICES.md +45 -0
- package/.opencode/mednotes/skills/create-medical-flashcards/SKILL.md +113 -0
- package/.opencode/mednotes/skills/create-medical-note/SKILL.md +90 -0
- package/.opencode/mednotes/skills/enrich-medical-note/SKILL.md +120 -0
- package/.opencode/mednotes/skills/fix-medical-wiki/SKILL.md +559 -0
- package/.opencode/mednotes/skills/link-medical-wiki/SKILL.md +224 -0
- package/.opencode/mednotes/skills/obsidian-cli/SKILL.md +118 -0
- package/.opencode/mednotes/skills/obsidian-markdown/SKILL.md +207 -0
- package/.opencode/mednotes/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
- package/.opencode/mednotes/skills/obsidian-markdown/references/EMBEDS.md +63 -0
- package/.opencode/mednotes/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
- package/.opencode/mednotes/skills/obsidian-ops/SKILL.md +136 -0
- package/.opencode/mednotes/skills/pdf-library/SKILL.md +45 -0
- package/.opencode/mednotes/skills/process-medical-chats/SKILL.md +246 -0
- package/.opencode/mednotes/skills/workflow-report/SKILL.md +100 -0
- package/.opencode/mednotes/src/mednotes/__init__.py +5 -0
- package/.opencode/mednotes/src/mednotes/domains/__init__.py +5 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/README.md +26 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/__init__.py +2 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/build_demo_apkg.py +177 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/contracts.py +385 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/flashcards_machine.py +522 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/fsm.py +817 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/index.py +630 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/install_models.py +445 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/model.py +359 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/obsidian_links.py +135 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/obsidian_note_utils.py +546 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/pipeline.py +580 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/report.py +510 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/sources.py +682 -0
- package/.opencode/mednotes/src/mednotes/domains/flashcards/sync_rules.py +184 -0
- package/.opencode/mednotes/src/mednotes/domains/history/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/history/history_fsm.py +852 -0
- package/.opencode/mednotes/src/mednotes/domains/history/history_machine.py +453 -0
- package/.opencode/mednotes/src/mednotes/domains/setup/__init__.py +7 -0
- package/.opencode/mednotes/src/mednotes/domains/setup/setup_fsm.py +808 -0
- package/.opencode/mednotes/src/mednotes/domains/setup/setup_machine.py +973 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/README.md +64 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/api.py +668 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/batch_state.py +102 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/atomicity/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/atomicity/atomicity.py +877 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/body_link/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/body_link/body_linker.py +1562 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/effect_adapters.py +949 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/effects/fix_wiki_runtime_adapters.py +433 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/coverage.py +413 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/graph.py +396 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/graph/graph_fixes.py +161 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/hygiene/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/hygiene/hygiene.py +483 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/__init__.py +2 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/anchors.py +185 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/__init__.py +0 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/cache.py +223 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/config.py +131 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/download.py +224 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/frontmatter.py +59 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/insert.py +227 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/core/local_import.py +54 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/__init__.py +42 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/web_profiles.py +99 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/web_search.py +203 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/illustrate/sources/wikimedia.py +102 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_db_adapter.mjs +434 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_node_runtime.py +274 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/markdown/markdown_query.py +227 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/artifacts.py +605 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/canonical_merge.py +277 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/markdown_zones.py +85 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/meaning_planner.py +307 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_iter.py +67 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_merge.py +278 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_plan.py +409 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_policy.py +22 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/__init__.py +79 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/fixes.py +264 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/frontmatter.py +435 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/models.py +208 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/prompts.py +37 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/tables.py +236 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/note_style/validate.py +404 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/provenance.py +478 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/raw_chats.py +273 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/notes/sources_backfill.py +235 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/__init__.py +10 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/anchors.py +16 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/captions.py +47 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/cli.py +179 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/cloud.py +52 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/config.py +196 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/context_packets.py +76 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/db.py +81 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/doctor.py +102 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/figure_ids.py +42 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/ingest.py +326 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/insert.py +316 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/mentions.py +57 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/ocr.py +71 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/paths.py +35 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/pdf_engine.py +77 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/schema.py +155 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/search.py +188 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/app.py +89 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/image_backend.py +29 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/pdf/tui/state.py +65 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish.py +1139 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish_receipts.py +365 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/publish/publish_recovery.py +240 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_behavior_corpus.py +2069 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_report_validation.py +4448 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/agent_run_audit.py +852 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/architect_prompt_eval.py +341 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/body_linker_eval.py +240 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/curator_output_validation.py +175 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/curator_prompt_eval.py +865 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/quality/triager_prompt_eval.py +1295 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/related_notes.py +1920 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/related_notes/related_notes_headless.py +1186 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/plan_attestation.py +148 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_receipts.py +360 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_runtime.py +52 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/specialist/specialist_task_runner.py +2470 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/style/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/style/style.py +1952 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/subagents/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/subagents/agents.py +1767 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/alias_projection.py +331 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/link_terms.py +151 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/llm_disambiguation.py +182 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/__init__.py +116 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/audit.py +201 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/migration.py +314 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/normalize.py +72 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/policy.py +135 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/resolve.py +413 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/schema.py +157 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/taxonomy/status.py +137 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_bootstrap.py +509 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_curator_batch.py +1115 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_ingestion.py +632 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_map.py +930 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/capabilities/vocabulary/vocabulary_recovery.py +1388 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/cli.py +6665 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/common.py +69 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/config.py +210 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/__init__.py +74 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agent_report.py +242 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agent_run_audit.py +196 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/agents.py +601 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/curator.py +256 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/effect_payloads.py +519 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/happy_path.py +190 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/link_git.py +110 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/link_runtime_artifact.py +52 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/note_plan.py +75 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/paths.py +114 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/public_report.py +53 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/publish.py +111 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/raw_coverage.py +217 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes.py +136 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes_headless.py +153 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/related_notes_runtime.py +395 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/schema_registry.py +637 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/specialist.py +432 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/status.py +62 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/style_rewrite.py +568 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/vocabulary_ingestion.py +223 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_blockers.py +510 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_guardrails.py +637 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_outcomes.py +121 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/contracts/workflow_receipts.py +100 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/__main__.py +4 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/cli.py +275 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/__init__.py +2 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/candidates.py +193 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/cli.py +189 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/gemini.py +220 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/inputs.py +120 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/models.py +34 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/parsing.py +48 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/prompts.py +216 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/quality.py +54 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/reporting.py +24 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/runner.py +433 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/utils.py +39 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/enrich/workflow/vault_guard_bridge.py +17 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_context_packets.py +454 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_decision_projection.py +133 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_effects.py +1260 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_fsm.py +2768 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_machine.py +1588 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_plan.py +306 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_primary_objective.py +316 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_problem.py +153 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_receipt_evidence.py +306 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_states.py +290 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/fix_wiki_user_report.py +342 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/fix_wiki/health.py +6332 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_fsm.py +1119 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_git.py +638 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_machine.py +1106 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_retry_governance.py +374 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_runtime_result.py +485 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/link_triggers.py +183 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/linking.py +2758 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/reference_repair.py +718 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link/related_notes_fsm.py +1855 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link_related/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/link_related/link_related_machine.py +834 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/__init__.py +1 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_fsm.py +1592 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_machine.py +3097 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_primary_objective.py +28 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/flows/process_chats/process_chats_runtime_result.py +185 -0
- package/.opencode/mednotes/src/mednotes/domains/wiki/performance.py +97 -0
- package/.opencode/mednotes/src/mednotes/kernel/__init__.py +6 -0
- package/.opencode/mednotes/src/mednotes/kernel/agent_directive.py +336 -0
- package/.opencode/mednotes/src/mednotes/kernel/base.py +51 -0
- package/.opencode/mednotes/src/mednotes/kernel/blockers.py +39 -0
- package/.opencode/mednotes/src/mednotes/kernel/effect_executor.py +55 -0
- package/.opencode/mednotes/src/mednotes/kernel/effect_intent.py +69 -0
- package/.opencode/mednotes/src/mednotes/kernel/effects.py +160 -0
- package/.opencode/mednotes/src/mednotes/kernel/errors.py +38 -0
- package/.opencode/mednotes/src/mednotes/kernel/fsm_event.py +35 -0
- package/.opencode/mednotes/src/mednotes/kernel/fsm_model.py +55 -0
- package/.opencode/mednotes/src/mednotes/kernel/fsm_transition_result.py +75 -0
- package/.opencode/mednotes/src/mednotes/kernel/guardrails.py +188 -0
- package/.opencode/mednotes/src/mednotes/kernel/progress.py +319 -0
- package/.opencode/mednotes/src/mednotes/kernel/public_report.py +346 -0
- package/.opencode/mednotes/src/mednotes/kernel/state_machine.py +164 -0
- package/.opencode/mednotes/src/mednotes/kernel/workflow.py +619 -0
- package/.opencode/mednotes/src/mednotes/platform/__init__.py +5 -0
- package/.opencode/mednotes/src/mednotes/platform/backup_policy.py +382 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/__init__.py +62 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/cli.py +275 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/contracts.py +83 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/core.py +4168 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/integrity.py +989 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/operational_contract.py +2293 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/telemetry.py +875 -0
- package/.opencode/mednotes/src/mednotes/platform/feedback/telemetry_config.py +65 -0
- package/.opencode/mednotes/src/mednotes/platform/opencode_runtime_config.py +182 -0
- package/.opencode/mednotes/src/mednotes/platform/paths/__init__.py +1560 -0
- package/.opencode/mednotes/src/mednotes/platform/secrets.py +89 -0
- package/.opencode/mednotes/src/mednotes/platform/user_config.py +103 -0
- package/.opencode/mednotes/src/mednotes/platform/vault_guard.py +214 -0
- package/.opencode/mednotes/uv.lock +932 -0
- package/.opencode/mednotes.generated.json +395 -0
- package/.opencode/opencode.json +31 -0
- package/.opencode/plugins/mednotes-fsm.mjs +7 -0
- package/.opencode/plugins/mednotes_hook/adapters/antigravity.mjs +169 -0
- package/.opencode/plugins/mednotes_hook/adapters/harness_payload.mjs +103 -0
- package/.opencode/plugins/mednotes_hook/adapters/opencode_plugin.mjs +341 -0
- package/.opencode/plugins/mednotes_hook/adapters/opencode_user_config_sync.mjs +177 -0
- package/.opencode/plugins/mednotes_hook/anki_preflight.mjs +214 -0
- package/.opencode/plugins/mednotes_hook/cli.mjs +143 -0
- package/.opencode/plugins/mednotes_hook/diagnostics.mjs +11 -0
- package/.opencode/plugins/mednotes_hook/domain/agent_directive_core.mjs +160 -0
- package/.opencode/plugins/mednotes_hook/fsm_directive.mjs +1470 -0
- package/.opencode/plugins/mednotes_hook/hook_errors.mjs +120 -0
- package/.opencode/plugins/mednotes_hook/retention.mjs +114 -0
- package/.opencode/plugins/mednotes_hook/runtime.mjs +174 -0
- package/.opencode/plugins/mednotes_hook/telemetry_capture.mjs +511 -0
- package/.opencode/plugins/mednotes_hook/vault_guard.mjs +624 -0
- package/AGENTS.md +57 -0
- package/README.md +194 -0
- package/adapters/antigravity/agents.json +80 -0
- package/adapters/antigravity/templates/med-chat-triager.md +214 -0
- package/adapters/antigravity/templates/med-flashcard-maker.md +72 -0
- package/adapters/antigravity/templates/med-knowledge-architect.md +241 -0
- package/adapters/antigravity/templates/med-link-graph-curator.md +187 -0
- package/adapters/antigravity/templates/med-publish-guard.md +71 -0
- package/adapters/gemini-cli/gemini-extension.json +14 -0
- package/adapters/gemini-cli/package.json +15 -0
- package/adapters/gemini-cli/pyproject.toml +48 -0
- package/bin/mednotes-opencode.mjs +155 -0
- package/contracts/agents.json +116 -0
- package/core/agents/med-chat-triager.md +197 -0
- package/core/agents/med-flashcard-maker.md +56 -0
- package/core/agents/med-knowledge-architect.md +224 -0
- package/core/agents/med-link-graph-curator.md +171 -0
- package/core/agents/med-publish-guard.md +55 -0
- package/core/commands/flashcards.toml +22 -0
- package/core/commands/mednotes/create.toml +22 -0
- package/core/commands/mednotes/enrich.toml +24 -0
- package/core/commands/mednotes/fix-wiki.toml +24 -0
- package/core/commands/mednotes/history.toml +19 -0
- package/core/commands/mednotes/link-body.toml +22 -0
- package/core/commands/mednotes/link-related.toml +24 -0
- package/core/commands/mednotes/link.toml +24 -0
- package/core/commands/mednotes/pdf-library.toml +24 -0
- package/core/commands/mednotes/process-chats.toml +20 -0
- package/core/commands/mednotes/setup.toml +18 -0
- package/core/commands/mednotes/status.toml +24 -0
- package/core/commands/mednotes/telemetry.toml +24 -0
- package/core/commands/report.toml +23 -0
- package/core/skills/THIRD_PARTY_NOTICES.md +45 -0
- package/core/skills/create-medical-flashcards/SKILL.md +113 -0
- package/core/skills/create-medical-note/SKILL.md +90 -0
- package/core/skills/enrich-medical-note/SKILL.md +120 -0
- package/core/skills/fix-medical-wiki/SKILL.md +559 -0
- package/core/skills/link-medical-wiki/SKILL.md +224 -0
- package/core/skills/obsidian-cli/SKILL.md +118 -0
- package/core/skills/obsidian-markdown/SKILL.md +207 -0
- package/core/skills/obsidian-markdown/references/CALLOUTS.md +58 -0
- package/core/skills/obsidian-markdown/references/EMBEDS.md +63 -0
- package/core/skills/obsidian-markdown/references/PROPERTIES.md +61 -0
- package/core/skills/obsidian-ops/SKILL.md +136 -0
- package/core/skills/pdf-library/SKILL.md +45 -0
- package/core/skills/process-medical-chats/SKILL.md +246 -0
- package/core/skills/workflow-report/SKILL.md +100 -0
- 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())
|