opencode-bioresearcher 1.6.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 (40) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +103 -0
  3. package/agents/bioresearcher-dr-worker.md +54 -0
  4. package/connector-meta.json +23 -0
  5. package/index.js +77 -0
  6. package/loader.js +3 -0
  7. package/package.json +42 -0
  8. package/skill-bundle.json +12 -0
  9. package/skills/bioresearcher-deep-research/SKILL.md +330 -0
  10. package/skills/bioresearcher-deep-research/references/analysis-methods.md +90 -0
  11. package/skills/bioresearcher-deep-research/references/article-literature.md +89 -0
  12. package/skills/bioresearcher-deep-research/references/best-practices.md +102 -0
  13. package/skills/bioresearcher-deep-research/references/citations.md +146 -0
  14. package/skills/bioresearcher-deep-research/references/clinical-trials.md +87 -0
  15. package/skills/bioresearcher-deep-research/references/diseases.md +94 -0
  16. package/skills/bioresearcher-deep-research/references/drugs.md +88 -0
  17. package/skills/bioresearcher-deep-research/references/ensembl-pdb.md +134 -0
  18. package/skills/bioresearcher-deep-research/references/functional-genomics.md +118 -0
  19. package/skills/bioresearcher-deep-research/references/genes.md +93 -0
  20. package/skills/bioresearcher-deep-research/references/optional-analysis.md +108 -0
  21. package/skills/bioresearcher-deep-research/references/patents.md +92 -0
  22. package/skills/bioresearcher-deep-research/references/rate-limiting-auth.md +95 -0
  23. package/skills/bioresearcher-deep-research/references/report-template.md +117 -0
  24. package/skills/bioresearcher-deep-research/references/tool-selection.md +142 -0
  25. package/skills/bioresearcher-deep-research/references/utility-config.md +116 -0
  26. package/skills/bioresearcher-deep-research/references/variants.md +109 -0
  27. package/skills/bioresearcher-deep-research/references/worker-protocol.md +110 -0
  28. package/skills/bioresearcher-deep-research/scripts/markdown-to-html.py +86 -0
  29. package/skills/bioresearcher-plot-making/SKILL.md +97 -0
  30. package/skills/bioresearcher-plot-making/references/literature-search-method-summary.md +163 -0
  31. package/skills/bioresearcher-plot-making/references/qa-gates-and-gotchas.md +156 -0
  32. package/skills/bioresearcher-plot-making/references/structural-biology-binder-visualization.md +206 -0
  33. package/skills/bioresearcher-plot-making/scripts/audit_figure_collisions.py +742 -0
  34. package/skills/bioresearcher-plot-making/scripts/audit_panel_alignment.py +935 -0
  35. package/skills/bioresearcher-plot-making/scripts/audit_pdf_text.py +152 -0
  36. package/skills/bioresearcher-plot-making/scripts/plot_helpers.py +177 -0
  37. package/skills/bioresearcher-pubmed-weekly/SKILL.md +223 -0
  38. package/skills/bioresearcher-pubmed-weekly/scripts/parse_updatefiles.py +272 -0
  39. package/skills/bioresearcher-pubmed-weekly/scripts/pubmed_weekly.py +493 -0
  40. package/skills/bioresearcher-python-setup-uv/SKILL.md +184 -0
@@ -0,0 +1,116 @@
1
+ # Utility & Configuration Tools
2
+
3
+ Cross-cutting tools: `discover`, `batch_get`, and `biomcp_configure`.
4
+
5
+ ## Overview
6
+
7
+ `discover` resolves free text to typed entities; `batch_get` fetches many
8
+ entities in parallel with per-item failure isolation; `biomcp_configure` is
9
+ the unified inspect/configure surface for optional features (database, R
10
+ analysis, biowasm) and environment parameters.
11
+
12
+ ## Tools
13
+
14
+ ### discover
15
+
16
+ | Parameter | Type | Notes |
17
+ |-----------|------|-------|
18
+ | query | string (required) | Free text, e.g. "BRAF V600E", "lung cancer", "imatinib" |
19
+
20
+ Returns entities matching the concept with their types and IDs - the right
21
+ first call when a question's entity types are ambiguous.
22
+
23
+ ### batch_get
24
+
25
+ | Parameter | Type | Notes |
26
+ |-----------|------|-------|
27
+ | inputs | array (required) | Items: `{entity, id, sections?}` where entity is one of `gene`, `variant`, `drug`, `disease`, `trial`, `article`, `patent` |
28
+
29
+ Fetches all inputs in parallel server-side; ONE failed item does not fail the
30
+ batch - the response carries per-item failure rows. Prefer this over N
31
+ sequential `*_get` calls when you already know the IDs.
32
+
33
+ ```json
34
+ {"inputs": [
35
+ {"entity": "gene", "id": "BRAF", "sections": ["core", "druggability"]},
36
+ {"entity": "trial", "id": "NCT04280705", "sections": ["core"]},
37
+ {"entity": "article", "id": "21639808"},
38
+ {"entity": "drug", "id": "vemurafenib", "sections": ["safety"]}
39
+ ]}
40
+ ```
41
+
42
+ ### biomcp_configure
43
+
44
+ | Parameter | Type | Notes |
45
+ |-----------|------|-------|
46
+ | action | `status` (default) / `set` / `reset` | status works with no other arguments |
47
+ | values | object, optional | For set: `{"<dotted file-param id>": value}` e.g. `{"features.analysis_biowasm.enabled": true}`; `null` removes a key; max 32 keys per call |
48
+ | target | string or array, optional | For reset: a feature id (`database`, `analysis_r`, `analysis_biowasm`) removes the section, or a list of dotted ids |
49
+ | filter | string, optional | For status: `file`, `env`, a feature id, or dotted-id prefix - returns detailed rows |
50
+ | dry_run | boolean, optional | Validate and diff without writing |
51
+ | confirm_sensitive | boolean, optional | Must be true when set/reset touches sensitive keys (connection targets, mirrors, credentials) - first attempts are refused by design |
52
+
53
+ Key behaviors:
54
+
55
+ - Calling with `{}` returns the status overview: per-feature running state,
56
+ config file health, conflicts, pending-restart flags, dependency
57
+ prerequisites.
58
+ - Feature groups and their env gates: `database` (DB_TYPE=mysql|sqlite),
59
+ `analysis_r` (ANALYSIS_R=1), `analysis_biowasm` (ANALYSIS_BIOWASM=1).
60
+ - REGISTRATION-TIME GATING: tool groups register at SERVER START only. After
61
+ enabling a feature (via env block or this tool's `set`, which writes
62
+ `.biomcp.json` in the server's working directory), the tools appear only
63
+ after a client RESTART - until then calls return `no_such_tool`. There is
64
+ no live activation.
65
+ - Environment-only parameters (API keys, proxy, security boundaries like
66
+ ANALYSIS_BIOWASM_DATA_DIR) are QUERY-ONLY here - the response explains how
67
+ to set them in the client env block; env values are masked (presence +
68
+ fingerprint).
69
+ - A `set` is refused (`cwd_refused`) when the server's working directory is
70
+ not a project root (e.g. cwd-less clients like Claude Desktop) - the error
71
+ carries a paste-ready env-block translation.
72
+ - `.biomcp.json` is loaded once at startup; env variables take precedence
73
+ over the file; `BIOMCP_PROJECT_CONFIG=0` disables file loading entirely.
74
+
75
+ ## Worked examples
76
+
77
+ Overall status:
78
+
79
+ ```json
80
+ {}
81
+ ```
82
+
83
+ Enable SQLite database feature:
84
+
85
+ ```json
86
+ {"action": "set", "values": {"features.database.enabled": true,
87
+ "features.database.type": "sqlite",
88
+ "features.database.sqlite_path": ["data/geo.db"]}}
89
+ ```
90
+
91
+ (the first set of sensitive keys like sqlite_path is refused - re-send the
92
+ identical call with `"confirm_sensitive": true`)
93
+
94
+ Check a feature's parameters in detail:
95
+
96
+ ```json
97
+ {"action": "status", "filter": "features.analysis_r"}
98
+ ```
99
+
100
+ ## Failure modes
101
+
102
+ | Symptom | Cause | Fix |
103
+ |---------|-------|-----|
104
+ | `no_such_tool` after enabling a feature | tools register at server start only | restart the client/session, then verify with `biomcp_configure` `{}` (`running_now`) |
105
+ | set refused: "sensitive" | sensitive key class requires confirmation | re-send identical call with `confirm_sensitive: true` |
106
+ | `cwd_refused` | server cwd is / or $HOME | use the client env block (error carries the translation) |
107
+ | batch_get item row contains `_error` | that single ID failed (typo, unknown ID) | fix the ID and re-request just that item; other items succeeded |
108
+
109
+ ## Integration notes
110
+
111
+ - Smoke-test sequence for a fresh setup: `biomcp_configure` with `{}` ->
112
+ confirm expected features `running_now` -> one cheap domain call.
113
+ - `doctor` CLI complements this: `npx -y biomcp@1.1 doctor` (exit 0 = clear);
114
+ `--client opencode` emits a paste-ready client entry.
115
+ - Multi-entity literature pulls (e.g. 10 PMIDs from article_search) belong in
116
+ ONE `batch_get` call, not 10 article_get calls.
@@ -0,0 +1,109 @@
1
+ # Variant Research
2
+
3
+ Variant search, annotation, oncology evidence, and trial cross-links via
4
+ `variant_search` / `variant_get` / `variant_oncokb` / `variant_trials`.
5
+
6
+ ## Overview
7
+
8
+ `variant_search` queries MyVariant.info with STRUCTURED parameters (gene +
9
+ protein change as separate fields - not compound free text); `variant_get`
10
+ returns per-variant annotation with sections; `variant_oncokb` adds precision
11
+ oncology evidence (token-gated); `variant_trials` links to clinical trials.
12
+
13
+ ## Tools
14
+
15
+ ### variant_search
16
+
17
+ | Parameter | Type | Notes |
18
+ |-----------|------|-------|
19
+ | query | string, optional | rsid ("rs113488022") or HGVS ("NM_004333.4:c.1799T>A"). Do NOT put compound text like "BRAF V600E" here |
20
+ | gene | string, optional | Gene symbol filter, e.g. "BRAF" - pair with hgvsp |
21
+ | hgvsp | string, optional | Protein change, e.g. "V600E" - pair with gene |
22
+ | hgvsc | string, optional | cDNA change |
23
+ | rsid | string, optional | dbSNP rsID |
24
+ | significance | enum, optional | `benign`, `likely_benign`, `pathogenic`, `likely_pathogenic`, `uncertain` (ClinVar) |
25
+ | consequence | string, optional | e.g. "missense", "synonymous" |
26
+ | max_frequency | number, optional | Max allele frequency 0-1 (gnomAD-style rarity filter) |
27
+ | min_cadd | number, optional | Minimum CADD score |
28
+ | limit | int 1-50, default 10 | Maximum results |
29
+ | offset | int >= 0, default 0 | Result offset |
30
+
31
+ ### variant_get
32
+
33
+ | Parameter | Type | Notes |
34
+ |-----------|------|-------|
35
+ | id | string (required) | rsid, HGVS, or ClinVar ID |
36
+ | sections | enum array, optional | `core`, `frequency`, `predictions`, `clinical`, `alphagenome_scores`, `all`. Core (id, gene, rsid, significance) is ALWAYS returned at top level |
37
+ | limit | int 1-100, default 20 | Caps arrays within sections |
38
+
39
+ Note: `alphagenome_scores` is currently UNAVAILABLE - it returns an error stub
40
+ pending reimplementation. Request the other sections instead.
41
+
42
+ ### variant_oncokb
43
+
44
+ | Parameter | Type | Notes |
45
+ |-----------|------|-------|
46
+ | gene | string (required) | Gene symbol, e.g. "BRAF", "EGFR" |
47
+ | protein_change | string (required) | e.g. "V600E", "L858R" |
48
+
49
+ REQUIRES the `ONCOKB_TOKEN` environment variable (register at oncokb.org).
50
+ Returns OncoKB precision-oncology levels of evidence, oncogenic status, and
51
+ therapeutics.
52
+
53
+ ### variant_trials
54
+
55
+ | Parameter | Type | Notes |
56
+ |-----------|------|-------|
57
+ | variant | string (required) | rsID, HGVS, or variant ID |
58
+
59
+ ## Worked examples
60
+
61
+ The canonical BRAF V600E lookup (structured, never free text):
62
+
63
+ ```json
64
+ {"gene": "BRAF", "hgvsp": "V600E", "limit": 5}
65
+ ```
66
+
67
+ Pathogenic missense variants in a gene, rarity-filtered:
68
+
69
+ ```json
70
+ {"gene": "TP53", "consequence": "missense",
71
+ "significance": "pathogenic", "max_frequency": 0.001, "limit": 20}
72
+ ```
73
+
74
+ By rsID directly:
75
+
76
+ ```json
77
+ {"query": "rs113488022"}
78
+ ```
79
+
80
+ Deep annotation of one variant:
81
+
82
+ ```json
83
+ {"id": "rs113488022", "sections": ["frequency", "predictions", "clinical"]}
84
+ ```
85
+
86
+ OncoKB evidence (token present):
87
+
88
+ ```json
89
+ {"gene": "EGFR", "protein_change": "L858R"}
90
+ ```
91
+
92
+ ## Failure modes
93
+
94
+ | Symptom | Cause | Fix |
95
+ |---------|-------|-----|
96
+ | variant_oncokb error mentioning token | `ONCOKB_TOKEN` not set | register/request access at oncokb.org and set it in the client env block, or skip OncoKB and rely on `variant_get` clinical section |
97
+ | 0 hits for "BRAF V600E" passed as query | compound free text is not an rsid/HGVS | use `gene` + `hgvsp` as separate parameters (the tool also auto-splits a bare "GENE V600E" string, but explicit params are reliable) |
98
+ | alphagenome_scores error stub | section currently unavailable | request `frequency`/`predictions`/`clinical` instead |
99
+ | Empty results with many filters | over-constrained combination | relax filters one at a time (drop `min_cadd` first, then `max_frequency`) |
100
+
101
+ ## Integration notes
102
+
103
+ - Chain: variant_search -> variant_get (detail) -> variant_oncokb (oncology
104
+ evidence, if token) -> variant_trials / trial_get (clinical relevance).
105
+ - For NOVEL variants (absent from databases) use `ensembl_consequence`
106
+ (VEP on demand) instead of variant_get - see references/ensembl-pdb.md.
107
+ - `discover(query="BRAF V600E")` resolves ambiguous variant text to typed
108
+ entities first when unsure.
109
+ - MyVariant is server-limited at 100 ms - no manual throttling.
@@ -0,0 +1,110 @@
1
+ # Worker Protocol
2
+
3
+ The canonical contract for per-aspect research workers - both parallel
4
+ subagents and the sequential fallback in the main conversation.
5
+
6
+ ## Overview
7
+
8
+ Each worker owns exactly ONE research aspect of a TOPIC. It queries biomcp
9
+ tools, collects identifiers, and writes one markdown file under
10
+ `reports/<TOPIC>/`. Workers never re-delegate, never fabricate, and never fall
11
+ back to internal knowledge for facts. Workers also never interview the user -
12
+ clarification is exclusively the orchestrator's Step 1 (SKILL.md).
13
+
14
+ ## Worker prompt template (orchestrator fills this in)
15
+
16
+ ```
17
+ TOPIC: <TOPIC>
18
+ YOUR RESEARCH FOCUS: <RESEARCH-ASPECT>
19
+ DESCRIPTION: <ABSTRACT>
20
+ ```
21
+
22
+ - ABSTRACT: <200 words describing the exact focus of the aspect and a list of
23
+ detailed research items to investigate.
24
+ - Tier B (generic subagent): the orchestrator should ALSO inline into the
25
+ prompt the Worker Rules below, the per-domain tool cheatsheet from
26
+ `references/tool-selection.md`, and the citation format summary from
27
+ `references/citations.md` - generic subagents may not have access to this
28
+ skill's files.
29
+ - Tier A (dedicated `bioresearcher-dr-worker` plugin subagent): the worker
30
+ reads this file plus `references/tool-selection.md` and
31
+ `references/citations.md` itself at startup (via
32
+ `${CLAUDE_PLUGIN_ROOT}`); the orchestrator sends ONLY the filled-in
33
+ template below.
34
+
35
+ ## File protocol
36
+
37
+ - Output file: `reports/<TOPIC>/<YOUR-FOCUS>.md` where `<YOUR-FOCUS>` is the
38
+ underscore-separated aspect name (e.g. `clinical_landscape.md`).
39
+ - The write tool auto-creates parent directories - never use bash mkdir.
40
+ - The file must be self-contained: a reader should understand the findings,
41
+ the tools/queries used, and the sources cited without any other context.
42
+ - File structure: title, one-paragraph scope summary, findings with in-text
43
+ citations, tool/query log (which biomcp tools + key argument values), and a
44
+ full bibliography.
45
+
46
+ ## Worker rules
47
+
48
+ 1. Stay focused: execute only the assigned aspect; do NOT delegate to other
49
+ subagents (no re-delegation), and do not expand scope.
50
+ 2. Tool selection: query biomcp tools per `references/tool-selection.md`;
51
+ filter at the source (specific terms, `limit`, `sections`) - never retrieve
52
+ broadly and filter locally.
53
+ 3. Sequential MCP calls only - never issue concurrent biomcp calls. No manual
54
+ sleep timers are needed between calls (server-side limiters pace each
55
+ source); the only exceptions are HPA `protein_atlas`/`expression` sections
56
+ and GEO supplementary downloads, which are unthrottled - space those out.
57
+ 4. No internal knowledge: use only biomcp tool results or official sources.
58
+ If evidence is missing after retries, say so explicitly in the report.
59
+ 5. Citations: every claim gets [N] references; keep a numbered bibliography in
60
+ `references/citations.md` format. Capture identifiers as you go: PMIDs,
61
+ PMCIDs, DOIs, NCT IDs, patent IDs, GEO/SRA accessions, database IDs.
62
+ 6. Retry logic: if a query fails, wait a few seconds, retry with a simpler
63
+ query; at most 3 attempts per query before recording the gap and moving on.
64
+ 7. Writing: succinct, accurate, professional - academic standard.
65
+
66
+ ## Retry ladder (per query)
67
+
68
+ ```
69
+ attempt 1: original query
70
+ fail -> wait a few seconds
71
+ attempt 2: simplified query (fewer terms, broader limit)
72
+ fail -> wait a few seconds
73
+ attempt 3: alternate tool/source (see references/tool-selection.md routing)
74
+ fail -> record "evidence gap" in the aspect file with the failed query; continue
75
+ ```
76
+
77
+ ## Parallel execution (orchestrator with subagent/Task tool)
78
+
79
+ - Pick the tier by capability: dedicated `bioresearcher-dr-worker` subagent
80
+ (Tier A) when the harness offers it; otherwise generic subagents with the
81
+ inlined cheatsheet (Tier B). Do not mix tiers within one topic.
82
+ - Launch workers in parallel in batches of up to 5.
83
+ - Track each aspect in the todo list; mark complete when its output file
84
+ exists and ends with a bibliography.
85
+ - If a worker fails or stalls, restart it (same prompt), max 3 restarts.
86
+ - Tell the user up front: "If subagents are stuck without progress for too
87
+ long, interrupt and ask me to resume work."
88
+
89
+ ## Sequential degradation (no subagent tool)
90
+
91
+ If the harness has no subagent/Task tool, the SAME protocol runs inline in the
92
+ main conversation, one aspect at a time:
93
+
94
+ 1. Announce the aspect being worked on.
95
+ 2. Apply Worker rules 2-7 exactly (same tool selection, retries, citation
96
+ discipline, file protocol).
97
+ 3. Write `reports/<TOPIC>/<ASPECT>.md` before moving to the next aspect.
98
+ 4. After the last aspect, proceed to synthesis (SKILL.md Step 5).
99
+
100
+ Sequential mode trades latency for context - keep per-aspect tool calls lean
101
+ (strict `limit`, narrow `sections`) so the accumulated context stays usable.
102
+
103
+ ## Aspect completion checklist
104
+
105
+ - [ ] Output file exists at `reports/<TOPIC>/<ASPECT>.md`
106
+ - [ ] Every claim has a citation, source note, or method note
107
+ - [ ] Bibliography present, numbered by order of appearance
108
+ - [ ] Identifiers included (PMIDs / DOIs / NCT IDs / patent IDs / accessions)
109
+ - [ ] Tool/query log included
110
+ - [ ] Evidence gaps (if any) explicitly listed
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env python3
2
+ """Convert a markdown research report to a standalone HTML file.
3
+
4
+ Usage:
5
+ uv run --with markdown python scripts/markdown-to-html.py report.md -o report.html
6
+
7
+ Dependencies:
8
+ - markdown (python-markdown), provided by `uv run --with markdown`
9
+
10
+ Wraps the rendered markdown in a minimal GitHub-like inline-CSS page so the
11
+ output is a single self-contained file with no external assets.
12
+ """
13
+
14
+ import argparse
15
+ import sys
16
+ from pathlib import Path
17
+
18
+ try:
19
+ import markdown as md_lib
20
+ except ImportError:
21
+ sys.stderr.write(
22
+ "error: the 'markdown' package is required. Run via:\n"
23
+ " uv run --with markdown python scripts/markdown-to-html.py IN.md -o OUT.html\n"
24
+ )
25
+ sys.exit(2)
26
+
27
+ CSS = """
28
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
29
+ font-size: 16px; line-height: 1.6; color: #1f2328; background: #ffffff;
30
+ max-width: 980px; margin: 0 auto; padding: 32px 44px; }
31
+ h1, h2, h3, h4, h5, h6 { font-weight: 600; line-height: 1.25; margin-top: 24px;
32
+ margin-bottom: 16px; }
33
+ h1 { font-size: 2em; border-bottom: 1px solid #d1d9e0; padding-bottom: .3em; }
34
+ h2 { font-size: 1.5em; border-bottom: 1px solid #d1d9e0; padding-bottom: .3em; }
35
+ h3 { font-size: 1.25em; }
36
+ p { margin-top: 0; margin-bottom: 16px; }
37
+ a { color: #0969da; text-decoration: none; }
38
+ a:hover { text-decoration: underline; }
39
+ code { font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
40
+ font-size: 85%; padding: .2em .4em; background: #f0f2f5; border-radius: 6px; }
41
+ pre { background: #f6f8fa; border-radius: 6px; padding: 16px; overflow: auto; }
42
+ pre code { background: transparent; padding: 0; font-size: 85%; }
43
+ blockquote { border-left: .25em solid #d1d9e0; color: #59636e; padding: 0 1em;
44
+ margin: 0 0 16px 0; }
45
+ table { border-collapse: collapse; margin-bottom: 16px; display: block;
46
+ overflow: auto; max-width: 100%; }
47
+ table th, table td { border: 1px solid #d1d9e0; padding: 6px 13px; }
48
+ table tr:nth-child(2n) { background: #f6f8fa; }
49
+ ul, ol { margin-bottom: 16px; padding-left: 2em; }
50
+ hr { height: .25em; background: #d1d9e0; border: 0; margin: 24px 0; }
51
+ """
52
+
53
+
54
+ def convert(markdown_path: Path, title: str) -> str:
55
+ text = markdown_path.read_text(encoding="utf-8")
56
+ body = md_lib.markdown(text, extensions=["tables", "fenced_code", "sane_lists"])
57
+ return (
58
+ "<!DOCTYPE html>\n"
59
+ '<html lang="en">\n<head>\n<meta charset="utf-8">\n'
60
+ f"<title>{title}</title>\n"
61
+ f"<style>{CSS}</style>\n</head>\n<body>\n{body}\n</body>\n</html>\n"
62
+ )
63
+
64
+
65
+ def main() -> None:
66
+ parser = argparse.ArgumentParser(
67
+ description="Convert a markdown file to standalone GitHub-like HTML."
68
+ )
69
+ parser.add_argument("input", help="input markdown file")
70
+ parser.add_argument("-o", "--output", help="output HTML file (default: input with .html)")
71
+ parser.add_argument("--title", help="HTML <title> (default: input filename stem)")
72
+ args = parser.parse_args()
73
+
74
+ src = Path(args.input)
75
+ if not src.is_file():
76
+ sys.stderr.write(f"error: no such file: {src}\n")
77
+ sys.exit(1)
78
+
79
+ out = Path(args.output) if args.output else src.with_suffix(".html")
80
+ title = args.title or src.stem.replace("_", " ").replace("-", " ").title()
81
+ out.write_text(convert(src, title), encoding="utf-8")
82
+ print(f"wrote {out}")
83
+
84
+
85
+ if __name__ == "__main__":
86
+ main()
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: bioresearcher-plot-making
3
+ description: "Biomedical visualization router and plotting engine: classifies research data and produces publication-ready scientific figures (structural protein-binder complexes, conformational dynamics, literature method summaries, developmental case registers, and evidence tables). Use when asked to plot, visualize, graph, or chart biomedical data, PDB complexes, binding modes, literature searches, or translational safety cases."
4
+ license: Apache-2.0
5
+ compatibility: "Any Agent Skills harness with Python 3.10+ and uv; matplotlib, pymupdf, numpy, pillow, pymol (optional for 3D)"
6
+ metadata:
7
+ version: "1.0.0"
8
+ source: "bioresearcher-skills"
9
+ allowed-tools: Bash Read Write Edit Glob Grep
10
+ ---
11
+
12
+ # Bioresearcher Plot-Making: Visual Dispatcher & Production Engine
13
+
14
+ This skill serves as the central router and quality assurance engine for
15
+ producing publication-grade scientific figures. It directs requests to
16
+ specialized domain plotting specifications, enforces declarative data
17
+ contracts, and verifies outputs against deterministic geometric QA gates.
18
+
19
+ ## 1. Decision Matrix & Routing Table
20
+
21
+ Inspect the research request and input data to select the corresponding plot
22
+ type. Immediately load the detailed specification document using `Read`:
23
+
24
+ | Generalized Plot Type | Input Data & Research Context | Reference Specification |
25
+ | :--- | :--- | :--- |
26
+ | **`structural-biology_binder-visualization`** | PDB/mmCIF coordinates, structural conformer comparisons, RMSF trajectories, multi-ligand binding modes, residue contact matrices (BSA, H-bonds, salt bridges). | [Structural Biology Guide](references/structural-biology-binder-visualization.md) |
27
+ | **`literature-search_method-summary`** | Biomedical literature syntheses, translational risk pathways, developmental drug case registers (preclinical $\to$ approved), preclinical assay detection cascades, structured evidence tables. | [Literature Summary Guide](references/literature-search-method-summary.md) |
28
+ | **`qa-gates-and-gotchas`** | Multi-panel alignment troubleshooting, vector collision audit failures, minimum glyph size compliance, hard-earned gotchas. | [QA Gates and Gotchas Guide](references/qa-gates-and-gotchas.md) |
29
+
30
+ Do not generate plotting code without loading the corresponding reference guide.
31
+
32
+ ## 2. Production Workflow
33
+
34
+ Follow this five-step workflow for all visualization requests:
35
+
36
+ ### Step 1: Classify and Load Domain Specification
37
+
38
+ Identify the appropriate archetype from the Decision Matrix above. Load the
39
+ corresponding specification via `Read` (e.g. `Read references/structural-biology-binder-visualization.md`).
40
+
41
+ ### Step 2: Ingest and Validate Declarative Data Contracts
42
+
43
+ Ensure input data conforms to the required schema before writing plotting code:
44
+ - **Structural Dynamics**: `conformer_rmsf.tsv` (`resnum`, `chain_role`, `domain`, `displacement_A`).
45
+ - **Interaction Matrix**: `binder_hotspot_matrix.tsv` (composite `<site>:<residue>` headers, triple-encoded cells `BSA_A2|HBOND|SALTBRIDGE`).
46
+ - **Case Register**: `literature_cases.tsv` (`entity_name`, `hazard_description`, `development_stage`, `status`, `pmids`).
47
+ - **Detection Cascade**: `assay_cascade.tsv` (`assay_name`, `readout`, `method_family`, `screening_stage`, `pmids`).
48
+ - **Evidence Matrix**: `evidence_table.tsv` (`citation`, `test_system`, `sample_matrix`, `analytical_method`, `readout`).
49
+
50
+ ### Step 3: Verify uv Python Environment & Dependencies
51
+
52
+ Never rely on globally installed host packages. Ensure the project-local uv
53
+ environment is configured (via `bioresearcher-python-setup-uv`) with all
54
+ required visualization libraries:
55
+
56
+ ```bash
57
+ ./uv venv .venv
58
+ VIRTUAL_ENV="$(pwd)/.venv" ./uv pip install pymol-open-source matplotlib pymupdf numpy pillow biopython pandas
59
+ ```
60
+
61
+ If `bioresearcher-python-setup-uv` already raced and exported `UV_INDEX_URL`, keep it exported — these installs inherit it automatically.
62
+
63
+ The `VIRTUAL_ENV` pin is mandatory: on hosts with an active conda/mamba environment (`CONDA_PREFIX`), bare `./uv pip install` silently targets the HOST environment instead of `./.venv`.
64
+
65
+ Never execute plotting scripts that write outputs to the workspace root:
66
+ - Create a dedicated target directory: `figures/<TARGET>/`.
67
+ - Place or generate data files directly inside `figures/<TARGET>/`.
68
+ - Ensure all Python scripts resolve relative paths via `Path(__file__).resolve().parent`.
69
+
70
+ ### Step 4: Execute Deterministic Plotting Script
71
+
72
+ Run the figure generation script using the project-local interpreter:
73
+
74
+ ```bash
75
+ ./.venv/bin/python fig1.py
76
+ # Or for standalone PyMOL scripts:
77
+ # ./.venv/bin/pymol -cq render_script.py
78
+ ```
79
+
80
+ ### Step 5: Enforce Three-Layer Quality Assurance (QA)
81
+
82
+ Certify figures before delivery:
83
+ 1. **Layer 1 (Static Source Preflight)**: Enforce standard typography (`font.family='sans-serif'`, base size 7 pt) and vector formats (`pdf.fonttype=42`, `svg.fonttype='none'`).
84
+ 2. **Layer 2 (Deterministic Geometry Gates)**:
85
+ - Run panel alignment gate (`audit_panel_alignment.py`): max deviation $\le 1.5\text{ pt}$.
86
+ - Run vector collision audit (`audit_figure_collisions.py`): 0 FAIL.
87
+ - Run font floor audit (`audit_pdf_text.py`): minimum glyph size $\ge 5.0\text{ pt}$.
88
+ 3. **Layer 3 (Vision Model Semantic Review)**: Ask focused, single-question queries to verify biological topology and icon orientation.
89
+
90
+ ## 3. Core Gotchas Summary
91
+
92
+ - **CWD Drift**: Relative save paths silently write to the process cwd; always verify outputs exist in the target directory with fresh timestamps.
93
+ - **Probe Render Trick**: Never render 3D text labels in PyMOL; extract 2D projection coordinates via a probe pass (`render_anchors.json`) and draw native vector text in Matplotlib.
94
+ - **Contact-Fragment Pruning**: Superimposing full macromolecules buries the receptor; prune ligands to residues within 8 Å plus 3-residue extension.
95
+ - **Dynamic Text Advance**: Use `right_edge()` taking `max(ink_bbox, advance_width)` to space inline text; character-count heuristics fail on proportional fonts.
96
+ - **Baseline Staggering**: Alternate column header baselines ($y=9.50$ vs $8.98$) to prevent PDF parsers from merging adjacent text runs.
97
+ - **Aspect Ratio Desync**: Panels sharing a row with `aspect='equal'` will letterbox and drift vertically unless physical aspect ratios match.