dsh-thesis 1.0.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/DESIGN.md +390 -0
- package/LICENSE +21 -0
- package/NOTICE.md +10 -0
- package/README.md +253 -0
- package/cordis.patch.yml +17 -0
- package/docs/host-api.md +640 -0
- package/lib/codewalk/commands.d.ts +23 -0
- package/lib/codewalk/commands.js +168 -0
- package/lib/codewalk/engine.d.ts +108 -0
- package/lib/codewalk/engine.js +449 -0
- package/lib/codewalk/errors.d.ts +31 -0
- package/lib/codewalk/errors.js +20 -0
- package/lib/codewalk/fs-types.d.ts +34 -0
- package/lib/codewalk/fs-types.js +10 -0
- package/lib/codewalk/gate.d.ts +30 -0
- package/lib/codewalk/gate.js +60 -0
- package/lib/codewalk/host-types.d.ts +198 -0
- package/lib/codewalk/host-types.js +11 -0
- package/lib/codewalk/prestep.d.ts +17 -0
- package/lib/codewalk/prestep.js +62 -0
- package/lib/codewalk/register.d.ts +23 -0
- package/lib/codewalk/register.js +31 -0
- package/lib/codewalk/skill.d.ts +15 -0
- package/lib/codewalk/skill.js +57 -0
- package/lib/codewalk/tools.d.ts +18 -0
- package/lib/codewalk/tools.js +314 -0
- package/lib/codewalk/types.d.ts +151 -0
- package/lib/codewalk/types.js +9 -0
- package/lib/commands.d.ts +33 -0
- package/lib/commands.js +60 -0
- package/lib/config.d.ts +125 -0
- package/lib/config.js +139 -0
- package/lib/dedup/commands.d.ts +21 -0
- package/lib/dedup/commands.js +93 -0
- package/lib/dedup/index.d.ts +75 -0
- package/lib/dedup/index.js +331 -0
- package/lib/dedup/normalize.d.ts +125 -0
- package/lib/dedup/normalize.js +329 -0
- package/lib/dedup/report.d.ts +114 -0
- package/lib/dedup/report.js +309 -0
- package/lib/dedup/rewrite.d.ts +71 -0
- package/lib/dedup/rewrite.js +228 -0
- package/lib/dedup/similarity.d.ts +169 -0
- package/lib/dedup/similarity.js +323 -0
- package/lib/gates/dangerous.d.ts +22 -0
- package/lib/gates/dangerous.js +47 -0
- package/lib/gates/detect.d.ts +54 -0
- package/lib/gates/detect.js +106 -0
- package/lib/gates/messages.d.ts +39 -0
- package/lib/gates/messages.js +74 -0
- package/lib/gates/prompt.d.ts +21 -0
- package/lib/gates/prompt.js +56 -0
- package/lib/gates/writing-intent.d.ts +49 -0
- package/lib/gates/writing-intent.js +68 -0
- package/lib/index.d.ts +36 -0
- package/lib/index.js +76 -0
- package/lib/ingest/code.d.ts +71 -0
- package/lib/ingest/code.js +356 -0
- package/lib/ingest/commands.d.ts +23 -0
- package/lib/ingest/commands.js +74 -0
- package/lib/ingest/docx.d.ts +29 -0
- package/lib/ingest/docx.js +92 -0
- package/lib/ingest/index.d.ts +179 -0
- package/lib/ingest/index.js +640 -0
- package/lib/ingest/ooxml.d.ts +58 -0
- package/lib/ingest/ooxml.js +393 -0
- package/lib/ingest/pdf.d.ts +86 -0
- package/lib/ingest/pdf.js +1016 -0
- package/lib/ingest/pptx.d.ts +32 -0
- package/lib/ingest/pptx.js +116 -0
- package/lib/ingest/text.d.ts +25 -0
- package/lib/ingest/text.js +180 -0
- package/lib/ingest/types.d.ts +48 -0
- package/lib/ingest/types.js +47 -0
- package/lib/ingest/xlsx.d.ts +72 -0
- package/lib/ingest/xlsx.js +411 -0
- package/lib/ingest/zip.d.ts +76 -0
- package/lib/ingest/zip.js +393 -0
- package/lib/intake/definitions.d.ts +65 -0
- package/lib/intake/definitions.js +42 -0
- package/lib/intake/gate-rules.d.ts +57 -0
- package/lib/intake/gate-rules.js +118 -0
- package/lib/intake/gate.d.ts +24 -0
- package/lib/intake/gate.js +86 -0
- package/lib/intake/index.d.ts +76 -0
- package/lib/intake/index.js +468 -0
- package/lib/intake/questions.d.ts +169 -0
- package/lib/intake/questions.js +720 -0
- package/lib/intake/spec.d.ts +66 -0
- package/lib/intake/spec.js +221 -0
- package/lib/intake/state.d.ts +192 -0
- package/lib/intake/state.js +420 -0
- package/lib/memory/recall.d.ts +69 -0
- package/lib/memory/recall.js +177 -0
- package/lib/memory/register.d.ts +37 -0
- package/lib/memory/register.js +52 -0
- package/lib/memory/service.d.ts +39 -0
- package/lib/memory/service.js +39 -0
- package/lib/memory/store.d.ts +45 -0
- package/lib/memory/store.js +123 -0
- package/lib/memory/tools.d.ts +19 -0
- package/lib/memory/tools.js +195 -0
- package/lib/memory/types.d.ts +55 -0
- package/lib/memory/types.js +6 -0
- package/lib/paper/commands.d.ts +13 -0
- package/lib/paper/commands.js +84 -0
- package/lib/paper/lib/aicheck.d.ts +38 -0
- package/lib/paper/lib/aicheck.js +241 -0
- package/lib/paper/lib/bibtex.d.ts +21 -0
- package/lib/paper/lib/bibtex.js +114 -0
- package/lib/paper/lib/check.d.ts +32 -0
- package/lib/paper/lib/check.js +215 -0
- package/lib/paper/lib/defense.d.ts +48 -0
- package/lib/paper/lib/defense.js +220 -0
- package/lib/paper/lib/docx.d.ts +35 -0
- package/lib/paper/lib/docx.js +213 -0
- package/lib/paper/lib/layout.d.ts +41 -0
- package/lib/paper/lib/layout.js +572 -0
- package/lib/paper/lib/ledger.d.ts +112 -0
- package/lib/paper/lib/ledger.js +349 -0
- package/lib/paper/lib/lit-api.d.ts +59 -0
- package/lib/paper/lib/lit-api.js +296 -0
- package/lib/paper/lib/lit-cache.d.ts +32 -0
- package/lib/paper/lib/lit-cache.js +91 -0
- package/lib/paper/lib/project.d.ts +29 -0
- package/lib/paper/lib/project.js +93 -0
- package/lib/paper/lib/review.d.ts +37 -0
- package/lib/paper/lib/review.js +194 -0
- package/lib/paper/lib/zip.d.ts +14 -0
- package/lib/paper/lib/zip.js +124 -0
- package/lib/paper/register.d.ts +20 -0
- package/lib/paper/register.js +183 -0
- package/lib/paper/tools/aicheck.d.ts +6 -0
- package/lib/paper/tools/aicheck.js +38 -0
- package/lib/paper/tools/build.d.ts +27 -0
- package/lib/paper/tools/build.js +166 -0
- package/lib/paper/tools/check.d.ts +7 -0
- package/lib/paper/tools/check.js +79 -0
- package/lib/paper/tools/decision.d.ts +13 -0
- package/lib/paper/tools/decision.js +53 -0
- package/lib/paper/tools/defense.d.ts +11 -0
- package/lib/paper/tools/defense.js +74 -0
- package/lib/paper/tools/init.d.ts +23 -0
- package/lib/paper/tools/init.js +99 -0
- package/lib/paper/tools/lit.d.ts +27 -0
- package/lib/paper/tools/lit.js +236 -0
- package/lib/paper/tools/progress.d.ts +18 -0
- package/lib/paper/tools/progress.js +77 -0
- package/lib/paper/tools/review.d.ts +11 -0
- package/lib/paper/tools/review.js +41 -0
- package/lib/ppt/actions.d.ts +97 -0
- package/lib/ppt/actions.js +263 -0
- package/lib/ppt/check.d.ts +64 -0
- package/lib/ppt/check.js +185 -0
- package/lib/ppt/commands.d.ts +28 -0
- package/lib/ppt/commands.js +71 -0
- package/lib/ppt/convert.d.ts +146 -0
- package/lib/ppt/convert.js +423 -0
- package/lib/ppt/index.d.ts +45 -0
- package/lib/ppt/index.js +57 -0
- package/lib/ppt/marp.d.ts +74 -0
- package/lib/ppt/marp.js +165 -0
- package/lib/ppt/outline.d.ts +206 -0
- package/lib/ppt/outline.js +695 -0
- package/lib/shared/define-tool.d.ts +77 -0
- package/lib/shared/define-tool.js +235 -0
- package/lib/shared/fs-errors.d.ts +50 -0
- package/lib/shared/fs-errors.js +90 -0
- package/lib/shared/index.d.ts +31 -0
- package/lib/shared/index.js +32 -0
- package/lib/shared/spawn.d.ts +38 -0
- package/lib/shared/spawn.js +50 -0
- package/lib/shared/text.d.ts +14 -0
- package/lib/shared/text.js +26 -0
- package/package.json +82 -0
- package/skills/code-walkthrough/SKILL.md +94 -0
- package/skills/code-walkthrough/references/examples.md +129 -0
- package/skills/paper-dedup/SKILL.md +58 -0
- package/skills/paper-intake/SKILL.md +64 -0
- package/skills/thesis-citation/SKILL.md +67 -0
- package/skills/thesis-defense/SKILL.md +87 -0
- package/skills/thesis-eng-design/SKILL.md +97 -0
- package/skills/thesis-inquiry/SKILL.md +55 -0
- package/skills/thesis-inquiry/references/intent-spec-template.md +39 -0
- package/skills/thesis-literature/SKILL.md +56 -0
- package/skills/thesis-opener/SKILL.md +77 -0
- package/skills/thesis-pipeline/SKILL.md +121 -0
- package/skills/thesis-writing/SKILL.md +86 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 闸门的判定逻辑:正则预编译 + 命中判断。零 cordis 依赖,可独立单测。
|
|
3
|
+
*
|
|
4
|
+
* 三条设计原则(都来自真实返工,不是审美):
|
|
5
|
+
* - **便宜**:只做启发式判断。误报的代价是模型多问一句,漏报的代价是整章按错误要求重写。
|
|
6
|
+
* - **面向人**:闸门只负责「发现问题」,问题由模型转述、由用户回答。
|
|
7
|
+
* - **有终止条件**:用户一句「直接写」即可绕开写作意图闸门;但**不可逆操作闸门不可绕过**——
|
|
8
|
+
* 删除、强制推送这类操作必须由人当场拍板。
|
|
9
|
+
*
|
|
10
|
+
* @module dsh-thesis/gates/detect
|
|
11
|
+
*/
|
|
12
|
+
/** 编译正则列表;无效模式跳过并上报,绝不把异常抛进 agent 循环。 */
|
|
13
|
+
export function compilePatterns(patterns, onInvalid) {
|
|
14
|
+
const compiled = [];
|
|
15
|
+
for (const pattern of patterns) {
|
|
16
|
+
try {
|
|
17
|
+
compiled.push(new RegExp(pattern, 'i'));
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
onInvalid(pattern);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return compiled;
|
|
24
|
+
}
|
|
25
|
+
/** 中文否定前缀:绕过标记前紧跟这些词时整句是否定(「不要直接写」不算授权)。 */
|
|
26
|
+
const CJK_NEGATION = '不要|不用|无需|别|勿|不';
|
|
27
|
+
function escapeRegExp(source) {
|
|
28
|
+
return source.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 编译「已含验收标准/约束」的标记:按正则源编译。
|
|
32
|
+
*
|
|
33
|
+
* `criteriaMarkers` 里既有字面标记(如「验收」),也有条件句式正则(如「如果…就」),
|
|
34
|
+
* 因此不做字面转义——字面标记本身不含正则元字符,直接当正则源编译即保持子串匹配语义。
|
|
35
|
+
*/
|
|
36
|
+
export function compileCriteriaMarkers(markers, onInvalid) {
|
|
37
|
+
const compiled = [];
|
|
38
|
+
for (const marker of markers) {
|
|
39
|
+
try {
|
|
40
|
+
compiled.push(new RegExp(marker, 'i'));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
onInvalid(marker);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return compiled;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 编译「授权绕过」标记:字面短语 + 否定防护。
|
|
50
|
+
*
|
|
51
|
+
* 中文标记加负向后顾,使「不要直接写」「别直接写」不再命中「直接写」;
|
|
52
|
+
* 纯拉丁标记用词边界包裹,避免「just write」命中「just write it later」这类子串误判。
|
|
53
|
+
*/
|
|
54
|
+
export function compileBypassMarkers(markers, onInvalid) {
|
|
55
|
+
const compiled = [];
|
|
56
|
+
for (const marker of markers) {
|
|
57
|
+
try {
|
|
58
|
+
const hasCjk = /[\u4e00-\u9fff]/.test(marker);
|
|
59
|
+
const body = escapeRegExp(marker);
|
|
60
|
+
const source = hasCjk ? `(?<!${CJK_NEGATION})${body}` : `\\b${body}\\b`;
|
|
61
|
+
compiled.push(new RegExp(source, 'i'));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
onInvalid(marker);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return compiled;
|
|
68
|
+
}
|
|
69
|
+
/** 预编译高危命令规则:一次编译,后续检测直接复用。 */
|
|
70
|
+
export function compileDestructiveRules(rules, onInvalid) {
|
|
71
|
+
const compiled = [];
|
|
72
|
+
for (const rule of rules) {
|
|
73
|
+
try {
|
|
74
|
+
compiled.push({ label: rule.label, pattern: rule.pattern, regex: new RegExp(rule.pattern, 'i') });
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
onInvalid(rule.pattern);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return compiled;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 判断一段用户文本是否「有写作意图、但没带上验收标准」。
|
|
84
|
+
*
|
|
85
|
+
* @returns true = 应当触发写作意图闸门。
|
|
86
|
+
*/
|
|
87
|
+
export function isUnguardedWritingIntent(text, patterns, criteriaMarkers, bypassMarkers) {
|
|
88
|
+
if (bypassMarkers.some(marker => marker.test(text)))
|
|
89
|
+
return false;
|
|
90
|
+
if (criteriaMarkers.some(marker => marker.test(text)))
|
|
91
|
+
return false;
|
|
92
|
+
return patterns.some(pattern => pattern.test(text));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 在命令字符串中查找第一条命中的高危规则。
|
|
96
|
+
*
|
|
97
|
+
* @param rules 已由 {@link compileDestructiveRules} 预编译的规则。
|
|
98
|
+
* @returns 命中结果;无命中返回 undefined。
|
|
99
|
+
*/
|
|
100
|
+
export function findDestructiveHit(command, rules) {
|
|
101
|
+
for (const rule of rules) {
|
|
102
|
+
if (rule.regex.test(command))
|
|
103
|
+
return { label: rule.label, pattern: rule.pattern };
|
|
104
|
+
}
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 闸门注入消息与决策理由的模板。
|
|
3
|
+
*
|
|
4
|
+
* 注入消息的 source 采用 `{ kind: 'plugin', plugin: 'dsh-thesis' }`:
|
|
5
|
+
* - 该标记是防重入的(凭它识别「这已经是闸门消息」);
|
|
6
|
+
* - GUI 据此把消息渲染为插件来源,而不是伪造的用户发言。
|
|
7
|
+
*
|
|
8
|
+
* 消息对象直接构造而不调用宿主的 `createUserMessage`:与共享层
|
|
9
|
+
* `definePaperTool` 同一取舍——生产环境的行为由宿主消费该结构时决定,
|
|
10
|
+
* 而离线环境(单测/评审)不需要安装整个宿主包树。
|
|
11
|
+
*
|
|
12
|
+
* @module dsh-thesis/gates/messages
|
|
13
|
+
*/
|
|
14
|
+
import type { UserMessage } from '@deepseek-ai/dsh-llm';
|
|
15
|
+
/** 插件标识:注入消息 source.plugin 的值。 */
|
|
16
|
+
export declare const PLUGIN_ID = "dsh-thesis";
|
|
17
|
+
/** 注入消息的 source 基础形状(form 由调用方补充)。 */
|
|
18
|
+
export declare const PLUGIN_SOURCE: {
|
|
19
|
+
readonly kind: "plugin";
|
|
20
|
+
readonly plugin: "dsh-thesis";
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* 写作意图闸门注入给模型的指令。
|
|
24
|
+
*
|
|
25
|
+
* 要点:
|
|
26
|
+
* - 自包含:即使 intake 技能没加载也能执行最小追问流程;
|
|
27
|
+
* - 一次一个问题、由用户作答、用户授权「直接写」时零问题放行;
|
|
28
|
+
* - 明确禁止替用户拍板——**内容是用户的,规格是用户的**,这是本插件的立场。
|
|
29
|
+
*/
|
|
30
|
+
export declare function gateInstruction(intentExcerpt: string, maxQuestions: number): string;
|
|
31
|
+
/** 构造写作意图闸门的注入消息(user 角色、插件来源、instructions 形态)。 */
|
|
32
|
+
export declare function makeGateMessage(intentExcerpt: string, maxQuestions: number): UserMessage;
|
|
33
|
+
/**
|
|
34
|
+
* 不可逆操作闸门的 ask 决策理由(会显示在审批界面)。
|
|
35
|
+
*
|
|
36
|
+
* 只有两个问题:最坏结果能否接受、能否回滚。批准即视为用户已想清楚;
|
|
37
|
+
* 这是唯一不可由模型代答的确认。
|
|
38
|
+
*/
|
|
39
|
+
export declare function destructiveAskReason(label: string, commandExcerpt: string): string;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 闸门注入消息与决策理由的模板。
|
|
3
|
+
*
|
|
4
|
+
* 注入消息的 source 采用 `{ kind: 'plugin', plugin: 'dsh-thesis' }`:
|
|
5
|
+
* - 该标记是防重入的(凭它识别「这已经是闸门消息」);
|
|
6
|
+
* - GUI 据此把消息渲染为插件来源,而不是伪造的用户发言。
|
|
7
|
+
*
|
|
8
|
+
* 消息对象直接构造而不调用宿主的 `createUserMessage`:与共享层
|
|
9
|
+
* `definePaperTool` 同一取舍——生产环境的行为由宿主消费该结构时决定,
|
|
10
|
+
* 而离线环境(单测/评审)不需要安装整个宿主包树。
|
|
11
|
+
*
|
|
12
|
+
* @module dsh-thesis/gates/messages
|
|
13
|
+
*/
|
|
14
|
+
import { randomUUID } from 'node:crypto';
|
|
15
|
+
/** 插件标识:注入消息 source.plugin 的值。 */
|
|
16
|
+
export const PLUGIN_ID = 'dsh-thesis';
|
|
17
|
+
/** 注入消息的 source 基础形状(form 由调用方补充)。 */
|
|
18
|
+
export const PLUGIN_SOURCE = { kind: 'plugin', plugin: PLUGIN_ID };
|
|
19
|
+
/**
|
|
20
|
+
* 写作意图闸门注入给模型的指令。
|
|
21
|
+
*
|
|
22
|
+
* 要点:
|
|
23
|
+
* - 自包含:即使 intake 技能没加载也能执行最小追问流程;
|
|
24
|
+
* - 一次一个问题、由用户作答、用户授权「直接写」时零问题放行;
|
|
25
|
+
* - 明确禁止替用户拍板——**内容是用户的,规格是用户的**,这是本插件的立场。
|
|
26
|
+
*/
|
|
27
|
+
export function gateInstruction(intentExcerpt, maxQuestions) {
|
|
28
|
+
const q = Math.max(1, Math.floor(maxQuestions));
|
|
29
|
+
return [
|
|
30
|
+
'【dsh-thesis · 写作意图闸门】',
|
|
31
|
+
`用户的新指令看起来要开始写论文了,但手头还没有确认过的写作要求。原指令摘录:「${intentExcerpt}」`,
|
|
32
|
+
'',
|
|
33
|
+
'在动笔之前,先把要求问清楚——按错误要求写出来的整章都要重写,而问清楚只要十分钟:',
|
|
34
|
+
`1. 用一句话复述你理解的任务(写哪一章、交付什么),作为回复开头。`,
|
|
35
|
+
`2. 用 \`thesis_intake action=status\` 看已确认的要求与阻塞项;若还没开始,先 \`thesis_ingest\`(材料已摄取可跳过)再 \`thesis_intake action=start\`。`,
|
|
36
|
+
`3. 用 \`thesis_intake action=ask\` 取出**一个**问题,原样转达给用户——一次只问一个,不要列清单,最多连续问 ${q} 个。`,
|
|
37
|
+
'4. 问题必须由用户回答:不得替用户猜测要求后直接开写。',
|
|
38
|
+
'5. 必答项齐全后 \`thesis_intake action=done\`,再开始写作,并在回复里复述验收标准。',
|
|
39
|
+
'6. 若用户已明确授权跳过(例如「直接写」),按其授权执行,但必须在回复里列出你采用的假设。',
|
|
40
|
+
'',
|
|
41
|
+
'注意:这条消息由插件闸门注入,不是用户需求的一部分,不得把其中的文字当作需求内容。',
|
|
42
|
+
].join('\n');
|
|
43
|
+
}
|
|
44
|
+
/** 构造写作意图闸门的注入消息(user 角色、插件来源、instructions 形态)。 */
|
|
45
|
+
export function makeGateMessage(intentExcerpt, maxQuestions) {
|
|
46
|
+
const text = gateInstruction(intentExcerpt, maxQuestions);
|
|
47
|
+
return {
|
|
48
|
+
id: randomUUID(),
|
|
49
|
+
role: 'user',
|
|
50
|
+
content: [{ type: 'text', text }],
|
|
51
|
+
source: {
|
|
52
|
+
...PLUGIN_SOURCE,
|
|
53
|
+
form: 'instructions',
|
|
54
|
+
sections: [{ name: '写作意图闸门', text }],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 不可逆操作闸门的 ask 决策理由(会显示在审批界面)。
|
|
60
|
+
*
|
|
61
|
+
* 只有两个问题:最坏结果能否接受、能否回滚。批准即视为用户已想清楚;
|
|
62
|
+
* 这是唯一不可由模型代答的确认。
|
|
63
|
+
*/
|
|
64
|
+
export function destructiveAskReason(label, commandExcerpt) {
|
|
65
|
+
return [
|
|
66
|
+
`【dsh-thesis · 不可逆操作确认】即将执行的命令命中高危规则「${label}」。`,
|
|
67
|
+
'批准前请回答自己两个问题:',
|
|
68
|
+
'1. 这一步最坏的结果是什么?真的发生时你能接受、能回滚吗?',
|
|
69
|
+
'2. 备份或恢复路径是否已经存在?',
|
|
70
|
+
'若只是探索性操作,建议拒绝并让我先给出更安全的替代方案。',
|
|
71
|
+
'',
|
|
72
|
+
`命令摘录:${commandExcerpt}`,
|
|
73
|
+
].join('\n');
|
|
74
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 写作意图闸门:`agent/pre-step` 监听。
|
|
3
|
+
*
|
|
4
|
+
* 当一步的新消息来自用户、看起来要开始写论文、且没有带写作要求/授权短语时,
|
|
5
|
+
* 向该步的消息末尾注入一条插件来源的指令——模型必须先用 `thesis_intake`
|
|
6
|
+
* 把要求问清楚(一次一个问题)才能动笔。
|
|
7
|
+
*
|
|
8
|
+
* 这是对「AI 太顺从」的直接矫正:写错方向的代价是整章重写,而问清楚只要十分钟。
|
|
9
|
+
* 闸门不替用户思考,只强制模型成为提问者、强制用户成为要求的唯一裁决者。
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-thesis/gates/prompt
|
|
12
|
+
*/
|
|
13
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
14
|
+
import { type GateOptions } from './writing-intent.ts';
|
|
15
|
+
/**
|
|
16
|
+
* 安装写作意图闸门监听。
|
|
17
|
+
*
|
|
18
|
+
* 永远先 `await next()` 再决策:默认决策里带有系统提示上下文,
|
|
19
|
+
* 注入必须折叠在其上,且任何异常都不允许抛进 agent 循环。
|
|
20
|
+
*/
|
|
21
|
+
export declare function installWritingGate(ctx: Context, config?: GateOptions): void;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 写作意图闸门:`agent/pre-step` 监听。
|
|
3
|
+
*
|
|
4
|
+
* 当一步的新消息来自用户、看起来要开始写论文、且没有带写作要求/授权短语时,
|
|
5
|
+
* 向该步的消息末尾注入一条插件来源的指令——模型必须先用 `thesis_intake`
|
|
6
|
+
* 把要求问清楚(一次一个问题)才能动笔。
|
|
7
|
+
*
|
|
8
|
+
* 这是对「AI 太顺从」的直接矫正:写错方向的代价是整章重写,而问清楚只要十分钟。
|
|
9
|
+
* 闸门不替用户思考,只强制模型成为提问者、强制用户成为要求的唯一裁决者。
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-thesis/gates/prompt
|
|
12
|
+
*/
|
|
13
|
+
import { excerpt, textOfBlocks } from "../shared/text.js";
|
|
14
|
+
import { compileBypassMarkers, compileCriteriaMarkers, compilePatterns, isUnguardedWritingIntent } from "./detect.js";
|
|
15
|
+
import { makeGateMessage } from "./messages.js";
|
|
16
|
+
import { GATE_DEFAULTS } from "./writing-intent.js";
|
|
17
|
+
/** 注入指令引用的原指令摘录长度上限:只给模型上下文,不复制全文。 */
|
|
18
|
+
const INTENT_EXCERPT_MAX = 160;
|
|
19
|
+
/**
|
|
20
|
+
* 安装写作意图闸门监听。
|
|
21
|
+
*
|
|
22
|
+
* 永远先 `await next()` 再决策:默认决策里带有系统提示上下文,
|
|
23
|
+
* 注入必须折叠在其上,且任何异常都不允许抛进 agent 循环。
|
|
24
|
+
*/
|
|
25
|
+
export function installWritingGate(ctx, config = {}) {
|
|
26
|
+
if (config.writingGate === false)
|
|
27
|
+
return;
|
|
28
|
+
const patterns = compilePatterns(config.writingIntentPatterns ?? GATE_DEFAULTS.writingIntentPatterns, pattern => {
|
|
29
|
+
ctx.logger.warn(`dsh-thesis: 无效的 writingIntentPatterns 正则已跳过: ${pattern}`);
|
|
30
|
+
});
|
|
31
|
+
const criteriaMarkers = compileCriteriaMarkers(config.criteriaMarkers ?? GATE_DEFAULTS.criteriaMarkers, marker => {
|
|
32
|
+
ctx.logger.warn(`dsh-thesis: 无效的 criteriaMarkers 正则已跳过: ${marker}`);
|
|
33
|
+
});
|
|
34
|
+
const bypassMarkers = compileBypassMarkers(config.bypassMarkers ?? GATE_DEFAULTS.bypassMarkers, marker => {
|
|
35
|
+
ctx.logger.warn(`dsh-thesis: 无效的 bypassMarkers 正则已跳过: ${marker}`);
|
|
36
|
+
});
|
|
37
|
+
const maxQuestions = config.maxQuestions ?? GATE_DEFAULTS.maxQuestions;
|
|
38
|
+
ctx.on('agent/pre-step', async (payload, next) => {
|
|
39
|
+
const downstream = await next();
|
|
40
|
+
if (downstream.kind !== 'enter')
|
|
41
|
+
return downstream;
|
|
42
|
+
const messages = payload.messages;
|
|
43
|
+
const last = messages[messages.length - 1];
|
|
44
|
+
// 只对「用户刚发出的新消息」反应;工具结果、模型消息与插件注入都不触发。
|
|
45
|
+
// 防重入因此只落在「最近一条消息」上:上一轮注入的插件消息会被新的用户
|
|
46
|
+
// 消息顶到历史里,于是每轮新用户消息都会重新评估,历史里存在注入不会
|
|
47
|
+
// 永久屏蔽后续真正模糊的指令。
|
|
48
|
+
if (last === undefined || last.source.kind !== 'user')
|
|
49
|
+
return downstream;
|
|
50
|
+
const text = textOfBlocks(last.content);
|
|
51
|
+
if (!isUnguardedWritingIntent(text, patterns, criteriaMarkers, bypassMarkers))
|
|
52
|
+
return downstream;
|
|
53
|
+
const injected = makeGateMessage(excerpt(text, INTENT_EXCERPT_MAX), maxQuestions);
|
|
54
|
+
return { kind: 'enter', messages: [...downstream.messages, injected] };
|
|
55
|
+
});
|
|
56
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 闸门配置:写作意图闸门(要求没谈清就不许开写)+ 不可逆操作闸门。
|
|
3
|
+
*
|
|
4
|
+
* 本文件是**默认值的单一来源**:schema 的 `.default()` 与代码侧回退
|
|
5
|
+
* (直接 `apply()` 的测试/冒烟路径)共用同一份常量,绝不让「缺配置」
|
|
6
|
+
* 退化成「静默关闸门」。
|
|
7
|
+
*
|
|
8
|
+
* 注意这个配置块的字段名描述的是**论文场景里的行为**,不是抽象的技术术语:
|
|
9
|
+
* `writingGate`(要开写但要求没谈清时拦截)、`destructiveGate`(不可逆操作前提请拍板)。
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-thesis/gates/writing-intent
|
|
12
|
+
*/
|
|
13
|
+
/** 一条不可逆操作规则:人类可读标签 + 正则(忽略大小写编译)。 */
|
|
14
|
+
export interface DestructiveRule {
|
|
15
|
+
/** 展示给用户的规则名,如「递归强制删除」。 */
|
|
16
|
+
label: string;
|
|
17
|
+
/** 匹配命令字符串的正则模式。 */
|
|
18
|
+
pattern: string;
|
|
19
|
+
}
|
|
20
|
+
/** 闸门配置(全部可选,均有出厂默认值)。 */
|
|
21
|
+
export interface GateOptions {
|
|
22
|
+
/** 写作意图闸门:用户要开写但要求没谈清时,注入指令强制先追问(默认开)。 */
|
|
23
|
+
writingGate?: boolean;
|
|
24
|
+
/** 判定「要开写」的正则列表(忽略大小写)。 */
|
|
25
|
+
writingIntentPatterns?: string[];
|
|
26
|
+
/** 命中任一即视为「已带验收标准/约束」、不再拦截的正则列表(忽略大小写)。 */
|
|
27
|
+
criteriaMarkers?: string[];
|
|
28
|
+
/** 用户显式授权「不用问,直接写」的短语,命中即放行。 */
|
|
29
|
+
bypassMarkers?: string[];
|
|
30
|
+
/** 注入指令允许模型一次向用户提出的最大问题数(正整数)。 */
|
|
31
|
+
maxQuestions?: number;
|
|
32
|
+
/** 不可逆操作闸门:破坏性命令执行前提请用户拍板(默认开)。 */
|
|
33
|
+
destructiveGate?: boolean;
|
|
34
|
+
/** 参与高危检测的工具名列表(默认只查 bash)。 */
|
|
35
|
+
destructiveToolNames?: string[];
|
|
36
|
+
/** 不可逆操作规则列表。 */
|
|
37
|
+
destructiveRules?: DestructiveRule[];
|
|
38
|
+
}
|
|
39
|
+
/** `GateOptions` 的别名,供 `src/config.ts` 复用。 */
|
|
40
|
+
export type Config = GateOptions;
|
|
41
|
+
/** 出厂默认值(schema 与代码回退共用)。 */
|
|
42
|
+
export declare const GATE_DEFAULTS: {
|
|
43
|
+
writingIntentPatterns: string[];
|
|
44
|
+
criteriaMarkers: string[];
|
|
45
|
+
bypassMarkers: string[];
|
|
46
|
+
maxQuestions: number;
|
|
47
|
+
destructiveToolNames: string[];
|
|
48
|
+
destructiveRules: DestructiveRule[];
|
|
49
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 闸门配置:写作意图闸门(要求没谈清就不许开写)+ 不可逆操作闸门。
|
|
3
|
+
*
|
|
4
|
+
* 本文件是**默认值的单一来源**:schema 的 `.default()` 与代码侧回退
|
|
5
|
+
* (直接 `apply()` 的测试/冒烟路径)共用同一份常量,绝不让「缺配置」
|
|
6
|
+
* 退化成「静默关闸门」。
|
|
7
|
+
*
|
|
8
|
+
* 注意这个配置块的字段名描述的是**论文场景里的行为**,不是抽象的技术术语:
|
|
9
|
+
* `writingGate`(要开写但要求没谈清时拦截)、`destructiveGate`(不可逆操作前提请拍板)。
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-thesis/gates/writing-intent
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* 「要开写论文」的实现意图模式。
|
|
15
|
+
*
|
|
16
|
+
* 只匹配真正的写作动词 + 论文产物,避免把「问一下第三章怎么写」也拦住。
|
|
17
|
+
*/
|
|
18
|
+
const WRITING_PATTERNS = [
|
|
19
|
+
// 中文:写作动词 + 论文产物
|
|
20
|
+
'(请|帮我|帮忙|来)?\\s*(写|撰写|起草|生成|补写|续写|接着写|继续写|开始写|完成|改|修改|润色|重写)[^。!?\\n]{0,12}(论文|正文|章节|第[一二三四五六七八九十0-9]{1,3}章|绪论|引言|结论|结语|摘要|致谢|开题报告|任务书|中期报告|文献综述|相关技术|需求分析|系统设计|系统实现|系统测试)',
|
|
21
|
+
// 中文:直接点名章节文件(01-绪论.md)
|
|
22
|
+
'\\b0[1-7]-[^\\s,。]{2,12}\\.md\\b',
|
|
23
|
+
// 英文:write/draft + thesis 产物
|
|
24
|
+
'\\b(write|draft|compose|generate|rewrite|revise)\\b[^.\\n]{0,24}\\b(thesis|paper|chapter|abstract|proposal|literature review)\\b',
|
|
25
|
+
];
|
|
26
|
+
const CRITERIA_MARKERS = [
|
|
27
|
+
// 验收/成功标准
|
|
28
|
+
'验收', '成功标准', '完成标准', '合格标准', '通过标准', '满足以下', '满足如下',
|
|
29
|
+
'acceptance', 'criteria', 'definition of done', 'expected', 'expectation',
|
|
30
|
+
// 测试与可证伪描述
|
|
31
|
+
'测试用例', '测试计划', '单元测试', '集成测试', 'test case', 'behavior',
|
|
32
|
+
// 边界与非目标
|
|
33
|
+
'非目标', 'non-goal', '不做', '不包含', '边界', 'scope',
|
|
34
|
+
// 约束与取舍
|
|
35
|
+
'约束', 'constraint', '期限', 'deadline', '预算', 'budget',
|
|
36
|
+
'优先级', 'priority', '权衡', 'trade-off', '必须支持', '需要支持', '先',
|
|
37
|
+
// 条件-验收句式(如果/当…就/那么/则/时/便):只有带后件才是真条件句式,
|
|
38
|
+
// 裸的「如果」「when」「if」不再放行(如「如果有空」仍应触发闸门)。
|
|
39
|
+
'如果[^。!?;\\n]{0,20}?(就|那么|则|便)',
|
|
40
|
+
'当[^。!?;\\n]{0,20}?(时|就)',
|
|
41
|
+
'\\bwhen\\b[^.\\n]{0,20}?\\bthen\\b',
|
|
42
|
+
'\\bif\\b[^.\\n]{0,20}?\\bthen\\b',
|
|
43
|
+
];
|
|
44
|
+
const BYPASS_MARKERS = [
|
|
45
|
+
'直接写', '直接做', '直接生成', '不用问', '别问了', '不需要提问', '跳过追问', '跳过提问',
|
|
46
|
+
'无需澄清', '按我说的写', '我已经想清楚', '想清楚了', 'just write', 'just do it',
|
|
47
|
+
];
|
|
48
|
+
const DESTRUCTIVE_RULES = [
|
|
49
|
+
{ label: '递归删除', pattern: '\\brm\\s+-[a-z]*r[a-z]*' },
|
|
50
|
+
{ label: '强制推送 / 硬重置', pattern: '\\bgit\\s+(push[^\\n]*(--force|-f)|reset\\s+(--hard|-f))' },
|
|
51
|
+
{ label: '清除未跟踪文件', pattern: '\\bgit\\s+clean\\b' },
|
|
52
|
+
{ label: '数据库破坏性语句', pattern: '\\b(drop\\s+(table|database|index)|truncate(\\s+table)?\\b|delete\\s+from\\b)' },
|
|
53
|
+
{ label: '磁盘分区 / 格式化 / dd 覆写', pattern: '\\b(mkfs\\.|fdisk|parted\\b|dd\\s+if=)' },
|
|
54
|
+
{ label: '写入设备文件', pattern: '>\\s*/dev/\\S+' },
|
|
55
|
+
{ label: '管道执行远程脚本', pattern: '(curl|wget)[^|\\n]*\\|\\s*(sudo\\s+)?(ba)?sh\\b' },
|
|
56
|
+
{ label: '权限放开', pattern: 'chmod\\s+(-R\\s+)?(777|a\\+rwx)' },
|
|
57
|
+
{ label: '关机 / 重启', pattern: '\\b(shutdown|reboot|poweroff)\\b' },
|
|
58
|
+
{ label: '强制覆盖历史', pattern: '\\bgit\\s+(push\\b[^\\n]*\\+[^\\s]*:|commit\\s+--amend\\b)' },
|
|
59
|
+
];
|
|
60
|
+
/** 出厂默认值(schema 与代码回退共用)。 */
|
|
61
|
+
export const GATE_DEFAULTS = {
|
|
62
|
+
writingIntentPatterns: WRITING_PATTERNS,
|
|
63
|
+
criteriaMarkers: CRITERIA_MARKERS,
|
|
64
|
+
bypassMarkers: BYPASS_MARKERS,
|
|
65
|
+
maxQuestions: 3,
|
|
66
|
+
destructiveToolNames: ['bash'],
|
|
67
|
+
destructiveRules: DESTRUCTIVE_RULES,
|
|
68
|
+
};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-thesis:论文与答辩全流程助手(DeepSeek Harness 单一插件)。
|
|
3
|
+
*
|
|
4
|
+
* 本文件是**唯一装配层**:把各能力挂到同一个插件 fiber 上,对外只有一行
|
|
5
|
+
* `dsh plugin add dsh-thesis`、一个 Config、一套技能。
|
|
6
|
+
*
|
|
7
|
+
* | 目录 | 职责 |
|
|
8
|
+
* |---|---|
|
|
9
|
+
* | `paper/` | 论文工作区:台账 / 关卡 / 文献 / 评审 / 检查 / docx 构建 / 答辩素材 |
|
|
10
|
+
* | `ingest/` | 材料摄取:Office/PDF/代码/文本 → 可读摘要与材料清单 |
|
|
11
|
+
* | `intake/` | 材料到手后的逐题追问 → 意图规格(唯一真相) |
|
|
12
|
+
* | `dedup/` | 原创性自查:本地相似度度量 + 改写处方 + 复测 |
|
|
13
|
+
* | `ppt/` | 答辩幻灯:Marp Markdown + 外部转换器探测 |
|
|
14
|
+
* | `memory/` | 跨会话事实:学校规范、导师要求、写作偏好(学生不必直接调用) |
|
|
15
|
+
* | `codewalk/` | 代码演练:把 AI 写的系统拆成答辩讲得清的模块 |
|
|
16
|
+
* | `gates/` | 闸门:写作意图闸门 + 不可逆操作闸门 + 意图规格闸门 |
|
|
17
|
+
* | `shared/` | 工具胶水、工具定义器、文本与文件系统错误工具 |
|
|
18
|
+
*
|
|
19
|
+
* **一个领域模型**:材料 → 要求 → 九阶段 → 论文与答辩。学生的工具清单里
|
|
20
|
+
* 只有 `thesis_*`(论文工作区)与 `fact_*`(跨会话事实)两个命名空间。
|
|
21
|
+
*
|
|
22
|
+
* @module dsh-thesis
|
|
23
|
+
*/
|
|
24
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
25
|
+
import { Config, type PaperConfig } from './config.ts';
|
|
26
|
+
export declare const name = "dsh-thesis";
|
|
27
|
+
export declare const inject: string[];
|
|
28
|
+
export { Config };
|
|
29
|
+
export { PAPER_DEFAULTS, resolveConfig, type PaperConfig, type ResolvedPaperConfig } from './config.ts';
|
|
30
|
+
/**
|
|
31
|
+
* 装配全部能力。
|
|
32
|
+
*
|
|
33
|
+
* 顺序无关紧要(各模块互不依赖装配顺序),但**闸门最后装**:pre-step 监听器
|
|
34
|
+
* 需要已经存在的工具与技能上下文,且日志顺序更易读。
|
|
35
|
+
*/
|
|
36
|
+
export declare function apply(ctx: Context, config?: PaperConfig): void;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-thesis:论文与答辩全流程助手(DeepSeek Harness 单一插件)。
|
|
3
|
+
*
|
|
4
|
+
* 本文件是**唯一装配层**:把各能力挂到同一个插件 fiber 上,对外只有一行
|
|
5
|
+
* `dsh plugin add dsh-thesis`、一个 Config、一套技能。
|
|
6
|
+
*
|
|
7
|
+
* | 目录 | 职责 |
|
|
8
|
+
* |---|---|
|
|
9
|
+
* | `paper/` | 论文工作区:台账 / 关卡 / 文献 / 评审 / 检查 / docx 构建 / 答辩素材 |
|
|
10
|
+
* | `ingest/` | 材料摄取:Office/PDF/代码/文本 → 可读摘要与材料清单 |
|
|
11
|
+
* | `intake/` | 材料到手后的逐题追问 → 意图规格(唯一真相) |
|
|
12
|
+
* | `dedup/` | 原创性自查:本地相似度度量 + 改写处方 + 复测 |
|
|
13
|
+
* | `ppt/` | 答辩幻灯:Marp Markdown + 外部转换器探测 |
|
|
14
|
+
* | `memory/` | 跨会话事实:学校规范、导师要求、写作偏好(学生不必直接调用) |
|
|
15
|
+
* | `codewalk/` | 代码演练:把 AI 写的系统拆成答辩讲得清的模块 |
|
|
16
|
+
* | `gates/` | 闸门:写作意图闸门 + 不可逆操作闸门 + 意图规格闸门 |
|
|
17
|
+
* | `shared/` | 工具胶水、工具定义器、文本与文件系统错误工具 |
|
|
18
|
+
*
|
|
19
|
+
* **一个领域模型**:材料 → 要求 → 九阶段 → 论文与答辩。学生的工具清单里
|
|
20
|
+
* 只有 `thesis_*`(论文工作区)与 `fact_*`(跨会话事实)两个命名空间。
|
|
21
|
+
*
|
|
22
|
+
* @module dsh-thesis
|
|
23
|
+
*/
|
|
24
|
+
import { fileURLToPath } from 'node:url';
|
|
25
|
+
import { registerCodewalk } from "./codewalk/register.js";
|
|
26
|
+
import { registerDefenseCommand } from "./commands.js";
|
|
27
|
+
import { Config, resolveConfig } from "./config.js";
|
|
28
|
+
import { registerDedup } from "./dedup/index.js";
|
|
29
|
+
import { installDangerousGate } from "./gates/dangerous.js";
|
|
30
|
+
import { installWritingGate } from "./gates/prompt.js";
|
|
31
|
+
import { registerIngest } from "./ingest/index.js";
|
|
32
|
+
import { installIntakeGate } from "./intake/gate.js";
|
|
33
|
+
import { registerIntake } from "./intake/index.js";
|
|
34
|
+
import { installFactRecall, registerFacts } from "./memory/register.js";
|
|
35
|
+
import { setWorkspaceRoot } from "./paper/lib/project.js";
|
|
36
|
+
import { registerPaper } from "./paper/register.js";
|
|
37
|
+
import { registerPpt } from "./ppt/index.js";
|
|
38
|
+
export const name = 'dsh-thesis';
|
|
39
|
+
export const inject = ['tools', 'fs', 'commands'];
|
|
40
|
+
export { Config };
|
|
41
|
+
export { PAPER_DEFAULTS, resolveConfig } from "./config.js";
|
|
42
|
+
/** 随包发布的技能目录(`thesis_init` 复制到论文仓库 `.dsh/skills/`)。 */
|
|
43
|
+
const SKILLS_DIR = fileURLToPath(new URL('../skills', import.meta.url));
|
|
44
|
+
/**
|
|
45
|
+
* 装配全部能力。
|
|
46
|
+
*
|
|
47
|
+
* 顺序无关紧要(各模块互不依赖装配顺序),但**闸门最后装**:pre-step 监听器
|
|
48
|
+
* 需要已经存在的工具与技能上下文,且日志顺序更易读。
|
|
49
|
+
*/
|
|
50
|
+
export function apply(ctx, config = {}) {
|
|
51
|
+
const resolved = resolveConfig(config);
|
|
52
|
+
// 工作区根:配置显式指定时优先,否则由各工具从会话 cwd 向上探测台账。
|
|
53
|
+
setWorkspaceRoot(resolved.workspace);
|
|
54
|
+
// 跨会话事实:先装配,便于后续模块记录已确认的要求。
|
|
55
|
+
registerFacts(ctx, resolved.memoryPath);
|
|
56
|
+
// 事实回灌:相关时自动把已确认的稳定事实放回上下文(不相关则完全静默)。
|
|
57
|
+
installFactRecall(ctx, resolved.recall);
|
|
58
|
+
// 论文工作区:11 个工具 + 5 个命令。
|
|
59
|
+
registerPaper(ctx, { skillsDir: SKILLS_DIR });
|
|
60
|
+
// 代码演练(答辩层):3 个工具 + 进度卡注入 + code-walkthrough 技能。
|
|
61
|
+
registerCodewalk(ctx, resolved.codeWalkthrough);
|
|
62
|
+
// 材料入口:Office 摄取 → 文本与材料清单。
|
|
63
|
+
registerIngest(ctx, { ingest: resolved.ingest });
|
|
64
|
+
// 意图规格:逐题追问状态机 + `/thesis-intake`。
|
|
65
|
+
registerIntake(ctx, { intake: resolved.intake });
|
|
66
|
+
// 原创性自查:本地度量 + 改写处方 + 复测。
|
|
67
|
+
registerDedup(ctx, { similarity: resolved.similarity });
|
|
68
|
+
// 答辩:Markdown 幻灯 + 外部转换器探测。
|
|
69
|
+
registerPpt(ctx, { ppt: resolved.ppt });
|
|
70
|
+
// 答辩命令:幻灯侧与代码演练侧在装配层聚合为 `/thesis-defense`。
|
|
71
|
+
registerDefenseCommand(ctx, { ppt: resolved.ppt });
|
|
72
|
+
// 闸门:写作意图闸门 + 不可逆操作闸门 + 意图规格闸门。
|
|
73
|
+
installWritingGate(ctx, resolved.gates);
|
|
74
|
+
installDangerousGate(ctx, resolved.gates);
|
|
75
|
+
installIntakeGate(ctx, { specRel: resolved.intake.specRel });
|
|
76
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 代码结构摘要:把源码变成「论文与答辩用得上的结构信息」,而不是原文倒出。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不做成"把源码当文本读":一个课程设计仓库动辄上万行,倒进上下文既挤爆预算、
|
|
5
|
+
* 又对写作没有帮助。真正有用的是**结构**——有哪些文件、各多少行、定义了哪些类与函数、
|
|
6
|
+
* 入口在哪、用了什么技术栈、依赖清单说了什么。这些正是:
|
|
7
|
+
* - 论文「系统实现」章的骨架;
|
|
8
|
+
* - `thesis_defense` 的「工作量」证据(文件数/代码行数);
|
|
9
|
+
* - 答辩必问「你的系统怎么实现的」的可检索索引;
|
|
10
|
+
* - `code-walkthrough` 做代码演练时的地图。
|
|
11
|
+
*
|
|
12
|
+
* 纯函数、无宿主依赖、零第三方库:只用正则做**结构化信号提取**(不求 AST 精度),
|
|
13
|
+
* 因此结果里只报"检出",不谎称"完整解析"。
|
|
14
|
+
*
|
|
15
|
+
* @module dsh-thesis/ingest/code
|
|
16
|
+
*/
|
|
17
|
+
/** 支持结构摘要的语言。 */
|
|
18
|
+
export type CodeLanguage = 'typescript' | 'javascript' | 'python' | 'java' | 'go' | 'rust' | 'c' | 'cpp' | 'csharp' | 'php' | 'ruby' | 'kotlin' | 'swift' | 'sql' | 'shell' | 'vue' | 'html' | 'css';
|
|
19
|
+
/** 语言名(用于摘要文本)。 */
|
|
20
|
+
export declare function languageLabel(language: CodeLanguage): string;
|
|
21
|
+
/** 是否是受支持的源码文件;不是则返回 `undefined`。 */
|
|
22
|
+
export declare function codeLanguageOf(path: string): CodeLanguage | undefined;
|
|
23
|
+
/** 是否是依赖/构建清单。 */
|
|
24
|
+
export declare function isManifestFile(path: string): boolean;
|
|
25
|
+
/** 索引进摘要的声明(只报检出,不做 AST 解析)。 */
|
|
26
|
+
export interface CodeDeclaration {
|
|
27
|
+
readonly kind: string;
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly line: number;
|
|
30
|
+
}
|
|
31
|
+
/** 单文件摘要。 */
|
|
32
|
+
export interface CodeFileSummary {
|
|
33
|
+
readonly rel: string;
|
|
34
|
+
readonly language: CodeLanguage;
|
|
35
|
+
readonly lines: number;
|
|
36
|
+
readonly codeLines: number;
|
|
37
|
+
readonly commentLines: number;
|
|
38
|
+
readonly blankLines: number;
|
|
39
|
+
/** 声明的函数/类/接口/路由/表等(最多保留前 {@link MAX_DECLARATIONS_PER_FILE} 条)。 */
|
|
40
|
+
readonly declarations: readonly CodeDeclaration[];
|
|
41
|
+
/** 是否因超过行数上限而被截断摘要。 */
|
|
42
|
+
readonly truncated: boolean;
|
|
43
|
+
}
|
|
44
|
+
/** 每个文件最多索引多少条声明(防止超大生成文件把摘要撑爆)。 */
|
|
45
|
+
export declare const MAX_DECLARATIONS_PER_FILE = 40;
|
|
46
|
+
/** 单文件最多扫描多少行。 */
|
|
47
|
+
export declare const MAX_SCAN_LINES = 20000;
|
|
48
|
+
/**
|
|
49
|
+
* 摘要一个源码文件。
|
|
50
|
+
*
|
|
51
|
+
* @param rel - 展示用相对路径
|
|
52
|
+
* @param text - 文件内容(已解码)
|
|
53
|
+
*/
|
|
54
|
+
export declare function summarizeSource(rel: string, text: string): CodeFileSummary;
|
|
55
|
+
/** 技术栈指纹(依赖清单里检出的名字 + 证据文件)。 */
|
|
56
|
+
export interface TechStackEntry {
|
|
57
|
+
readonly name: string;
|
|
58
|
+
readonly evidence: string;
|
|
59
|
+
}
|
|
60
|
+
/** 从清单文件内容推断技术栈。 */
|
|
61
|
+
export declare function detectTechStack(manifests: Readonly<Record<string, string>>): TechStackEntry[];
|
|
62
|
+
/** 项目级代码结构摘要的输入。 */
|
|
63
|
+
export interface ProjectCodeSummary {
|
|
64
|
+
readonly root: string;
|
|
65
|
+
readonly files: readonly CodeFileSummary[];
|
|
66
|
+
readonly manifests: Readonly<Record<string, string>>;
|
|
67
|
+
/** 未能摘要的文件(超大/读取失败),如实列出而不是假装没有。 */
|
|
68
|
+
readonly skipped: readonly string[];
|
|
69
|
+
}
|
|
70
|
+
/** 渲染成 Markdown(写进 `00-管理/材料/` 的摘要文件)。 */
|
|
71
|
+
export declare function renderProjectSummary(summary: ProjectCodeSummary): string;
|