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 +18 -69
- package/lib/figure/lib/layout.js +36 -8
- package/package.json +1 -1
- package/NOTICE.md +0 -10
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
|
|
14
|
+
在 DSH 的插件管理器里搜索 `dsh-thesis`即可 装上。
|
|
18
15
|
|
|
19
|
-
|
|
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
|
|
24
|
-
dsh plugin --profile web add "$PWD"
|
|
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
|
-
|
|
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
|
-
|
|
87
|
-
`lib/` 只是 `tsc` 产物(已 gitignore,发布时作为编译后的 JS 提供)。
|
|
35
|
+
## 红线
|
|
88
36
|
|
|
89
|
-
|
|
90
|
-
|
|
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
|
|
47
|
+
MIT
|
package/lib/figure/lib/layout.js
CHANGED
|
@@ -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 +
|
|
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
|
|
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
|
|
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 +
|
|
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
package/NOTICE.md
DELETED