dsh-molbio-tools 0.22.2 → 0.24.1

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 (57) hide show
  1. package/CHANGELOG.md +221 -0
  2. package/README.md +51 -19
  3. package/batch.mjs +5 -1
  4. package/benchmark/README.md +7 -7
  5. package/benchmark/changed.mjs +4 -3
  6. package/benchmark/fixtures.mjs +32 -4
  7. package/benchmark/profile.mjs +1 -1
  8. package/benchmark/run.mjs +1 -0
  9. package/benchmark/sequences.mjs +1 -0
  10. package/benchmark/tasks/02-primers.json +3 -3
  11. package/benchmark/tasks/06-analysis.json +111 -1
  12. package/benchmark/tasks/09-formats.json +44 -1
  13. package/benchmark/verifications.mjs +13 -0
  14. package/build/client-bundle.mjs +1 -1
  15. package/build/client-entry.mjs +80 -1
  16. package/build/panel-core.mjs +85 -0
  17. package/build/preset-patch.mjs +1 -1
  18. package/build/release-preflight.mjs +1 -1
  19. package/charts.mjs +35 -16
  20. package/contamination.mjs +229 -0
  21. package/cordis.patch.yml +10 -6
  22. package/design.mjs +111 -78
  23. package/docs/capability-gap-survey.md +5 -0
  24. package/docs/client-panel.md +10 -8
  25. package/docs/history.md +86 -0
  26. package/docs/maintainer.md +8 -5
  27. package/docs/roadmap.md +37 -15
  28. package/docs/rules.md +5 -2
  29. package/docs/store-evidence.md +30 -23
  30. package/docs/store-preset-exemption-issue.md +1 -1
  31. package/docs/store-preset-exemption.md +1 -1
  32. package/docs/v24-plan.md +178 -0
  33. package/docs/workflow.md +11 -10
  34. package/dotplot.mjs +257 -0
  35. package/fastq-qc.mjs +31 -16
  36. package/fastq-trim.mjs +238 -0
  37. package/host.mjs +2 -2
  38. package/index.mjs +521 -34
  39. package/lib/client.js +376 -109
  40. package/package.json +8 -3
  41. package/papers.mjs +10 -0
  42. package/plot.mjs +24 -8
  43. package/preset/install.mjs +8 -0
  44. package/preset/molbio-lab/agent.cordis.yml +19 -13
  45. package/preset/molbio-lab/preset.yml +1 -1
  46. package/protein-charge.mjs +158 -0
  47. package/protein-structure.mjs +10 -2
  48. package/protein.mjs +36 -14
  49. package/records.mjs +17 -2
  50. package/seqio.mjs +49 -14
  51. package/test/charts.mjs +11 -0
  52. package/test/client.mjs +12 -5
  53. package/test/contract.mjs +44 -12
  54. package/test/drift-probe.mjs +15 -1
  55. package/test/map-card.mjs +139 -1
  56. package/test/preset-health.mjs +39 -0
  57. package/test/smoke.mjs +378 -11
package/CHANGELOG.md CHANGED
@@ -10,6 +10,227 @@
10
10
  改为按**包名**引用后模块缓存问题随之消失——没有拷贝,也就没有需要保持同步的版本目录。
11
11
  下文历史条目里出现的 `vN` 目录保持原样,作为当时的记录。
12
12
 
13
+ ## [0.24.1] — 2026-11-08(输入边界与数据安全加固;工具数不变)
14
+
15
+ **不新增工具:工具数保持 64,benchmark 保持 65 题 / 64 覆盖。** 这一版只修“已经在产品里、
16
+ 但不会报错的错”。面板包 `0.1.9 → 0.1.10`:两个客户端产物因 `seqio.mjs` /
17
+ `protein-structure.mjs` 的加固改动而重建,按仓库规则必须升版本号,否则会用同一版本发不同字节。
18
+
19
+ ### 1. 数据安全与路径语义
20
+
21
+ - `molbio_paper_export_bibtex`:自定义 `file` 不以 `.json` 结尾时,旧逻辑把库路径本身当
22
+ 输出路径,BibTeX 会**覆盖文献库**。现在始终派生独立的 `.bib`(`refs.txt` →
23
+ `refs.txt.bib`),库文件保持不变。
24
+ - 相对路径读取改为按**会话工作区**解析(`sandboxPolicy.resolve(...).workspaceRoot`),
25
+ 不再落到 provider 的 `process.cwd()`;绝对路径行为不变。
26
+ - 文献库/记录库加载时拒绝 `null` 或标量条目,返回带文件名的输入错误,而不是
27
+ `paperKey()` / `record.name.trim()` 抛出的原始 `TypeError`;协议重命名与新增使用
28
+ 同一条重名规则;`molbio_paper_update` 在标题/URL/PMID 变化后重算派生 id。
29
+ - `molbio_batch_analyze`:显式 `paths` 也受 `max_files` 约束;无法解析的路径变成一条
30
+ 错误行而不是整批失败;glob 先转义反斜杠,`.` 不再被当通配符(`a.fa` 不会匹配
31
+ `aXfa`)。
32
+
33
+ ### 2. 并发声明与大输入
34
+
35
+ - 四个总会写默认 SVG 的工具——`molbio_helical_wheel`、`molbio_hydropathy_plot`、
36
+ `molbio_protein_charge`、`molbio_dotplot`——以及带 `output_path` 的
37
+ `molbio_fasta_fastq extract` 改为 `safe: false`。旧谓词在 `output_path` 省略时
38
+ 声称并发安全,而默认文件仍会被并发调用互相覆盖。
39
+ - 图表/FASTQ/蛋白剖面不再把用户规模数组展开进 `Math.min(...)`/`Math.max(...)`:
40
+ 200,000 行表格、130,000 条 reads 与长蛋白在文档声明的上限内不再抛 `RangeError`;
41
+ FASTQ 汇总改为单次流式计数,不再物化全部 Phred 值。
42
+ - `molbio_pubmed_abstract` 的 efetch 传输失败改为具名输入错误,不再让原始异常逃出工具。
43
+
44
+ ### 3. 发布与仓库卫生
45
+
46
+ - `.github/workflows/test.yml` 增加 `permissions: contents: read`;
47
+ `.gitignore` 忽略 `.codex-tmp/`。
48
+ - `preset/install.mjs` 校验 `--profile`(字母/数字开头,仅 `._-`),避免 profile 名
49
+ 经 Windows shell 时被当作命令元字符。
50
+ - 同步多处注释里过时的工具数(57/60/62 → 64)。
51
+
52
+ ### 4. 验证与边界
53
+
54
+ - `npm test` 全绿:smoke 64 工具、benchmark 65 题 / 64 覆盖、`preset-health` 29 行、
55
+ `release` 13 项;`build/client-bundle.mjs --check` 与 `build/preset-patch.mjs --check`
56
+ 与源一致。
57
+ - 不新增工具、不改 preset 行清单、不改 DSH 基线(0.2.2-rc.1)。
58
+ - `benchmark-changed` 的 POSIX 路径断言在本地沙箱因 Node 无法 spawn `git` 未执行;
59
+ CI 会执行。这是沙箱限制,不是仓库缺陷。
60
+
61
+ ## [0.24.0] — 2026-11-05(载体/接头污染筛查;FASTQ 修剪;接头表静默错修复)
62
+
63
+ **工具数 62 → 64,benchmark 63 → 65 题(64/64 覆盖)。** 两块工作都来自候选池 survey:
64
+ #11 载体/接头污染筛查、#12 FASTQ 修剪/过滤;同时修掉 FASTQ QC 接头表里一个已有的静默错。
65
+ 面板包 `0.1.8 → 0.1.9`:客户端产物因 `seqio.mjs` 新增 `toFastq` 而改变,面板行为未变。
66
+
67
+ ### 1. `molbio_contamination_screen`(survey #11)
68
+
69
+ - 内置**短签名**表:pUC/pBR322 ori、pUC19 lacZ/M13、pUC/pBR322 bla(AmpR)、pET T7
70
+ promoter/terminator、pcDNA CMV/BGH、Gateway attB1/attB2;接头复用
71
+ Illumina Universal/Small RNA、Nextera、polyA/polyG。
72
+ - 输入单条序列或 FASTA/FASTQ;**双链**、默认 exact,`max_mismatches` 0–2 走
73
+ seed-and-verify(不扫描每个窗口);报告每个签名的 reads 数、示例位置/链/错配。
74
+ - 明确写"短签名筛查,不是 BLAST/全库比对;未命中不等于干净"。
75
+ - **顺带修的静默错**:原 `fastq-qc.mjs` 里的 Illumina Small RNA 5' 是 RNA 序列
76
+ (`GUUCAG…`,含 U),而 `adapterContent` 直接去 DNA reads 里 `indexOf`;正向分子
77
+ 永远匹配不上,只有反向互补能命中。现在表在 `contamination.mjs` 里以 DNA 形式定义
78
+ (U→T),`fastq-qc` 从那里导入并再导出;smoke 用"正向 + 反向互补"两条 read 断言两者都命中。
79
+
80
+ ### 2. `molbio_fastq_trim`(survey #12)
81
+
82
+ 按固定顺序执行:5' 引物(精确,只在读首)→ 3' 接头(精确,切到接头前)→ 3' 滑窗质量
83
+ (窗口均值低于阈值时向左收)→ poly-G/poly-A 尾 → 最小长度/N 比例过滤;支持
84
+ Phred+33/+64,写出 FASTQ 或 FASTA,返回 before/after reads/bases、逐阶段 trim 数、
85
+ drop 原因与写入路径。
86
+
87
+ - 质量字符原样保留;`toFastq` 写四行记录,index 与 `parseFastq` 往返。
88
+ - **不做** paired-end overlap merge、cutadapt 式 semiglobal adapter alignment、UMI;
89
+ 这些在描述与 README 里写明,不假装完成。
90
+ - 测试钉住:每条 read 的修剪后长度、poly-G 边界、adapter 位置、min_length 丢弃、
91
+ Phred+64、FASTQ round-trip 与错误路径。
92
+
93
+ ### 3. 计数、产物与测试
94
+
95
+ - `package.json` 0.23.0 → **0.24.0**;`files` 加入两个新模块。
96
+ - 面板包 0.1.8 → **0.1.9**;两个 `lib/client.js` 重新构建(`seqio.mjs` 新增
97
+ `toFastq` 进入浏览器半的模块图)。
98
+ - 工具注册 62 → **64**;benchmark 63 → **65** 题,覆盖 **64/64**;预设描述、
99
+ `agent.cordis.yml`、README、maintainer、rules、workflow、roadmap、survey 状态同步。
100
+ - smoke 覆盖两个新工具的已知值与错误路径,以及 Small RNA 接头双向命中的回归。
101
+ - `npm test` 全绿:smoke 64 工具、benchmark 65 题、覆盖 64/64、`contract` 17/17、
102
+ `preset-health` 29 行、`release` 13/13、`bench --offline` 65 题全过;
103
+ `build/client-bundle.mjs --check` 与 `build/preset-patch.mjs --check` 与源一致。
104
+ - **定向真实模型跑分(2026-11-05)**:`contamination-screen` 与 `fastq-trim` 两题 **2/2 通过**,
105
+ tool selection / arguments / final answer 均 2/2(409,576 tokens,70 s)。只是受影响题目的测量,
106
+ 不是全量 65 题跑分;报告在 `benchmark/reports/20261105-194348-suite.md`(reports/ 不进 git)。
107
+
108
+ ### 4. 未做 / 边界
109
+
110
+ - 短签名表不是数据库:未命中不能证明样本干净;短签名的假阳性/假阴性必须由使用者判断。
111
+ - FASTQ 修剪不做 paired-end merge、semiglobal adapter alignment、UMI;也不做
112
+ quality-score recalibration。
113
+ - `molbio_fastq_qc` 仍只读;修剪后的报告需对输出文件再跑一次 QC。
114
+
115
+ ## [0.23.0] — 2026-10-31(跨内含子引物朝向修复;调用卡;两个轻量工具;DSH 0.2.2-rc.1 适配)
116
+
117
+ **工具数 60 → 62,benchmark 61 → 63 题(62/62 覆盖)。** 三块工作:修掉路线图 2.0 的遗留
118
+ (这是本版唯一改变既有输出的修复)、落地 2.9a 的调用卡、从候选池加入两个纯计算工具。
119
+ 面板包 `0.1.7 → 0.1.8`(客户端产物变了)。
120
+
121
+ ### 1. 跨内含子引物:报告真实的分子与真实的方向(2.0 遗留)
122
+
123
+ `molbio_design_intron_primers` 一直把两条引物都报成剪接转录本的 **sense 子串**。后果有两个:
124
+ 报告的 `reverse` 不是要订购的那条分子(真正的反向引物是它的反向互补),而且 `forward` 落在
125
+ `reverse` 的下游——把这对引物按原样理解会得到一套不扩增的测定。
126
+
127
+ - **修法**:反向候选改在**剪接转录本的反向互补**上扫描,因此 GC clamp、末端稳定性、末端 GC、
128
+ self-end、发夹这些 **3' 端约束第一次落在真正会订购的那条分子上**;命中再经 `anchor`(其 3' 端,
129
+ 即结合位点的左边缘)映回 sense 坐标。正向候选仍是 sense 链上的单外显子窗口。
130
+ - **输出**:`forward.sequence` 是 sense 子串,`reverse.sequence` 是可直接下单的 5'→3' 分子;
131
+ F 在 R 上游,剪接扩增子从 F 的 5' 端到 R 的 5' 端;`exon` 只在正向引物上,
132
+ `exons`/`junction_left`/`junction_right` 只在反向引物上。
133
+ - **错配报告**:`template_base` 仍是"完美匹配时该引物会携带的碱基"——正向是转录本碱基,
134
+ 反向是它的**互补**;3' 距离与基因组坐标仍逐条给出。
135
+ - **顺带修掉的静默错**:旧代码把 `mismatchReport` 的 **snake_case** 报告直接喂给
136
+ `mismatchPenalty`(它读 camelCase 的 `distanceFrom3Prime`),所以跨内含子模式的 3' 关键区
137
+ 加罚**从未生效**。现在用 `intronMismatchPenalty` 读正确字段。
138
+ - **行为变化**:同一 fixture 的物理扩增子没有变(spliced 320-406 / 87 bp,genomic 387 bp,
139
+ junction 12+6 bp),但反向引物序列变成其反向互补,**候选集也因为 3' 约束落到正确的一端而改变**
140
+ (smoke 夹具的 exon1 seed 10 → 49,才能在新的约束下仍得到 ≥900 bp 的基因组跨度)。
141
+ - 同时更新了渲染("the REVERSE primer spans...")、工具描述、README、benchmark 的
142
+ `intron-qpcr` 措辞,以及 smoke 的 sense-substring 断言(现在断言 reverse = 其剪接位点的
143
+ 反向互补,且 F 在 R 上游)。
144
+
145
+ ### 2. 调用卡:sequence logo 与 gRNA(路线图 2.9a)
146
+
147
+ - 新增 `molbio-figure` 投影(`figureCardMeta`)与浏览器半的 `figureCardView`/
148
+ `figureCardSummary`;`FIGURE_TOOL_KEYS` 声明 `molbio_sequence_logo` 与
149
+ `molbio_grna_design` 两个 key。
150
+ - logo 卡画出本次生成的 logo;gRNA 卡画 `map_path`(请求了才有)并列出**前 5 条 guide**
151
+ (rank/序列/PAM/位置/分数/脱靶数),没有 map 时仍显示摘要与表格,而不是空卡。
152
+ - 卡片与地图卡共用 `tool.call.toolview` 座位,仍然逐 key `ctx.slots.inject`;元数据全部
153
+ 校验并限界(summary 300 字符、路径 500、guide 行 5 条、SVG ≤ 256 KB,超出则只报文件路径)。
154
+ - `test/map-card.mjs` 现在同时驱动两种卡(host 投影 → view → 真实组件渲染),并覆盖
155
+ "无图但仍有表格"、超限、坏形状、坏 SVG 与错误调用。`contract` 从产物里的两张 key 表反推
156
+ 投影数,不再假设只有地图工具。
157
+
158
+ ### 3. 新增两个工具(候选池 survey #34 / #23)
159
+
160
+ - **`molbio_protein_charge`**(`protein-charge.mjs`):Henderson-Hasselbalch 净电荷-pH
161
+ 曲线、pI、pH 7.4 电荷与 SVG。`pka_set` 可选 `bjellqvist`(默认,与 `molbio_protein_props`
162
+ 的 pI 同一张表)或 `emboss`(Epk.dat)。pK 表与电荷函数从 `protein.mjs` 导出
163
+ (`PK_SETS`/`netCharge`/`countAminoAcids`),**两张表不再各存一份**。
164
+ 未建模二硫键、修饰残基、金属结合与离子强度,输出里写明所用 pK 表。
165
+ - **`molbio_dotplot`**(`dotplot.mjs`):精确 k-mer 自比较,黑色直接重复、红色反向重复;
166
+ 固定网格分箱,`truncated`(扫描上限 200k 对)与 `plot_truncated`(网格 60k 格)分别上报,
167
+ 简并碱基参与的 k-mer 跳过并计数。是"精确 k-mer 视图",不是滑窗同一性/打分 dotplot。
168
+ SVG 画布用 720×760、绘图区仍是 628 px 正方形:x 轴标题与图例各占一行,图例落在面板内,
169
+ 不再压在底边框上;`test/smoke.mjs` 加了对应的几何断言(标题/图例的 y 预算与先后顺序)。
170
+ - 两个工具都支持 `attach_image`(画图工具 15 → 17)、`auto_view` 与工作区 SVG。
171
+ - benchmark 新增 `protein-charge` 与 `dotplot` 两题;`test/smoke.mjs` 补了已知值:
172
+ 两端电荷 ±3、pI 是零交叉、两套 pK 给出不同的 pI、dotplot 的 133/40 与 12 个反向命中、
173
+ 扫描截断、重复输入确定性、以及各自的参数错误路径。
174
+
175
+ ### 4. 计数、守卫与文档
176
+
177
+ - `package.json` 0.22.2 → **0.23.0**;`files` 加入两个新模块;面板包 0.1.7 → **0.1.8**。
178
+ - preset 描述 60 → 62;`agent.cordis.yml` 的注释同步;`cordis.patch.yml` 由
179
+ `build/preset-patch.mjs` 重新生成。
180
+ - `test/contract.mjs`:picture 工具计数 15 → 17;卡片契约改为从
181
+ `MAP_TOOL_KEYS` + `FIGURE_TOOL_KEYS` 两处读 key,并用**行首锚定**的正则数
182
+ `presentationMeta`(文件头注释里也提到这个签名,子串计数会把它算成第四个实现)。
183
+ - `test/client.mjs`:keyed 座位注册数 4 → 8 → 9(两张 tab、四张卡、详情页),
184
+ 并断言两个 figure 工具共用同一个卡片组件。
185
+ - 文档同步:README(工具表、示例、方法学、目录结构、benchmark 计数)、maintainer、
186
+ roadmap(2.0b/2.9a 标为已修、3.2 的 63/62)、rules 与 workflow 的覆盖率数字、
187
+ benchmark/README 的题数;`docs/store-preset-exemption*.md` 里残留的 60 → 62;
188
+ `benchmark/fixtures.mjs` 的 `INTRON_GENOMIC` 缩进对齐。
189
+ - `npm test` 全绿;`bench --offline` 63 题全过;`benchmark-coverage` 62/62;
190
+ `benchmark-profile` 12 行 / 62 工具;`contract` 17/17;`build/preset-patch.mjs --check`
191
+ 与 `build/client-bundle.mjs --check` 与源一致。
192
+ - **定向真实模型跑分(2026-11-05,DSH 0.2.2-rc.1)**:`intron-qpcr` / `protein-charge` /
193
+ `dotplot` 三题 **3/3 通过**,tool selection、arguments、final answer 均 3/3(541,946 tokens,
194
+ 108 s)。这只是受影响题目的测量,不是全量 63 题跑分;报告在
195
+ `benchmark/reports/20261105-190958-suite.md`(reports/ 不进 git)。
196
+
197
+ ### 5. 未做 / 边界
198
+
199
+ - 跨内含子工具**不**补"正向引物跨连接点"的另一种布局:本版只把已有布局的分子与方向修正,
200
+ 物理扩增子不变。要支持两种布局需要把"哪条引物跨连接点"变成一个显式选项与两套搜索。
201
+ - `molbio_dotplot` 只做自身比较;两个序列之间的 dotplot 与滑窗打分版没有做。
202
+ - `molbio_protein_charge` 不建模二硫键、翻译后修饰、金属结合与离子强度;pI 与厂家计算器
203
+ 不可互换(这与 README 的方法学一致)。
204
+ - 路线图 2.9b(文献库写回)、2.9c(结构预览)、2.6(基因组级脱靶)仍未做。
205
+
206
+ ### 6. DSH 0.2.2-rc.1 适配
207
+
208
+ **工具行为零变化(62 个);变的是 preset 行清单与守卫。** 上游 0.2.2-rc.1 把 `list_agents` 并进
209
+ `@deepseek-ai/dsh-tool-subagent-control` 主入口,并删除了 `./list-agents` 子路径导出;本仓库的 preset
210
+ 还在按 0.2.1-rc.5 的 standard 抄这一行,于是 rc.1 上该行**按 specifier 解析即失败**
211
+ (`ERR_PACKAGE_PATH_NOT_EXPORTED`),`molbio-lab` 会被报告为 broken,而组合树静态看完全正常。
212
+
213
+ - `preset/molbio-lab/agent.cordis.yml`:删掉 `tool-subagent-list-agents` 行(30 → 29 行);
214
+ `tool-subagent` 的旧键 `modelSelectionSettings: true` 换成 0.2.2 的
215
+ `modelSelection: true` + `modelDiscovery: true`(旧键仍被 schema 接受但**静默忽略**,子代理的
216
+ 模型选择实际上被关掉了);`tool-fs` 显式补上游新写的 `applyPatch: false`(包默认值同为 false,
217
+ 补上是为了让两份 composition 逐行可比)。`cordis.patch.yml` 由 `build/preset-patch.mjs` 重新生成。
218
+ - `test/preset-health.mjs` 新增**真实 specifier 解析**(`createRequire(...).resolve`)前置检查:
219
+ `packageInstalled` 只证明包目录存在,会放过"包还在、子路径已删"的行——这正是 rc.5 → rc.1
220
+ 的漂移能一路绿到手工发现的原因。`test/drift-probe.mjs` 增加一条突变自检(用 `no-such-subpath`
221
+ 证明守卫会失败,不依赖某个版本删了什么)。
222
+ - `test/contract.mjs` 三处由实现字面量改回契约:官方 tab body 改名 `FileNavigator` 后改为校验
223
+ **入参**(仍收 `useTabInfo`/`sessionId`/`useSessions`);`presentationMeta` 投影改读
224
+ `dsh-tools` 的 `lib/index.js` + `lib/pipeline.js`(0.2.2 把装配管线拆了出去,语义未变);
225
+ `dsh-fs` README 的 binary 措辞改为接受新旧两种说法,真正的判断仍由 `FS_NOT_TEXT` 与
226
+ "seam 上没有 `writeBytes`"承担。
227
+ - 两个包 `dshReleases` 增加 `0.2.2-rc.1: compatible`;`engines.dsh` 不变——DSH 的
228
+ `unmetRequirements` 用 `includePrerelease`,`>=0.2.1-rc.2 <0.3.0-0` 本来就覆盖 0.2.2-rc.1,
229
+ 而 `-0` 上界继续挡住 `0.3.0-*`。
230
+ - 一次性 Profile(tarball,117 文件 / 1 049 323 B)在 0.2.2-rc.1 上重跑:安装 → 启动 HTTP 200 →
231
+ `agentPresets/list` 的 `broken` 为空、`molbio-lab` 的 `tool-molbio` 为 `fiberPhase: "active"` →
232
+ 卸载无残留;`contract` 17 / `preset-health` 29 行 / `drift-probe` 15 个突变 / `smoke` 62 工具全绿。
233
+
13
234
  ## [0.22.2] — 2026-10-30(DSH 0.2.1-rc.5 适配;契约守卫兼容桌面版)
14
235
 
15
236
  **工具行为零变化(60 个)。** 0.22.1 的验收基线是 **0.2.1-rc.3**;这一版把基线推到
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # dsh-molbio-tools
2
2
 
3
- **给 AI 助手装上分子生物学的"手"**:一个零依赖的 DeepSeek Harness 插件包,为它提供 **60 个
3
+ **给 AI 助手装上分子生物学的"手"**:一个零依赖的 DeepSeek Harness 插件包,为它提供 **64 个
4
4
  `molbio_*` 工具**——序列分析、引物/探针自动设计、克隆模拟、质粒图谱、测序验证、蛋白与 CRISPR
5
5
  分析、qPCR 统计、文献与实验记录。装上之后,你可以直接用自然语言让助手完成这些计算,并把结果
6
6
  画成图。
@@ -31,7 +31,7 @@
31
31
  ## 目录
32
32
 
33
33
  - [安装](#安装)(3 步)
34
- - [它会做什么](#它会做什么)(按任务分组的 60 个工具)
34
+ - [它会做什么](#它会做什么)(按任务分组的 64 个工具)
35
35
  - [典型用法示例](#典型用法示例)
36
36
  - [输出文件、自动打开与浏览器面板](#输出文件自动打开与浏览器面板)
37
37
  - [方法学与使用须知](#方法学与使用须知)(各工具的模型、估算范围与边界)
@@ -46,7 +46,7 @@
46
46
 
47
47
  ### 推荐:在 DSH 插件页安装(3 步)
48
48
 
49
- 装完后,预设选择器里会出现 **Molecular Biology Lab** 模式;**60 个工具只在这个模式里出现**,
49
+ 装完后,预设选择器里会出现 **Molecular Biology Lab** 模式;**64 个工具只在这个模式里出现**,
50
50
  不会污染你其它会话(这点很重要:工具多了会占提示预算)。**右栏的 Molbio / Papers 两个面板也随这个包
51
51
  一起装好**,不需要再装 `dsh-molbio-panel`。
52
52
 
@@ -104,9 +104,9 @@ dsh plugin --profile web update dsh-molbio-tools
104
104
  | 层(同一个 `cordis.patch.yml` 里的两段) | 作用 |
105
105
  | --- | --- |
106
106
  | `molbio-client` → `./host.mjs` | 只插一行**惰性宿主锚点**,不注册任何工具 |
107
- | `preset-molbio-lab` → `@deepseek-ai/dsh-agent-preset` | 声明 **Molecular Biology Lab** 模式,60 个工具在这一层里 |
107
+ | `preset-molbio-lab` → `@deepseek-ai/dsh-agent-preset` | 声明 **Molecular Biology Lab** 模式,64 个工具在这一层里 |
108
108
 
109
- 所以 60 个工具**只在选中该模式的会话里加载**,其它模式(standard / ptc / minimal / cordis)
109
+ 所以 64 个工具**只在选中该模式的会话里加载**,其它模式(standard / ptc / minimal / cordis)
110
110
  不带它们,不占提示预算。bundle 安装 ≠ 工具全局可见:bundle 只是把"模式"和"面板"一起装进来。
111
111
 
112
112
  > **那一行锚点不是多余的。** DSH 的客户端模块扫描只走**宿主 Loader 树**(官方原话:
@@ -122,7 +122,7 @@ dsh plugin --profile web update dsh-molbio-tools
122
122
 
123
123
  ### 只要浏览器面板
124
124
 
125
- 如果只想要图形面板、不想让该 profile 装 60 个工具(连模式都不装),装面板专用包即可
125
+ 如果只想要图形面板、不想让该 profile 装 64 个工具(连模式都不装),装面板专用包即可
126
126
  (见[浏览器面板](#浏览器面板可选))。**它和 `dsh-molbio-tools` 二选一**:两者注册同一组右栏 tab
127
127
  (`dsh-molbio-tools` / `dsh-molbio-tools/papers`),同时装会重复注册并报
128
128
  `tab type id ... is already registered`。同样在 **插件 → 添加插件** 里输入 `dsh-molbio-panel`,
@@ -138,10 +138,10 @@ dsh plugin --profile <profile> add dsh-molbio-panel
138
138
 
139
139
  ## 它会做什么
140
140
 
141
- 60 个工具按任务分为十组。下表是"你想做什么 → 用哪个工具"的索引,参数细节模型会在调用时自己
141
+ 64 个工具按任务分为十组。下表是"你想做什么 → 用哪个工具"的索引,参数细节模型会在调用时自己
142
142
  填;你只需要把意图说清楚。
143
143
 
144
- ### 1. 序列分析与酶切(7)
144
+ ### 1. 序列分析与酶切(9)
145
145
 
146
146
  | 工具 | 用途 |
147
147
  | --- | --- |
@@ -152,13 +152,15 @@ dsh plugin --profile <profile> add dsh-molbio-panel
152
152
  | `molbio_enzyme_lookup` | 酶目录查询:识别位点/切点几何/突出端长度;给序列时报告**双链向的全部切点**(IIS 酶的反向识别位点也会被切,这是上一个工具不报告的) |
153
153
  | `molbio_methylation_check` | **甲基化会不会挡住酶切**:列出序列中每个 Dam(GATC)/Dcm(CCWGG)位点,把每个酶判为 `cuts` / `impaired` / `blocked` / `no_site`,并给出"哪些酶能用来切"的清单 |
154
154
  | `molbio_double_digest` | **双酶切规划**:两个酶各自的切点/片段、合并切点与片段,以及两者**是否共用 buffer**(无共用则提示顺序酶切或换用厂商双酶切 buffer) |
155
+ | `molbio_dotplot` | **序列自身 dotplot**:按 k-mer 精确匹配画直接重复(黑)与反向重复(红),网格分箱以限制文件大小,并报告匹配数与任何扫描/绘图截断;是精确 k-mer 视图,不是滑窗同一性打分图 |
156
+ | `molbio_contamination_screen` | **载体/接头污染筛查**:用内置的短签名表(pUC/pBR322、pUC19 lacZ/M13、pET T7、pcDNA CMV/BGH、Gateway attB1/attB2、Illumina/Nextera 接头)扫描序列或 FASTA/FASTQ;双链、默认精确、可选 0–2 错配,报告每个签名命中的 reads 数、示例位置/链/错配。**短签名筛查,不是 BLAST/全库比对;未命中不等于干净** |
155
157
 
156
158
  ### 2. 引物设计(6)
157
159
 
158
160
  | 工具 | 用途 |
159
161
  | --- | --- |
160
162
  | `molbio_design_primers` | **自动设计 PCR 引物对**:Tm(SantaLucia 1998 最近邻模型,盐/浓度可调)、GC、GC clamp、run/发夹/自互补/二聚体约束、错配容差、非特异结合检查、3' 目标位置偏好(SNP/定点设计),按罚分排序给出多组候选 |
161
- | `molbio_design_intron_primers` | **跨内含子 qPCR 引物**:给定基因组序列 + 外显子坐标,正向引物跨外显子-外显子连接点,基因组 DNA 不会扩增(可设最小基因组间距) |
163
+ | `molbio_design_intron_primers` | **跨内含子 qPCR 引物**:给定基因组序列 + 外显子坐标,**反向引物**跨外显子-外显子连接点(正向引物在其上游、位于单个外显子内),基因组 DNA 不会扩增;F 在上游、R 是按 5'→3' 可直接订购的分子(可设最小基因组间距) |
162
164
  | `molbio_design_taqman` | **TaqMan(水解探针)测定设计**:引物对 + 探针一次给出(探针 5' 端非 G、Tm 比引物高 5 °C 起、无 run/重复、不与引物二聚化),每条候选附逐项罚分;支持 **MGB 化学**(更短、按未修饰 NN Tm 报告并明确标注修饰未建模)和**订购 CSV**(引物/探针、5'/3' 修饰、规模、纯化) |
163
165
  | `molbio_primer_tm` | 单条引物 Tm 估算(Na⁺/Mg²⁺/dNTP 盐校正) |
164
166
  | `molbio_primer_check` | 引物结构筛查:重复序列、3' 端加权的自互补、发夹、引物对二聚体,以及 Primer3 同款热力学指标 |
@@ -196,11 +198,12 @@ dsh plugin --profile <profile> add dsh-molbio-panel
196
198
  | `molbio_plot` | **实验数据图表(8 种)**:柱状图(均值±SD 误差棒)、散点图(可选拟合线)、折线图(按分组列拆多条序列)、直方图(分箱默认 Freedman-Diaconis)、箱型图(Q1–Q3 箱 + 中位线 + 1.5×IQR 须 + 离群点)、**小提琴图**(高斯核密度轮廓 + 内部箱;看分布形状时用它)、**火山图**(log2FC × 显著性,带虚线阈值;**只画不检验**,p 值必须由表提供)、**热力图**(行×列矩阵,一格一色 + 色标;支持宽表与长表两种版式,`scale: row_zscore\|log2` 是**显式申报的展示变换**)。表类图表直接读工作区 **CSV/TSV**(`data_path` + `columns` 指名列),也可用 `data` 内联表格文本;结果里带回算好的统计量,**报数请引用它而不是照图估读** |
197
199
  | `molbio_virtual_gel` | **虚拟琼脂糖凝胶**:给出预期片段大小 → SVG 凝胶图 + 分子量 ladder,和真胶对照 |
198
200
 
199
- ### 6. 蛋白质(5)
201
+ ### 6. 蛋白质(6)
200
202
 
201
203
  | 工具 | 用途 |
202
204
  | --- | --- |
203
205
  | `molbio_protein_props` | MW、等电点、A280 消光系数、A280(0.1%)、GRAVY、脂肪族指数 |
206
+ | `molbio_protein_charge` | **电荷-pH 滴定曲线**:Henderson-Hasselbalch 逐 pH 净电荷、pI、pH 7.4 电荷;pK 可选 **Bjellqvist**(与 `protein_props` 的 pI 一致)或 **EMBOSS Epk.dat**,出 SVG |
204
207
  | `molbio_peptide_digest` | 质谱用酶切模拟:trypsin/chymotrypsin/LysC/GluC(P 前不切)、漏切 0–3、[M+H]⁺ 质量 |
205
208
  | `molbio_codon_optimize` | E. coli/酵母/人密码子优化,可顺便避开指定酶切位点 |
206
209
  | `molbio_helical_wheel` | **螺旋轮投影**:残基按 3.6 残基/圈落在圆周上、按性质着色,给出疏水矩 μH(判断是否两亲性),出 SVG |
@@ -235,11 +238,12 @@ dsh plugin --profile <profile> add dsh-molbio-panel
235
238
  | `molbio_protocol_add` / `molbio_protocol_list` / `molbio_protocol_update` | 协议库(`protocols.json`):步骤列表、自由参数、来源文献 |
236
239
  | `molbio_experiment_log` / `molbio_experiment_list` | 实验日志(`experiments.json`):关联协议与文献、笔记与结果 |
237
240
 
238
- ### 10. 实验台分析(5,v19 新增,树图 v20 改进)
241
+ ### 10. 实验台分析(6,v19 新增,树图 v20 改进)
239
242
 
240
243
  | 工具 | 用途 |
241
244
  | --- | --- |
242
245
  | `molbio_fastq_qc` | **读级 FASTQ 质控**:每碱基质量(均值 **+ 四分位**)、每读质量直方图、每碱基 A/C/G/T 含量、每读 GC 分布(含理论正态叠加)、长度分布、**精确序列重复率**、过度代表序列、内置接头片段扫描、Q20/Q30 与"质量从第几位开始跌破 Q30"——并出一张六面板 SVG 报告 |
246
+ | `molbio_fastq_trim` | **FASTQ 修剪/过滤**:3' 滑窗质量修剪、5' 引物/3' 接头精确去除、poly-G/poly-A 尾修剪、最小长度与 N 比例过滤;支持 Phred+33/+64,写出 FASTQ 或 FASTA,并报告 before/after 计数与逐阶段 trim 数。不做 paired-end merge、semiglobal adapter alignment 或 UMI |
243
247
  | `molbio_codon_usage` | **密码子使用分析**:CAI(Sharp & Li)、逐密码子 RSCU、Nc(有效密码子数)、GC3/GC123、罕见密码子清单(带位置与宿主频率)、CDS 内 CpG 观测/期望、隐藏终止子扫描;可对比 E. coli / 酵母 / 人 |
244
248
  | `molbio_phylogenetic_tree` | **距离法系统发生树**:p-distance / Jukes-Cantor / Kimura 2P / Tamura-Nei 校正距离、UPGMA 或邻接法(NJ)、**bootstrap 支持度(种子可复现)**、严格/多数共识树、Newick 读写、矩形式/环形/扇形 SVG |
245
249
  | `molbio_pcr_simulate` | **in-silico PCR**:双链搜索引物结合位点、产物大小与坐标、逐位错配明细、**"3' 端必须精确匹配几个碱基"是独立旋钮**(中段错配仍能延伸、3' 错配通常不能)、环状模板跨 origin 产物、对额外模板(如载体)做错引导筛查、预期凝胶图 |
@@ -273,11 +277,13 @@ dsh plugin --profile <profile> add dsh-molbio-panel
273
277
  | "这段肽是不是两亲性?画个螺旋轮" | `molbio_helical_wheel(sequence, moment_window:11)` |
274
278
  | "画这个蛋白的疏水性图,找跨膜段" | `molbio_hydropathy_plot(sequence, window:19)` |
275
279
  | "这批测序数据质量怎么样,从第几位开始掉" | `molbio_fastq_qc(path)` → 看 `quality_tail_below_30` 与六面板报告 |
280
+ | "这批 reads 有没有接头或载体污染" | `molbio_contamination_screen(path 或 sequence)` → 看命中的短签名、位置/链/错配;未命中不等于干净 |
281
+ | "把 reads 的低质量尾和接头修掉,写出新 FASTQ" | `molbio_fastq_trim(path, output_path, adapter:…, poly_g:true)` → before/after 计数 + 写出文件 |
276
282
  | "这个基因在大肠杆菌里表达会好吗,问题在哪" | `molbio_codon_usage(sequence, host:'e_coli')` → CAI / 罕见密码子清单 |
277
283
  | "这对引物除了目标还会在哪扩增?预期几条带" | `molbio_pcr_simulate(template, primer_pairs:[…])` → 产物大小 + 凝胶图 |
278
284
  | "这几条序列谁跟谁最近,这个结论有信心吗" | `molbio_phylogenetic_tree(sequences:[…], bootstrap:100)` → 支持度 + Newick |
279
285
  | "这段是不是启动子/CPG 岛?复制起点在哪" | `molbio_gc_composition(sequence)` → CpG 岛区间 + 累积 GC skew 的 ori/ter 提示 |
280
- | "把这 60 个质粒/reads 一起做个汇总表" | `molbio_batch_analyze(directory, pattern, analysis:'all', enzymes:[…])` → 一张 CSV/TSV |
286
+ | "把这批质粒/reads 一起做个汇总表" | `molbio_batch_analyze(directory, pattern, analysis:'all', enzymes:[…])` → 一张 CSV/TSV |
281
287
  | "把 EcoRI 位点静默去掉,蛋白不能变" | `molbio_silent_site_mutagenesis(sequence, enzyme:'EcoRI', action:'remove')` → mutation texts → `molbio_mutagenesis_primers` |
282
288
  | "用 Cas12a 设计 gRNA(5' TTTV PAM)" | `molbio_grna_design(sequence, cas_family:'cas12a')` |
283
289
  | "用 Cas13a 敲低这个转录本" | `molbio_grna_design(sequence, cas_family:'cas13a', target_strand:'forward')` → 看 PFS 与 require_pfs |
@@ -301,14 +307,16 @@ SVG 文件**并在结果里返回路径:
301
307
  | `molbio_qpcr_efficiency` / `molbio_plot` / `molbio_virtual_gel` | 曲线;`molbio_plot` 的柱状/散点/折线/直方图/箱型/小提琴/火山/热力图(`output_path` 必填);凝胶图 |
302
308
  | `molbio_helical_wheel` / `molbio_hydropathy_plot` | 螺旋轮、疏水性图 |
303
309
  | `molbio_fastq_qc` | `<名称>.qc.svg`(六面板质控报告) |
310
+ | `molbio_fastq_trim` | `output_path` 指定的 FASTQ/FASTA(修剪后的 reads) |
304
311
  | `molbio_phylogenetic_tree` | `<名称>.nwk`(Newick)+ `<名称>-tree.svg` |
305
312
  | `molbio_pcr_simulate` | `pcr-gel.svg`(预期凝胶) |
306
313
  | `molbio_gc_composition` | `gc-composition.svg`(GC/CpG 三面板图) |
314
+ | `molbio_protein_charge` / `molbio_dotplot` | 电荷-pH 曲线;直接/反向重复 dotplot |
307
315
  | `molbio_fasta_fastq` / `molbio_extract_region` / `molbio_paper_export_bibtex` | FASTA / `.bib` |
308
316
 
309
317
  ### 让模型自己看图:`attach_image`(可选,默认关)
310
318
 
311
- 上面这些**画图工具**(共 15 个)都接受一个可选参数 **`attach_image: true`**。打开后,工具会把
319
+ 上面这些**画图工具**(共 17 个)都接受一个可选参数 **`attach_image: true`**。打开后,工具会把
312
320
  同一张图**当场光栅化成 PNG 并作为图片附件挂到这次调用的结果上**——模型因此能直接"看见"凝胶
313
321
  条带、质粒图谱、logo、螺旋轮、曲线,而不是只能读数值。传了就会在结果里回一个 `image` 对象
314
322
  (附件的 id/尺寸/字节数),文本里也会写明"图已附上"。
@@ -425,6 +433,25 @@ mutations remain deferred")。所以插件无法在工作区里合法地落一
425
433
  - **目标位置偏好**(`target_position` + `target_penalty`):按"较近引物 3' 端到目标的距离"加重
426
434
  罚分,用于 SNP 分型/定点设计;跨内含子设计里该坐标是**剪接坐标**。
427
435
  - **性能**:几十 kb 模板在亚秒级完成;找不到满足约束的组合时返回空并提示放宽条件。
436
+ - **跨内含子设计的方向(0.23.0)**:跨连接点的是**反向引物**,正向引物在上游的单个外显子内。
437
+ 报告的 `reverse.sequence` 是可直接订购的 5'→3' 分子(其剪接位点的反向互补),F 在 R 上游;
438
+ `exon` 只出现在正向引物上,`exons`/`junction_left`/`junction_right` 只出现在反向引物上。
439
+ 错配报告里的 `template_base` 仍是"完美匹配时该引物会携带的碱基"——正向是转录本碱基,反向是它的互补。
440
+
441
+ ### 蛋白电荷与序列 dotplot(0.23.0)
442
+
443
+ - **`molbio_protein_charge`**:Henderson-Hasselbalch 求和,N/C 末端各计一次,K/R/H 为正、
444
+ D/E/C/Y 为负;`pka_set` 选 `bjellqvist`(默认,与 `molbio_protein_props` 的 pI 同一张表)
445
+ 或 `emboss`(Epk.dat)。**pI 只对所用的 pK 表有意义**,结果里同时给出 `pka_set` 与
446
+ `pka_label`;未建模二硫键、修饰残基、金属结合与离子强度。
447
+ - **`molbio_dotplot`**:精确 k-mer 自比较,黑色是直接重复、红色是反向重复;简并碱基参与的
448
+ k-mer 被跳过并在 `ambiguous_bases` 计数。`truncated` 表示匹配扫描触顶(计数是下界),
449
+ `plot_truncated` 表示绘图网格触顶(部分点未画)。对角线是序列与自身,属正常现象。
450
+
451
+ ### 污染筛查与 FASTQ 修剪(0.24.0)
452
+
453
+ - **`molbio_contamination_screen`**:表是**内置短签名**,不是数据库:pUC/pBR322 ori、pUC19 lacZ/M13、pUC/pBR322 bla、pET T7 promoter/terminator、pcDNA CMV/BGH、Gateway attB1/attB2,以及 Illumina/Nextera/polyA/polyG 接头。默认 exact、双链、替换错配 0–2(seed-and-verify),报告每个签名的 reads 数、示例位置/链/错配。**短签名会漏也会假阳性**;命中是"值得追"的线索,未命中**不能证明**样本干净。接头表在源头做了 U→T 归一化;旧版 Small RNA 5' 的 RNA 序列在 DNA reads 上只能命中反向互补,这是一个已修的静默错。
454
+ - **`molbio_fastq_trim`**:按固定顺序执行——5' 引物(精确、只在读首)→ 3' 接头(精确、切到接头前)→ 3' 滑窗质量(窗口均值 < 阈值时向左收)→ poly-G/poly-A 尾 → 最小长度/N 比例过滤。Phred+33/+64 的**质量字符原样保留**;输出 FASTQ 或 FASTA。**不做** paired-end overlap merge、cutadapt 式 semiglobal adapter alignment、UMI 处理——这些不在一个工具里假装做完。滑窗阈值、窗口、poly 最小 run、最小长度、N 比例都回显在 `criteria` 里。
428
455
 
429
456
  ### TaqMan 探针(v17)
430
457
 
@@ -699,21 +726,22 @@ agent 的 shell 子进程继承**,内联参数会出现在对话记录与工
699
726
 
700
727
  想评估"模型到底会不会用这些工具",看 **[benchmark/README.md](benchmark/README.md)**:
701
728
  `npm run bench:offline` 零成本校验期望值(在 `npm test` 里),`npm run bench` 用真实模型
702
- 跑 **core 档 14 题**,`npm run bench:full` 跑**全部 61 题(覆盖 60/60 个工具)**。
729
+ 跑 **core 档 14 题**,`npm run bench:full` 跑**全部 65 题(覆盖 64/64 个工具)**。
703
730
 
704
731
  > **改动与 benchmark 的关系(硬规则)**:新增或改动任何工具行为,**必须**新增/更新对应的
705
732
  > benchmark 任务并重跑;没有改动就不必重跑。日常只需要一条命令——
706
733
  > `node benchmark/run.mjs --model --changed`——它会从你的改动反推出该跑哪几题
707
734
  > (`lib.mjs` 这类共享库会自动放大到全量),改动只在文档/测试时则直接回答"无事可跑"。
708
- > `test/benchmark-coverage.mjs` 会拦住"新工具没有任务"(断言 60/60 覆盖),
735
+ > `test/benchmark-coverage.mjs` 会拦住"新工具没有任务"(断言 64/64 覆盖),
709
736
  > `bench --offline` 会拦住"期望值过期"。完整对照表见
710
737
  > [docs/workflow.md](docs/workflow.md) 第 2 节。
711
738
 
712
739
  > **benchmark 已经查出的一个真实缺陷**:`molbio_design_primers` 返回的引物对把
713
740
  > forward/reverse **标反了**(`F` 在 201-222、`R` 在 105-124,即 F 在 R 下游),
714
741
  > 按原样送进 `molbio_pcr_simulate` **不产生产物**。已用工具直接复核,详见
715
- > [benchmark/README.md](benchmark/README.md) 的 Findings。**尚未修复**——
716
- > 它需要自己的回归测试(设计出的一对引物按原样必须能模拟出恰好一个产物)。
742
+ > [benchmark/README.md](benchmark/README.md) 的 Findings。**0.16.0 已修**:两条分子的序列、
743
+ > Tm、GC 从来都是对的,错的是"哪条叫 F"与扩增子的两端;回归测试是跨工具的——设计出的每对
744
+ > 引物按原样送进 `molbio_pcr_simulate` 必须恰好得到一个产物,且端点与报告的扩增子一致。
717
745
 
718
746
  其它文档:
719
747
 
@@ -731,7 +759,7 @@ agent 的 shell 子进程继承**,内联参数会出现在对话记录与工
731
759
 
732
760
  ```
733
761
  dsh-molbio-tools/
734
- ├── index.mjs # 插件入口:export { name, inject, apply },注册 60 个工具
762
+ ├── index.mjs # 插件入口:export { name, inject, apply },注册 64 个工具
735
763
  ├── lib.mjs # 基础库:IUPAC、翻译、酶表、NN 热力学、qPCR、lab math、甲基化/buffer 参考表
736
764
  ├── design.mjs # 引物自动设计(含跨内含子 qPCR)
737
765
  ├── taqman.mjs # TaqMan 水解探针设计(复用 design.mjs 的引物引擎)
@@ -739,6 +767,7 @@ dsh-molbio-tools/
739
767
  ├── methylation.mjs # 甲基化敏感位点检查 + 双酶切 buffer 兼容
740
768
  ├── protein.mjs # 蛋白性质 / 肽段酶切 / 密码子优化
741
769
  ├── protein-structure.mjs # 螺旋轮与疏水性图
770
+ ├── protein-charge.mjs # 电荷-pH 滴定曲线 + pI(bjellqvist/emboss 两套 pK)
742
771
  ├── genbank.mjs # GenBank flatfile 解析器
743
772
  ├── snapgene.mjs # SnapGene .dna 二进制解析器
744
773
  ├── plasmid.mjs # SVG 质粒图谱渲染器
@@ -752,7 +781,10 @@ dsh-molbio-tools/
752
781
  ├── sanger.mjs # ABIF (.ab1) 解析 + 测序验证
753
782
  ├── plot.mjs # SVG 柱状/散点图 + 虚拟琼脂糖凝胶
754
783
  ├── charts.mjs # 实验数据图表:CSV/TSV 读取 + 折线/直方图/箱型/小提琴/火山/热力图(基于 svgio)
755
- ├── seqio.mjs # FASTA/FASTQ 解析与统计
784
+ ├── dotplot.mjs # k-mer 自比较 dotplot(直接/反向重复,网格分箱)
785
+ ├── contamination.mjs # 内置载体/接头短签名表 + 双链污染筛查
786
+ ├── fastq-trim.mjs # FASTQ 修剪/过滤(质量/接头/引物/poly/N/长度)
787
+ ├── seqio.mjs # FASTA/FASTQ 解析与统计(含 FASTQ 写出)
756
788
  ├── batch.mjs # 批量文件分析:同一套摘要/ORF/酶切位点 → CSV/TSV
757
789
  ├── records.mjs # 协议库 / 实验日志存储
758
790
  ├── papers.mjs # 文献库存储
package/batch.mjs CHANGED
@@ -46,7 +46,11 @@ export function detectBatchFormat(path, text) {
46
46
  /** Convert a simple basename glob (*, ?) to a RegExp. */
47
47
  export function globToRegExp(pattern) {
48
48
  const source = String(pattern)
49
- .replace(/[.+^$()|[\]{}]/g, '\$&')
49
+ // A literal backslash must be escaped before anything else; otherwise the
50
+ // following replaceAll calls turn it into a regex escape instead of a
51
+ // filename character.
52
+ .replaceAll('\\', '\\\\')
53
+ .replace(/[.+^$()|[\]{}]/g, '\\$&')
50
54
  .replaceAll('*', '.*')
51
55
  .replaceAll('?', '.');
52
56
  return new RegExp('^' + source + '$', 'i');
@@ -76,11 +76,11 @@ Measured, and printed so the answer can be audited:
76
76
 
77
77
  ```
78
78
  $ touch crispr.mjs && node benchmark/run.mjs --list --changed
79
- 1 of 61 task(s) selected — --tools molbio_grna_design --changed
79
+ 1 of 65 task(s) selected — --tools molbio_grna_design --changed
80
80
  (from --changed; 2 module(s) affected: crispr.mjs, index.mjs)
81
81
 
82
82
  $ touch lib.mjs && node benchmark/run.mjs --list --changed
83
- 55 of 61 task(s) selected — --changed (27 module(s) affected: align.mjs, cloning.mjs, …)
83
+ 55 of 65 task(s) selected — --changed (27 module(s) affected: align.mjs, cloning.mjs, …)
84
84
  ```
85
85
 
86
86
  `lib.mjs` selecting nearly everything is the **correct** answer, not a failure to
@@ -102,7 +102,7 @@ you changed, so its task runs even if it is full-only.
102
102
  | `--tools a,b,c` / `primer_*` | the union | you changed a few |
103
103
  | `--changed --tools X` | the INTERSECTION | "of what I changed, test X" |
104
104
  | (default) | **core** — 14 tasks | a broad smoke of the catalog |
105
- | `--tier full` | all 61, 60/60 tools | before a release |
105
+ | `--tier full` | all 65, 64/64 tools | before a release |
106
106
 
107
107
  The two `--tools` operators are stated because confusing them is the difference
108
108
  between testing what you changed and testing **less** than that: alone it unions,
@@ -134,7 +134,7 @@ and `test/benchmark-changed.mjs` asserts both directions.
134
134
  ### The part that still runs in full, on purpose
135
135
 
136
136
  A model run with `--changed` or `--tools` first executes the **whole** offline
137
- gate: all 61 tasks' `where: "tool"` expectations are recomputed against the shipped
137
+ gate: all 65 tasks' `where: "tool"` expectations are recomputed against the shipped
138
138
  tools (no model, a few hundred milliseconds). That is what makes partial runs safe
139
139
  — the expected values are re-proven for the tasks you skipped, so "skipped because
140
140
  unchanged" is a checked claim, not a remembered one.
@@ -169,7 +169,7 @@ asserting whatever the tool happened to emit.
169
169
 
170
170
  Two guards make "someone forgot" fail loudly instead of quietly:
171
171
 
172
- - `test/benchmark-coverage.mjs` asserts **60/60 registered tools are covered by
172
+ - `test/benchmark-coverage.mjs` asserts **64/64 registered tools are covered by
173
173
  at least one task**. Adding a tool without a task is a red build. It also
174
174
  asserts the fixtures' premises (the Golden Gate backbone is BsaI-free, the
175
175
  mutagenesis template still admits a primer pair), that a `covers` entry names a
@@ -210,7 +210,7 @@ recorded tool text, and it says so instead of silently scoring empty text).
210
210
 
211
211
  ## The headless profile (`benchmark/profile.mjs`)
212
212
 
213
- The model-facing way to get the 60 tools is the **Molecular Biology Lab** agent
213
+ The model-facing way to get the 64 tools is the **Molecular Biology Lab** agent
214
214
  preset. The benchmark cannot use it, and the reason is structural rather than
215
215
  configurational — `dsh-headless/lib/index.js` refuses an adoption whose session
216
216
  records a preset:
@@ -568,7 +568,7 @@ implementation happens to return for one set of arguments.**
568
568
  and `args_ok` both true is a *reporting* finding, not a tool bug.
569
569
  - **Multimodal hand-off.** `attach_image` is exercised as an argument at most; no
570
570
  assertion inspects a decoded PNG (that is `svgpng.mjs` and `contract.mjs`).
571
- - **Most of the toolset is now covered, but shallowly.** 61 tasks touch all 60
571
+ - **Most of the toolset is now covered, but shallowly.** 65 tasks touch all 64
572
572
  tools; most tools appear in exactly one task, and several appearances are a
573
573
  single argument check rather than a full workflow. Breadth is asserted by
574
574
  `test/benchmark-coverage.mjs`; depth is not, and cannot be.
@@ -19,9 +19,10 @@
19
19
  * consequences of a helper module being imported by feature modules — a real
20
20
  * dependency, but not the tool's IMPLEMENTATION.
21
21
  *
22
- * Measured, not guessed: 26 of the 57 tools call bindings from more than one
23
- * module. So a per-file → tool map is not a partition, it is a graph, and the
24
- * honest model is:
22
+ * Measured, not guessed: 26 of the tools call bindings from more than one
23
+ * module (measured when the suite covered 57; the graph property, not the
24
+ * count, is what matters). So a per-file → tool map is not a partition, it is
25
+ * a graph, and the honest model is:
25
26
  *
26
27
  * changed file → the module(s) it belongs to
27
28
  * → every module that transitively depends on them
@@ -98,12 +98,37 @@ export function buildFixtures(makeTemplate) {
98
98
 
99
99
  // ── an intron-spanning qPCR target ────────────────────────────────────────
100
100
  //
101
- // `designIntronPrimers` needs the forward primer to straddle the exon-exon
102
- // junction with `min_junction_bases` on EACH side, so the junction must fall
103
- // inside an 18-28 bp primer window. Seeds and boundaries were searched; this
104
- // one yields a pair spanning 12 bp of exon 1 and 6 bp of exon 2.
101
+ // `designIntronPrimers` needs the junction-spanning primer (the REVERSE one
102
+ // in the reported pair since 0.23.0) to straddle the exon-exon junction with
103
+ // `min_junction_bases` on EACH side, so the junction must fall inside an
104
+ // 18-28 bp primer window. Seeds and boundaries were searched; this one yields
105
+ // a pair whose reverse primer spans 12 bp of exon 1 and 6 bp of exon 2.
105
106
  const INTRON_GENOMIC = `${makeTemplate(400, 1)}${makeTemplate(300, 101)}${makeTemplate(400, 201)}`;
106
107
 
108
+ // ── a sequence with a direct repeat for the dotplot task ─────────────────
109
+ //
110
+ // The first 23 bp are repeated verbatim at the end; at word 5 that gives a
111
+ // known, deterministic number of direct and inverted matches (pinned in
112
+ // tasks/06-analysis.json and re-proven by `bench --offline`).
113
+ const DOTPLOT_SEQUENCE = 'TTAGGCACGTAGCTAGGCTAACGTTAGGCACGTAGCTAGGCTAACG';
114
+
115
+ // ── embedded-signature screening + FASTQ trimming ─────────────────────────
116
+ //
117
+ // The contamination fixture carries a pUC/pBR322 origin fragment at the start
118
+ // and the Illumina Universal adapter at the end; the middle is a neutral
119
+ // ACGT repeat, so the expected hit set is exactly those two signatures.
120
+ const CONTAMINATION_SEQUENCE = 'GTTTGCCGGATCAAGAGCTACCAACTCTTT' + 'ACGT'.repeat(20) + 'AGATCGGAAGAG';
121
+
122
+ // Four reads: a low-quality 3' tail (r1), an adapter followed by extra bases
123
+ // (r2), a poly-G tail (r3), and a read that is too short after trimming (r4).
124
+ const TRIM_FASTQ_TEXT = [
125
+ '@r1', 'ACGT'.repeat(10), '+', 'I'.repeat(30) + '!'.repeat(10),
126
+ '@r2', 'ACGT'.repeat(7) + 'AC' + 'AGATCGGAAGAG' + 'TTTTT', '+', 'I'.repeat(47),
127
+ '@r3', 'ACGT'.repeat(5) + 'G'.repeat(10), '+', 'I'.repeat(30),
128
+ '@r4', 'ACGT'.repeat(2) + 'AC', '+', 'I'.repeat(10),
129
+ '',
130
+ ].join('\n');
131
+
107
132
  // ── a coding sequence for the codon/ORF tasks ─────────────────────────────
108
133
  const CDS = `ATG${'GCT'.repeat(3)}${'GAA'.repeat(3)}${'TTC'.repeat(3)}${'AAA'.repeat(3)}${'GGT'.repeat(3)}${'CGT'.repeat(3)}${'TAC'.repeat(3)}${'GAT'.repeat(3)}${'CTG'.repeat(3)}${'TAA'}`;
109
134
 
@@ -140,6 +165,9 @@ export function buildFixtures(makeTemplate) {
140
165
  CLONE_INSERT,
141
166
  MUTAGENESIS_TEMPLATE,
142
167
  MUTAGENESIS_MUTATION: '200A>G',
168
+ DOTPLOT_SEQUENCE,
169
+ CONTAMINATION_SEQUENCE,
170
+ TRIM_FASTQ_TEXT,
143
171
  INTRON_GENOMIC,
144
172
  INTRON_EXONS: [
145
173
  { start: 1, end: 400 },
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * ── Why a hand-built profile exists at all ──────────────────────────────────
7
7
  *
8
- * The model-facing way to get the 57 tools is the "Molecular Biology Lab" agent
8
+ * The model-facing way to get the 64 tools is the "Molecular Biology Lab" agent
9
9
  * preset, and the benchmark would obviously rather run THAT than a
10
10
  * reconstruction of it. It cannot: `dsh-headless` refuses a preset for a
11
11
  * structural reason, not a configuration one —
package/benchmark/run.mjs CHANGED
@@ -51,6 +51,7 @@ const FILE_FIXTURES = {
51
51
  'chart-violin': { 'ct.csv': FIXTURES.CT_CSV },
52
52
  'chart-volcano': { 'de.csv': FIXTURES.DE_CSV },
53
53
  'chart-heatmap': { 'expression.tsv': FIXTURES.HEATMAP_TSV },
54
+ 'fastq-trim': { 'reads.fq': FIXTURES.TRIM_FASTQ_TEXT },
54
55
  };
55
56
 
56
57
  /** Where reports land. Gitignored: a report is a measurement, not a source. */
@@ -158,6 +158,7 @@ export const NAMED_SEQUENCES = {
158
158
  pcr_forward: FIXTURES.CONSTRUCT.slice(100, 120),
159
159
  pcr_reverse: reverseComplementOf(FIXTURES.CONSTRUCT.slice(281, 301)),
160
160
  gg_vector: FIXTURES.GG_VECTOR,
161
+ contamination: FIXTURES.CONTAMINATION_SEQUENCE,
161
162
  intron_genomic: FIXTURES.INTRON_GENOMIC,
162
163
  gg_insert_1: FIXTURES.GG_INSERTS[0],
163
164
  gg_insert_2: FIXTURES.GG_INSERTS[1],