dsh-thesis 2.1.1 → 2.1.2

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/README.md CHANGED
@@ -1,98 +1,47 @@
1
1
  # dsh-thesis
2
2
 
3
- **把一堆材料变成一篇经得起查重与答辩的论文,和一个讲得清自己系统的你。**
3
+ **把一堆材料变成一篇经得起查重与答辩的论文。**
4
4
 
5
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的单一插件。给它学校模板、
6
- 论文要求、实验数据、代码和参考文献,它先追问、再动手,按九个阶段推进:
5
+ [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的单一插件。
6
+ 给它学校模板、论文要求、实验数据、代码和参考文献,它先追问、再动手,按九个阶段推进:
7
7
 
8
8
  **选题 → 开题 → 文献 → 设计 → 实现 → 测试 → 论文 → 合规 → 答辩**
9
9
 
10
- **没有斜杠命令。** 在论文工作区里开口说话,插件问一道选择题确认意图,然后自己一轮轮跑下去,
11
- 只在 G1-G4 人工关卡停下来让你拍板。全程留痕(台账 / 决定日志 / 检索记录 / 检查报告)。
12
-
13
10
  纯 TypeScript,零运行时第三方依赖。对外只有三个命名空间:`thesis_*`、`fact_*`、`defense_code_*`。
14
11
 
15
12
  ## 安装
16
13
 
17
- 在 DSH 的**插件管理器里搜索 `dsh-thesis`** 装上,重启 dsh。
14
+ 在 DSH 的插件管理器里搜索 `dsh-thesis`即可 装上。
18
15
 
19
- 从源码装(想改代码,或本机没有 pnpm):
16
+ 从源码装:
20
17
 
21
18
  ```bash
22
19
  git clone https://github.com/booondenz/dsh-thesis.git && cd dsh-thesis
23
- npm install && npm run build # DSH 没有 TS 加载器,必须先编译
24
- dsh plugin --profile web add "$PWD" # 需要 pnpm
25
- node scripts/install-into-profile.ts --profile web # 没有 pnpm 时的兜底
20
+ npm install && npm run build
21
+ dsh plugin --profile web add "$PWD"
26
22
  ```
27
23
 
28
- 卸载:插件管理器里移除,或 `dsh plugin --profile web remove dsh-thesis`。
29
-
30
- ## 第一次用
24
+ ## 使用
31
25
 
32
- 把材料放进论文目录(或告诉它绝对路径),在**该目录下**开会话说:
26
+ 把材料放进论文目录,或告诉它绝对路径,在**该目录下**开会话说:
33
27
 
34
28
  ```
35
29
  这是我的论文材料,帮我做毕业设计
36
30
  ```
37
31
 
38
32
  它会弹一道选择题(从零走到答辩 / 只补合规与答辩 / 只做答辩准备 / 先别自动跑),
39
- 选完就自动推进;撞上 G1-G4 关卡时在审批界面点「允许」或「拒绝」。
40
- 没有材料也能用:「我要做毕业设计,还没定题」。
41
-
42
- ## 配置
43
-
44
- 默认值都在,通常不用写。需要时在 profile 的 `cordis.patch.yml` 那一行里覆盖:
45
-
46
- ```yaml
47
- - id: paper
48
- name: dsh-thesis
49
- config:
50
- workspace: D:/毕业论文/我的论文 # 缺省从会话 cwd 向上探测 00-管理/进度台账.md
51
- autopilot: { maxGoalRounds: 20 } # 自动续跑的轮数上限(默认 40)
52
- ```
53
-
54
- 完整字段见 [docs/config.md](./docs/config.md)。
55
-
56
- ## 红线(写在护栏里,不是写在文档里)
57
-
58
- 1. **零假文献**:引用只能来自真实检索缓存,`thesis_lit_save` 拒绝缓存外的 id。
59
- 2. **零假数据**:数据与结果必须来自真实运行;降重不许改数据与结论。
60
- 3. **人是作者**:G1-G4 关卡由工具强制,决定必须由你拍板。
61
- 4. **规范以学校文件为准**:没有模板时用内置过渡模板并**显式标注「非学校模板」**。
62
- 5. **降重不是洗稿**:不接收费查重,报告里是本地估算,与学校结果不可等同(可回填对照)。
63
- 6. **不静默销毁数据**:读不出来 ≠ 不存在;读失败就抛错,绝不用默认值覆盖。
64
-
65
- ## 文档
66
-
67
- | 文档 | 内容 |
68
- |---|---|
69
- | [docs/usage.md](./docs/usage.md) | 使用手册:第一次会话、九阶段、常见动作、产出物、中断续跑 |
70
- | [docs/autopilot.md](./docs/autopilot.md) | 自动驾驶:四档意图、关卡拍板、何时不介入、降级与关闭 |
71
- | [docs/tools.md](./docs/tools.md) | 24 个工具 + 11 个技能一览(名字即契约) |
72
- | [docs/config.md](./docs/config.md) | 全部配置字段、默认值与约束 |
73
- | [docs/troubleshooting.md](./docs/troubleshooting.md) | 装了没反应、没弹选择题、PDF/pptx 失败等 |
74
- | [docs/design.md](./docs/design.md) | 设计文档与 ADR(架构、分层、质量策略、已知边界) |
75
- | [docs/host-api.md](./docs/host-api.md) | 宿主契约:用到的 DSH 接口与实测行为 |
76
- | [docs/release.md](./docs/release.md) | 发版:`npm run release:minor` + push → GitHub Actions → npm(含 provenance)|
77
-
78
- ## 开发
79
-
80
- ```bash
81
- npm install && npm run build # tsc → lib/
82
- npm test # node --test(Node 24 直接跑 .ts)
83
- npm run verify # build + 两个 typecheck + 全部单测 + 审计 + 端到端
84
- ```
33
+ 选完就自动推进;撞上 G1-G4 关卡时在审批界面点允许或拒绝。
85
34
 
86
- 仓库里**没有 JS 源文件**:`src/`、`scripts/`、`tests/` 全是 `.ts`,直接由 Node 执行(需要 Node ≥ 22.18);
87
- `lib/` 只是 `tsc` 产物(已 gitignore,发布时作为编译后的 JS 提供)。
35
+ ## 红线
88
36
 
89
- 质量分层:单测/契约、真实场景端到端、真实 cordis 装配、真实 dsh 引擎装载、对外表面审计
90
- (见 [docs/design.md](./docs/design.md) §9)。
37
+ - **零假文献**:引用只能来自真实检索缓存,`thesis_lit_save` 拒绝缓存外的 id。
38
+ - **零假数据**:数据与结果必须来自真实运行;降重不许改数据与结论。
39
+ - **人是作者**:G1-G4 关卡由工具强制,决定必须由你拍板。
40
+ - **规范以学校文件为准**:没有模板时用内置过渡模板并**显式标注「非学校模板」**。
41
+ - **降重不是洗稿**:不接收费查重,报告里是本地估算,与学校结果不可等同(可回填对照)。
42
+ - **不静默销毁数据**:读不出来 ≠ 不存在;读失败就抛错,绝不用默认值覆盖。
91
43
 
92
- **发版**:`npm run release:minor`(改版本号并提交)+ `git push` 即发布——workflow 发现 npm 上没有这个版本
93
- 就发布,发完自动补 tag 与 Release,**不需要手动打 tag**。由 GitHub Actions 跑构建、校验与
94
- `npm publish --provenance`,见 [docs/release.md](./docs/release.md)。
95
44
 
96
45
  ## 许可
97
46
 
98
- MIT。来源声明见 [NOTICE.md](./NOTICE.md)。
47
+ MIT
@@ -259,6 +259,9 @@ function buildFlow(model) {
259
259
  rows.set(row, bucket);
260
260
  }
261
261
  const rowRanks = [...rows.keys()].sort((a, b) => a - b);
262
+ // 层数一多,固定 48 的行距会把整张图拉成两三页高(8 层 ≈ 740 单位高)。
263
+ // 层数超过 4 就收紧行距:A4 正文宽 15.5cm 时,这张图仍能一页放下。
264
+ const gapY = rowRanks.length <= 4 ? cfg.gapY : Math.max(30, cfg.gapY - 14);
262
265
  const nodeWidthOf = (node) => (node.kind === 'decision' ? cfg.decisionWidth : cfg.nodeWidth);
263
266
  const nodeHeightOf = (node) => (node.kind === 'decision' ? cfg.decisionHeight : cfg.nodeHeight);
264
267
  // 先以「中轴 x=0」排布,最后整体平移:主干节点落在中轴上,其它节点摆在它右侧。
@@ -268,7 +271,7 @@ function buildFlow(model) {
268
271
  let maxX = 0;
269
272
  rowRanks.forEach((rankNo, rowIndex) => {
270
273
  const nodes = rows.get(rankNo) ?? [];
271
- const rowY = LAYOUT.margin + rowIndex * (cfg.nodeHeight + cfg.gapY);
274
+ const rowY = LAYOUT.margin + rowIndex * (cfg.nodeHeight + gapY);
272
275
  if (nodes.length > 1)
273
276
  notes.push(`第 ${rankNo + 1} 层有 ${nodes.length} 个并行节点(${nodes.map(n => n.text).join('、')})`);
274
277
  const main = nodes.find(node => mainPath.has(node.id));
@@ -303,6 +306,10 @@ function buildFlow(model) {
303
306
  for (const [id, rect] of positions) {
304
307
  positions.set(id, { ...rect, x: rect.x + shift });
305
308
  }
309
+ // 跳层连线的绕行通道:放在内容右侧。不绕行的话,draw.io 会把「判断 → 第 N 层」这类
310
+ // 跨层边画成一条竖线,正好从中间几层的节点身上穿过去(看上去像把框划开了)。
311
+ const laneX = maxX + shift + 26;
312
+ const contentWidth = Math.max(460, laneX + LAYOUT.margin - shift);
306
313
  for (const node of model.nodes) {
307
314
  const rect = positions.get(node.id);
308
315
  if (rect === undefined)
@@ -325,20 +332,29 @@ function buildFlow(model) {
325
332
  throw new FigureLayoutError(`连线端点「${edge.from} → ${edge.to}」在版面里找不到`);
326
333
  const fromRect = positions.get(edge.from);
327
334
  const toRect = positions.get(edge.to);
328
- const horizontal = fromRect !== undefined && toRect !== undefined && Math.abs(fromRect.y - toRect.y) < 1;
335
+ const fromRank = rank.get(edge.from) ?? 0;
336
+ const toRank = rank.get(edge.to) ?? 0;
337
+ const skipsRanks = toRank - fromRank > 1;
338
+ const horizontal = !skipsRanks && fromRect !== undefined && toRect !== undefined && Math.abs(fromRect.y - toRect.y) < 1;
339
+ const style = skipsRanks
340
+ ? `${STYLE.edge}exitX=1;exitY=0.5;entryX=1;entryY=0.5;`
341
+ : horizontal
342
+ ? `${STYLE.edge}exitX=1;exitY=0.5;entryX=0;entryY=0.5;`
343
+ : `${STYLE.edge}exitX=0.5;exitY=1;entryX=0.5;entryY=0;`;
329
344
  cells.push({
330
345
  kind: 'edge',
331
346
  id: nextEdge(),
332
347
  value: edge.label ?? '',
333
- style: horizontal
334
- ? `${STYLE.edge}exitX=1;exitY=0.5;entryX=0;entryY=0.5;`
335
- : `${STYLE.edge}exitX=0.5;exitY=1;entryX=0.5;entryY=0;`,
348
+ style,
336
349
  source,
337
350
  target,
351
+ ...(skipsRanks && fromRect !== undefined && toRect !== undefined
352
+ ? { points: [{ x: laneX, y: fromRect.y + fromRect.height / 2 }, { x: laneX, y: toRect.y + toRect.height / 2 }] }
353
+ : {}),
338
354
  });
339
355
  }
340
- const height = LAYOUT.margin + rowRanks.length * (cfg.nodeHeight + cfg.gapY) - cfg.gapY + LAYOUT.margin;
341
- return { cells, width, height, notes };
356
+ const height = LAYOUT.margin + rowRanks.length * (cfg.nodeHeight + gapY) - gapY + LAYOUT.margin;
357
+ return { cells, width: contentWidth, height, notes };
342
358
  }
343
359
  // ---------------------------------------------------------------------------
344
360
  // E-R 图
@@ -360,6 +376,7 @@ function buildEr(model) {
360
376
  const columns = model.entities.length > 1 ? 2 : 1;
361
377
  const width = LAYOUT.margin * 2 + maxWidth * columns + (columns - 1) * cfg.columnGap;
362
378
  const entityIds = new Map();
379
+ const entityRects = new Map();
363
380
  const columnY = new Array(columns).fill(LAYOUT.margin);
364
381
  model.entities.forEach((entity, index) => {
365
382
  const column = index % columns;
@@ -369,6 +386,7 @@ function buildEr(model) {
369
386
  const y = columnY[column] ?? LAYOUT.margin;
370
387
  const id = nextVertex('entity');
371
388
  entityIds.set(entity.name, id);
389
+ entityRects.set(entity.name, { x, y, width: widthForEntity, height });
372
390
  // 不可见的容器先画(在下层):关系线连到整张表,而不是某一行;
373
391
  // 先入队保证它垫在标题与字段下面,不遮挡文字与点击。
374
392
  cells.push({
@@ -405,11 +423,21 @@ function buildEr(model) {
405
423
  if (source === undefined || target === undefined)
406
424
  throw new FigureLayoutError(`关系端点「${relation.from} → ${relation.to}」在版面里找不到`);
407
425
  const label = [relation.cardinality, relation.label].filter(part => part !== undefined && part !== '').join(' ');
426
+ // 关系线必须锚在实体**外框**上:不给锚点时 draw.io 会从容器中心起笔,
427
+ // 线直接从字段行中间穿过去(三张表并排时尤其明显)。
428
+ const fromRect = entityRects.get(relation.from);
429
+ const toRect = entityRects.get(relation.to);
430
+ const sameColumn = fromRect !== undefined && toRect !== undefined && Math.abs(fromRect.x - toRect.x) < 1;
431
+ const ports = fromRect === undefined || toRect === undefined || sameColumn
432
+ ? 'exitX=0.5;exitY=1;entryX=0.5;entryY=0;'
433
+ : toRect.x > fromRect.x
434
+ ? 'exitX=1;exitY=0.5;entryX=0;entryY=0.5;'
435
+ : 'exitX=0;exitY=0.5;entryX=1;entryY=0.5;';
408
436
  cells.push({
409
437
  kind: 'edge',
410
438
  id: nextEdge(),
411
439
  value: label,
412
- style: `${STYLE.edge}endArrow=none;startArrow=none;`,
440
+ style: `${STYLE.edge}endArrow=none;startArrow=none;${ports}`,
413
441
  source,
414
442
  target,
415
443
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-thesis",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "论文与答辩全流程助手(DeepSeek Harness 插件):材料摄取 → 逐题追问 → 文献检索 → 逐章写作与评审 → 原创性自查与合规 → Word/答辩幻灯生产,全流程留痕,并跨会话记住学校规范与导师要求。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
package/NOTICE.md DELETED
@@ -1,10 +0,0 @@
1
- # 来源与许可声明(NOTICE)
2
-
3
- 本仓库的全部代码与文档均为作者本人(booondenz)的原创作品,其中一部分由作者此前
4
- 在其它宿主环境下的同题材项目整理、合并、重写而来。**没有使用任何第三方代码**,
5
- 因此这里没有需要致谢的外部贡献者;本文件存在的唯一目的,是如实说明这一点。
6
-
7
- ## 许可
8
-
9
- MIT,以本仓库根目录的 [`LICENSE`](./LICENSE) 为准。被合并整理的早期作品同为该作者所有、
10
- 同为 MIT 许可,合并这件事本身不改变许可。