dsh-thesis 1.0.1 → 2.0.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.
- package/LICENSE +1 -1
- package/NOTICE.md +1 -1
- package/README.md +51 -86
- package/docs/autopilot.md +73 -0
- package/docs/config.md +113 -0
- package/{DESIGN.md → docs/design.md} +69 -74
- package/docs/host-api.md +40 -4
- package/docs/release.md +126 -0
- package/docs/tools.md +76 -0
- package/docs/troubleshooting.md +88 -0
- package/docs/usage.md +87 -0
- package/lib/autopilot/index.d.ts +38 -0
- package/lib/autopilot/index.js +159 -0
- package/lib/autopilot/intent.d.ts +40 -0
- package/lib/autopilot/intent.js +82 -0
- package/lib/autopilot/messages.d.ts +22 -0
- package/lib/autopilot/messages.js +35 -0
- package/lib/autopilot/types.d.ts +60 -0
- package/lib/autopilot/types.js +10 -0
- package/lib/codewalk/engine.d.ts +1 -1
- package/lib/codewalk/engine.js +1 -1
- package/lib/codewalk/gate.d.ts +1 -1
- package/lib/codewalk/gate.js +1 -1
- package/lib/codewalk/host-types.d.ts +0 -29
- package/lib/codewalk/prestep.js +1 -1
- package/lib/codewalk/tools.js +2 -2
- package/lib/config.d.ts +17 -0
- package/lib/config.js +9 -1
- package/lib/dedup/index.js +0 -6
- package/lib/index.d.ts +3 -1
- package/lib/index.js +10 -7
- package/lib/ingest/index.d.ts +1 -1
- package/lib/ingest/index.js +1 -3
- package/lib/intake/index.d.ts +0 -17
- package/lib/intake/index.js +0 -51
- package/lib/intake/questions.d.ts +1 -1
- package/lib/intake/questions.js +1 -1
- package/lib/paper/lib/layout.d.ts +1 -1
- package/lib/paper/lib/layout.js +2 -2
- package/lib/paper/lib/ledger.d.ts +1 -1
- package/lib/paper/lib/ledger.js +1 -1
- package/lib/paper/register.d.ts +2 -2
- package/lib/paper/register.js +2 -8
- package/lib/paper/tools/decision.js +1 -1
- package/lib/paper/tools/init.d.ts +1 -1
- package/lib/paper/tools/init.js +1 -1
- package/lib/ppt/index.d.ts +1 -2
- package/lib/ppt/index.js +1 -2
- package/lib/shared/define-tool.d.ts +1 -1
- package/lib/shared/define-tool.js +1 -1
- package/package.json +17 -17
- package/skills/code-walkthrough/SKILL.md +5 -3
- package/skills/thesis-defense/SKILL.md +1 -1
- package/skills/thesis-pipeline/SKILL.md +3 -3
- package/lib/codewalk/commands.d.ts +0 -23
- package/lib/codewalk/commands.js +0 -168
- package/lib/commands.d.ts +0 -33
- package/lib/commands.js +0 -60
- package/lib/dedup/commands.d.ts +0 -21
- package/lib/dedup/commands.js +0 -93
- package/lib/ingest/commands.d.ts +0 -23
- package/lib/ingest/commands.js +0 -74
- package/lib/paper/commands.d.ts +0 -13
- package/lib/paper/commands.js +0 -84
- package/lib/ppt/commands.d.ts +0 -28
- package/lib/ppt/commands.js +0 -71
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动驾驶的文案与意图选项:**一次只问一个问题**,答案决定接下来跑到哪一步。
|
|
3
|
+
*
|
|
4
|
+
* 这里的文本是插件直接呈现给用户的(不是给模型的),所以用词要能让人一眼选:
|
|
5
|
+
* 三档常见起点 + 一个「先别自动跑」。选完就不再打断,直到撞上人工关卡。
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-thesis/autopilot
|
|
8
|
+
*/
|
|
9
|
+
/** 意图选择题的问题 id(宿主按 id 把答案回传)。 */
|
|
10
|
+
export declare const INTENT_QUESTION_ID = "dsh-thesis.intent";
|
|
11
|
+
/** 一档意图:选项文案 + 目标 + 启动指令里要向模型交代的边界。 */
|
|
12
|
+
export interface IntentChoice {
|
|
13
|
+
/** 选项标签(用户看到的那一行)。 */
|
|
14
|
+
readonly label: string;
|
|
15
|
+
/** 选项说明(UI 可渲染在标签下方)。 */
|
|
16
|
+
readonly description: string;
|
|
17
|
+
/** 写进 goal 的目标(宿主按它渲染续跑提示)。 */
|
|
18
|
+
readonly objective: string;
|
|
19
|
+
/** 注入给模型的第一条指令正文;空串表示「用户不要自动跑」。 */
|
|
20
|
+
readonly instruction: string;
|
|
21
|
+
}
|
|
22
|
+
/** 「先别自动跑」的选项标签:回答它等于关掉本次自动驾驶。 */
|
|
23
|
+
export declare const HANDS_OFF_LABEL = "\u5148\u522B\u81EA\u52A8\u8DD1\uFF0C\u6211\u81EA\u5DF1\u6765";
|
|
24
|
+
/** 三档起点 + 一个关闭项(顺序即 UI 顺序)。 */
|
|
25
|
+
export declare const INTENT_CHOICES: readonly IntentChoice[];
|
|
26
|
+
/** 把用户选的标签映射回一档意图;认不出来时返回 undefined(按「关掉」处理)。 */
|
|
27
|
+
export declare function choiceOfLabel(label: string): IntentChoice | undefined;
|
|
28
|
+
/** 构造那一道选择题。 */
|
|
29
|
+
export declare function intentQuestion(): {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly header: string;
|
|
32
|
+
readonly question: string;
|
|
33
|
+
readonly detail: string;
|
|
34
|
+
readonly options: readonly {
|
|
35
|
+
readonly label: string;
|
|
36
|
+
readonly description: string;
|
|
37
|
+
}[];
|
|
38
|
+
};
|
|
39
|
+
/** 启动指令消息的正文(含「这条不是用户需求」的免责声明)。 */
|
|
40
|
+
export declare function autopilotInstruction(choice: IntentChoice, maxGoalRounds: number): string;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动驾驶的文案与意图选项:**一次只问一个问题**,答案决定接下来跑到哪一步。
|
|
3
|
+
*
|
|
4
|
+
* 这里的文本是插件直接呈现给用户的(不是给模型的),所以用词要能让人一眼选:
|
|
5
|
+
* 三档常见起点 + 一个「先别自动跑」。选完就不再打断,直到撞上人工关卡。
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-thesis/autopilot
|
|
8
|
+
*/
|
|
9
|
+
/** 意图选择题的问题 id(宿主按 id 把答案回传)。 */
|
|
10
|
+
export const INTENT_QUESTION_ID = 'dsh-thesis.intent';
|
|
11
|
+
/** 「先别自动跑」的选项标签:回答它等于关掉本次自动驾驶。 */
|
|
12
|
+
export const HANDS_OFF_LABEL = '先别自动跑,我自己来';
|
|
13
|
+
/** 三档起点 + 一个关闭项(顺序即 UI 顺序)。 */
|
|
14
|
+
export const INTENT_CHOICES = [
|
|
15
|
+
{
|
|
16
|
+
label: '从零走到答辩(九阶段全跑)',
|
|
17
|
+
description: '材料 → 追问 → 文献 → 设计 → 实现 → 测试 → 论文 → 合规 → 答辩,中间只在人工关卡停下来问你。',
|
|
18
|
+
objective: '在 dsh-thesis 论文工作区里把毕业设计从材料推进到论文 docx 与答辩材料,按九阶段与 G1-G4 关卡推进,撞关卡时停下来问用户一次。',
|
|
19
|
+
instruction: [
|
|
20
|
+
'本次会话已开启**自动驾驶**:用户刚选了「从零走到答辩」。',
|
|
21
|
+
'按 `thesis-pipeline` 技能推进,顺序:`thesis_progress action=report` 看当前阶段 → 缺工作区就 `thesis_init` → 有材料就 `thesis_ingest` → 用 `thesis_intake` 逐题追问(一次一个)把要求问成意图规格 → 文献 → 逐章写作与评审 → 全文检查 → 构建 → 原创性自查 → 答辩。',
|
|
22
|
+
'省时间的并行部分(例如各章写作、逐章检查、文献笔记)用 subagent / workflow 扇出,但**结论必须落盘到工作区**,不要只留在子 agent 的上下文里。',
|
|
23
|
+
].join('\n'),
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: '已有材料/初稿,补齐合规与答辩',
|
|
27
|
+
description: '不重写已有章节;先做引用与格式检查、降重自查,再出答辩材料与幻灯。',
|
|
28
|
+
objective: '在已有的 dsh-thesis 论文工作区里补齐合规检查(引用/字数/图表/术语/模板)、原创性自查与答辩材料,不重写已有正文。',
|
|
29
|
+
instruction: [
|
|
30
|
+
'本次会话已开启**自动驾驶**:用户刚选了「已有材料/初稿,补齐合规与答辩」。',
|
|
31
|
+
'先 `thesis_progress action=report` 与 `thesis_check` 看清现状,再按缺口补:引用 bidirectional 一致、字数区间、图表编号、术语定义顺序、样式自查、原创性复测,最后 `thesis_defense` + `thesis_slides`。',
|
|
32
|
+
'**不要重写用户已有的正文**;发现必须改动的地方,先记进决定日志再动。',
|
|
33
|
+
].join('\n'),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: '只做答辩准备',
|
|
37
|
+
description: '答辩素材、预答辩问题库、幻灯与讲稿,以及把系统拆成讲得清的模块。',
|
|
38
|
+
objective: '只做答辩准备:答辩素材、六类问题库、答辩幻灯与讲稿,以及代码演练(把系统拆成讲得清的模块)。',
|
|
39
|
+
instruction: [
|
|
40
|
+
'本次会话已开启**自动驾驶**:用户刚选了「只做答辩准备」。',
|
|
41
|
+
'顺序:`thesis_defense` 出素材与问题库 → `thesis_slides outline` 出幻灯骨架并逐页补齐讲稿与证据锚点 → `thesis_slides check` 自查 → 需要讲实现细节时用 `defense_code_*` 做代码演练。',
|
|
42
|
+
'幻灯以 Markdown 为准,pptx 转换探测不到外部引擎时如实说明,不假装成功。',
|
|
43
|
+
].join('\n'),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
label: HANDS_OFF_LABEL,
|
|
47
|
+
description: '本次不建目标、不自动续跑,照常一问一答。',
|
|
48
|
+
objective: '',
|
|
49
|
+
instruction: '',
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
/** 把用户选的标签映射回一档意图;认不出来时返回 undefined(按「关掉」处理)。 */
|
|
53
|
+
export function choiceOfLabel(label) {
|
|
54
|
+
const normalized = label.trim();
|
|
55
|
+
return INTENT_CHOICES.find(choice => choice.label === normalized);
|
|
56
|
+
}
|
|
57
|
+
/** 构造那一道选择题。 */
|
|
58
|
+
export function intentQuestion() {
|
|
59
|
+
return {
|
|
60
|
+
id: INTENT_QUESTION_ID,
|
|
61
|
+
header: '确认意图',
|
|
62
|
+
question: '这次想让我做到哪一步?选一个我就一路跑下去。',
|
|
63
|
+
detail: '只在人工关卡(选题/逐章验收/开题/提交自查)停下来问你;其余步骤我自己推进。',
|
|
64
|
+
options: INTENT_CHOICES.map(choice => ({ label: choice.label, description: choice.description })),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** 启动指令消息的正文(含「这条不是用户需求」的免责声明)。 */
|
|
68
|
+
export function autopilotInstruction(choice, maxGoalRounds) {
|
|
69
|
+
return [
|
|
70
|
+
'【dsh-thesis · 自动驾驶】',
|
|
71
|
+
choice.instruction,
|
|
72
|
+
'',
|
|
73
|
+
`目标已登记(最多 ${maxGoalRounds} 轮自动续跑)。每轮结束前,用 \`thesis_progress action=report\` 看一眼台账,确认下一步是该继续、该停下来问,还是该收尾。`,
|
|
74
|
+
'规矩:',
|
|
75
|
+
'1. 人工关卡(G1-G4)由**用户**拍板——轮到你过闸时,插件会弹一次确认,不要自己替用户过。',
|
|
76
|
+
'2. 数据与文献只能来自真实运行与真实检索;拿不到就如实说,绝不编造。',
|
|
77
|
+
'3. 大段独立的写作/检查用子 agent 并行,但**产出必须写进工作区文件**,子 agent 的结论不落盘等于没做。',
|
|
78
|
+
'4. 该收尾时把 `update_goal` 标完成,并给出一页「做了什么 / 待你确认」的清单。',
|
|
79
|
+
'',
|
|
80
|
+
'注意:这条消息由插件注入,不是用户需求的一部分。',
|
|
81
|
+
].join('\n');
|
|
82
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动驾驶注入的消息与关卡确认文案。
|
|
3
|
+
*
|
|
4
|
+
* 与闸门消息共用同一条 source 约定:`{ kind: 'plugin', plugin: 'dsh-thesis', form: 'instructions' }`。
|
|
5
|
+
* `form` 是宿主渲染指令的语义形态;`sections` 只属于 snapshot 形态,这里不写。
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-thesis/autopilot
|
|
8
|
+
*/
|
|
9
|
+
import type { UserMessage } from '@deepseek-ai/dsh-llm';
|
|
10
|
+
import { type IntentChoice } from './intent.ts';
|
|
11
|
+
/** 构造启动指令消息(插件来源、instructions 形态)。 */
|
|
12
|
+
export declare function makeAutopilotMessage(choice: IntentChoice, maxGoalRounds: number): UserMessage;
|
|
13
|
+
/**
|
|
14
|
+
* 人工关卡的确认文案(`tools/pre-execute` 的 ask 决策,显示在审批界面)。
|
|
15
|
+
*
|
|
16
|
+
* 一关只问一次,问题只有一个:这一步你认不认。认了流程才会继续推进。
|
|
17
|
+
*/
|
|
18
|
+
export declare function gateApprovalReason(request: {
|
|
19
|
+
readonly gate: string;
|
|
20
|
+
readonly chapter?: string;
|
|
21
|
+
readonly note?: string;
|
|
22
|
+
}): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动驾驶注入的消息与关卡确认文案。
|
|
3
|
+
*
|
|
4
|
+
* 与闸门消息共用同一条 source 约定:`{ kind: 'plugin', plugin: 'dsh-thesis', form: 'instructions' }`。
|
|
5
|
+
* `form` 是宿主渲染指令的语义形态;`sections` 只属于 snapshot 形态,这里不写。
|
|
6
|
+
*
|
|
7
|
+
* @module dsh-thesis/autopilot
|
|
8
|
+
*/
|
|
9
|
+
import { randomUUID } from 'node:crypto';
|
|
10
|
+
import { PLUGIN_SOURCE } from "../gates/messages.js";
|
|
11
|
+
import { autopilotInstruction } from "./intent.js";
|
|
12
|
+
/** 构造启动指令消息(插件来源、instructions 形态)。 */
|
|
13
|
+
export function makeAutopilotMessage(choice, maxGoalRounds) {
|
|
14
|
+
return {
|
|
15
|
+
id: randomUUID(),
|
|
16
|
+
role: 'user',
|
|
17
|
+
content: [{ type: 'text', text: autopilotInstruction(choice, maxGoalRounds) }],
|
|
18
|
+
source: { ...PLUGIN_SOURCE, form: 'instructions' },
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 人工关卡的确认文案(`tools/pre-execute` 的 ask 决策,显示在审批界面)。
|
|
23
|
+
*
|
|
24
|
+
* 一关只问一次,问题只有一个:这一步你认不认。认了流程才会继续推进。
|
|
25
|
+
*/
|
|
26
|
+
export function gateApprovalReason(request) {
|
|
27
|
+
const target = request.chapter === undefined ? request.gate : `${request.gate}(${request.chapter})`;
|
|
28
|
+
return [
|
|
29
|
+
`【dsh-thesis · 人工关卡】流程要过「${target}」这一关。`,
|
|
30
|
+
'这是插件里唯一必须由你拍板的地方:过了这关,后面的产出就按它往下走。',
|
|
31
|
+
'',
|
|
32
|
+
'允许 = 这一关通过,流程继续;拒绝 = 打回,让模型先去补该补的东西。',
|
|
33
|
+
request.note === undefined ? '' : `模型给的备注:${request.note}`,
|
|
34
|
+
].filter(line => line !== '').join('\n');
|
|
35
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动驾驶用到的**最小宿主面**:只要真实宿主对象在结构上满足这些字段即可。
|
|
3
|
+
*
|
|
4
|
+
* 与 `codewalk/host-types.ts` 同一取舍:不在插件里 import `@deepseek-ai/dsh-agent` /
|
|
5
|
+
* `-goal` / `-user-questions`,离线单测才能在没有宿主包树的环境里跑;
|
|
6
|
+
* 真实宿主那边由 `scripts/host-smoke.ts` 复核。
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-thesis/autopilot
|
|
9
|
+
*/
|
|
10
|
+
/** 一个 agent 上真正用到的字段(委派深度、会话 cwd)。 */
|
|
11
|
+
export interface AutopilotAgent {
|
|
12
|
+
readonly id?: string;
|
|
13
|
+
readonly options?: {
|
|
14
|
+
readonly subagentDepth?: number;
|
|
15
|
+
};
|
|
16
|
+
readonly session?: {
|
|
17
|
+
readonly header?: {
|
|
18
|
+
readonly cwd?: string;
|
|
19
|
+
/** 持久化会话头里的委派深度:子 agent 大于 0(`delegationDepthOf` 的依据)。 */
|
|
20
|
+
readonly subagentDepth?: number;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** 一道选择题的选项。 */
|
|
25
|
+
export interface AutopilotOption {
|
|
26
|
+
readonly label: string;
|
|
27
|
+
readonly description?: string;
|
|
28
|
+
}
|
|
29
|
+
/** 一道选择题(宿主 `AskUserQuestionItem` 的子集)。 */
|
|
30
|
+
export interface AutopilotQuestion {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly question: string;
|
|
33
|
+
readonly header?: string;
|
|
34
|
+
readonly detail?: string;
|
|
35
|
+
readonly options?: readonly AutopilotOption[];
|
|
36
|
+
readonly multiSelect?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** 一个问题的答案(宿主 `AskUserQuestionAnswerItem`)。 */
|
|
39
|
+
export interface AutopilotAnswerItem {
|
|
40
|
+
readonly id: string;
|
|
41
|
+
readonly selected: readonly string[];
|
|
42
|
+
readonly custom?: string;
|
|
43
|
+
}
|
|
44
|
+
/** `ctx.userQuestions` 的最小面:问一次、拿结构化答案。 */
|
|
45
|
+
export interface UserQuestionsService {
|
|
46
|
+
ask(request: {
|
|
47
|
+
readonly questions: readonly AutopilotQuestion[];
|
|
48
|
+
readonly agent?: unknown;
|
|
49
|
+
readonly signal?: AbortSignal;
|
|
50
|
+
}): Promise<{
|
|
51
|
+
readonly answers?: readonly AutopilotAnswerItem[];
|
|
52
|
+
} | undefined>;
|
|
53
|
+
}
|
|
54
|
+
/** `ctx.goals` 的最小面:建一个目标,剩下的轮次由宿主的 goal-round-driver 驱动。 */
|
|
55
|
+
export interface GoalsService {
|
|
56
|
+
create(agent: unknown, request: {
|
|
57
|
+
readonly objective: string;
|
|
58
|
+
readonly maxGoalRounds?: number;
|
|
59
|
+
}): unknown;
|
|
60
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 自动驾驶用到的**最小宿主面**:只要真实宿主对象在结构上满足这些字段即可。
|
|
3
|
+
*
|
|
4
|
+
* 与 `codewalk/host-types.ts` 同一取舍:不在插件里 import `@deepseek-ai/dsh-agent` /
|
|
5
|
+
* `-goal` / `-user-questions`,离线单测才能在没有宿主包树的环境里跑;
|
|
6
|
+
* 真实宿主那边由 `scripts/host-smoke.ts` 复核。
|
|
7
|
+
*
|
|
8
|
+
* @module dsh-thesis/autopilot
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
package/lib/codewalk/engine.d.ts
CHANGED
|
@@ -103,6 +103,6 @@ export declare class AiLearningEngine {
|
|
|
103
103
|
recordGate(state: LearningState, milestoneId: string, input: Omit<GateRecord, 'milestone' | 'at'>): GateOutcome;
|
|
104
104
|
/** 只读快照,供状态视图、模型工具与注入使用。 */
|
|
105
105
|
snapshot(state: LearningState): EngineStateSnapshot;
|
|
106
|
-
/** `/thesis
|
|
106
|
+
/** `/thesis defense status` 用的人类可读状态文本。 */
|
|
107
107
|
describe(state: LearningState): string;
|
|
108
108
|
}
|
package/lib/codewalk/engine.js
CHANGED
|
@@ -358,7 +358,7 @@ export class AiLearningEngine {
|
|
|
358
358
|
doneQuestionCount,
|
|
359
359
|
};
|
|
360
360
|
}
|
|
361
|
-
/** `/thesis
|
|
361
|
+
/** `/thesis defense status` 用的人类可读状态文本。 */
|
|
362
362
|
describe(state) {
|
|
363
363
|
const view = this.snapshot(state);
|
|
364
364
|
const verified = state.milestones.filter((milestone) => milestone.status === 'verified').length;
|
package/lib/codewalk/gate.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 共享的验证门执行逻辑:通过 `ctx.shell` 跑一个里程碑的验证命令,把结论折进
|
|
3
3
|
* 引擎(记录 + 状态 + 落盘),再把结算后的结果交回给调用方渲染。
|
|
4
4
|
*
|
|
5
|
-
* 人类的 `/thesis
|
|
5
|
+
* 人类的 `/thesis defense check` 命令与模型侧的 `defense_code_update` `check_gate` 动作共用这段
|
|
6
6
|
* 逻辑,因此两个表面永远不会对「什么叫 verified」产生分歧。
|
|
7
7
|
*
|
|
8
8
|
* @module dsh-thesis/codewalk
|
package/lib/codewalk/gate.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* 共享的验证门执行逻辑:通过 `ctx.shell` 跑一个里程碑的验证命令,把结论折进
|
|
3
3
|
* 引擎(记录 + 状态 + 落盘),再把结算后的结果交回给调用方渲染。
|
|
4
4
|
*
|
|
5
|
-
* 人类的 `/thesis
|
|
5
|
+
* 人类的 `/thesis defense check` 命令与模型侧的 `defense_code_update` `check_gate` 动作共用这段
|
|
6
6
|
* 逻辑,因此两个表面永远不会对「什么叫 verified」产生分歧。
|
|
7
7
|
*
|
|
8
8
|
* @module dsh-thesis/codewalk
|
|
@@ -56,35 +56,6 @@ export interface AgentShape {
|
|
|
56
56
|
};
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
/** 传给某个已注册命令 handler 的调用信息。 */
|
|
60
|
-
export interface CommandInvocationShape {
|
|
61
|
-
readonly commandId: unknown;
|
|
62
|
-
readonly agent: AgentShape;
|
|
63
|
-
readonly rawInput: string;
|
|
64
|
-
readonly signal: AbortSignal;
|
|
65
|
-
}
|
|
66
|
-
/** 由调度 UI 直接渲染的命令结果。 */
|
|
67
|
-
export type CommandResultShape = {
|
|
68
|
-
readonly kind: 'success';
|
|
69
|
-
readonly text?: string;
|
|
70
|
-
} | {
|
|
71
|
-
readonly kind: 'error';
|
|
72
|
-
readonly text: string;
|
|
73
|
-
};
|
|
74
|
-
/** 插件自有命令注册(`ctx.commands.register`)。 */
|
|
75
|
-
export interface CommandDefinitionShape {
|
|
76
|
-
readonly name: string;
|
|
77
|
-
readonly description: string;
|
|
78
|
-
readonly input?: {
|
|
79
|
-
readonly hint: string;
|
|
80
|
-
};
|
|
81
|
-
readonly recordInput?: boolean;
|
|
82
|
-
handler(invocation: CommandInvocationShape): CommandResultShape | Promise<CommandResultShape>;
|
|
83
|
-
}
|
|
84
|
-
/** 命令注册表(`ctx.commands`);在 headless 组合里是可选的。 */
|
|
85
|
-
export interface CommandsServiceShape {
|
|
86
|
-
register(definition: CommandDefinitionShape): unknown;
|
|
87
|
-
}
|
|
88
59
|
/** 面向模型的消息或工具渲染中的一个文本内容块。 */
|
|
89
60
|
export interface TextBlockShape {
|
|
90
61
|
readonly type: 'text';
|
package/lib/codewalk/prestep.js
CHANGED
|
@@ -15,7 +15,7 @@ export function buildProgressMessage(state) {
|
|
|
15
15
|
const text = [
|
|
16
16
|
'当前工作区有一个进行中的代码演练(把自己的系统拆成讲得清的模块,并用可评分的追问确认你答辩时讲得出实现细节)。请带学生把它走完。',
|
|
17
17
|
'',
|
|
18
|
-
'和学生一起推进:带他做完当前里程碑的 todo,逐个提出追问,先沿提示阶梯升级再公布答案,并对照期望要点评分。用 defense_code_status、defense_code_next、defense_code_update 三个工具;只有学生说“做完了”才跑 "/thesis
|
|
18
|
+
'和学生一起推进:带他做完当前里程碑的 todo,逐个提出追问,先沿提示阶梯升级再公布答案,并对照期望要点评分。用 defense_code_status、defense_code_next、defense_code_update 三个工具;只有学生说“做完了”才跑 "/thesis defense check <milestone>"。',
|
|
19
19
|
'',
|
|
20
20
|
].join('\n');
|
|
21
21
|
const full = `${text}${JSON.stringify(state, null, 2)}`;
|
package/lib/codewalk/tools.js
CHANGED
|
@@ -84,7 +84,7 @@ async function toolCwd(engine, exec) {
|
|
|
84
84
|
}
|
|
85
85
|
const state = await engine.load(cwd);
|
|
86
86
|
if (state === undefined) {
|
|
87
|
-
throw new Error(`${cwd} 下没有代码演练状态;请先执行 "/thesis
|
|
87
|
+
throw new Error(`${cwd} 下没有代码演练状态;请先执行 "/thesis defense new <原始项目路径>"`);
|
|
88
88
|
}
|
|
89
89
|
return state;
|
|
90
90
|
}
|
|
@@ -138,7 +138,7 @@ function nextPayload(state) {
|
|
|
138
138
|
guidance: state.phase === 'analyzing'
|
|
139
139
|
? '先分析原始项目,用 defense_code_update 把架构记成 todo 与里程碑,然后推进阶段。'
|
|
140
140
|
: state.phase === 'skeletonizing'
|
|
141
|
-
? '开始搭骨架:剥掉非核心功能,留下带注释的 TODO 桩,用 /thesis
|
|
141
|
+
? '开始搭骨架:剥掉非核心功能,留下带注释的 TODO 桩,用 /thesis defense check 跑通编译验证门。'
|
|
142
142
|
: state.phase === 'learning'
|
|
143
143
|
? '推进当前里程碑:带学生走完它的 todo,逐个提出追问,并对照期望要点评分。'
|
|
144
144
|
: '全部里程碑已验证:总结学生现在能讲清哪些设计。',
|
package/lib/config.d.ts
CHANGED
|
@@ -76,6 +76,19 @@ export interface CodeWalkthroughOptions {
|
|
|
76
76
|
/** 每条门命令输出保留的最大字符数。 */
|
|
77
77
|
maxCapturedOutput: number;
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* 自动驾驶:插件在后台把流程推起来,人只在开头选一次意图、过关卡时拍一次板。
|
|
81
|
+
*
|
|
82
|
+
* 关掉它就是「纯工具模式」:没有启动选择题,也没有关卡确认,一切靠人和模型对话驱动。
|
|
83
|
+
*/
|
|
84
|
+
export interface AutopilotOptions {
|
|
85
|
+
/** 总开关(默认开)。 */
|
|
86
|
+
enabled: boolean;
|
|
87
|
+
/** 目标最多自动跑多少轮(防止跑飞)。 */
|
|
88
|
+
maxGoalRounds: number;
|
|
89
|
+
/** 模型要过 G1-G4 时,是否必须先由人在审批界面拍板(默认开)。 */
|
|
90
|
+
gateApproval: boolean;
|
|
91
|
+
}
|
|
79
92
|
/** 插件配置(全部可选,均有出厂默认值)。 */
|
|
80
93
|
export interface PaperConfig extends GateOptions {
|
|
81
94
|
/** 论文工作区根;缺省从会话 cwd 向上探测 `00-管理/进度台账.md`。 */
|
|
@@ -90,6 +103,8 @@ export interface PaperConfig extends GateOptions {
|
|
|
90
103
|
defense?: Partial<DefenseOptions>;
|
|
91
104
|
/** 代码演练(答辩层)。 */
|
|
92
105
|
codeWalkthrough?: Partial<CodeWalkthroughOptions>;
|
|
106
|
+
/** 自动驾驶(后台推进 + 开头一道选择题 + 关卡拍板)。 */
|
|
107
|
+
autopilot?: Partial<AutopilotOptions>;
|
|
93
108
|
/** 相关时把已确认的稳定事实放回上下文。 */
|
|
94
109
|
recall?: Partial<RecallOptions>;
|
|
95
110
|
}
|
|
@@ -102,6 +117,7 @@ export declare const PAPER_DEFAULTS: {
|
|
|
102
117
|
ppt: PptOptions;
|
|
103
118
|
defense: DefenseOptions;
|
|
104
119
|
codeWalkthrough: CodeWalkthroughOptions;
|
|
120
|
+
autopilot: AutopilotOptions;
|
|
105
121
|
recall: RecallOptions;
|
|
106
122
|
};
|
|
107
123
|
/** 解析后的配置:嵌套块已补全,调用方不再处理 undefined。 */
|
|
@@ -115,6 +131,7 @@ export interface ResolvedPaperConfig {
|
|
|
115
131
|
readonly ppt: PptOptions;
|
|
116
132
|
readonly defense: DefenseOptions;
|
|
117
133
|
readonly codeWalkthrough: CodeWalkthroughOptions;
|
|
134
|
+
readonly autopilot: AutopilotOptions;
|
|
118
135
|
readonly recall: RecallOptions;
|
|
119
136
|
/** 闸门配置:两个开关可选(缺省即由闸门自身按「开」处理),列表均已补全。 */
|
|
120
137
|
readonly gates: GateOptions;
|
package/lib/config.js
CHANGED
|
@@ -38,11 +38,12 @@ export const PAPER_DEFAULTS = {
|
|
|
38
38
|
},
|
|
39
39
|
maxCapturedOutput: 8000,
|
|
40
40
|
},
|
|
41
|
+
autopilot: { enabled: true, maxGoalRounds: 40, gateApproval: true },
|
|
41
42
|
recall: RECALL_DEFAULTS,
|
|
42
43
|
};
|
|
43
44
|
/** 补全嵌套块默认值。 */
|
|
44
45
|
export function resolveConfig(config = {}) {
|
|
45
|
-
const { workspace, memoryPath, ingest, intake, similarity, stylecheck, ppt, defense, codeWalkthrough, recall, writingGate, destructiveGate, writingIntentPatterns, criteriaMarkers, bypassMarkers, maxQuestions, destructiveToolNames, destructiveRules, } = config;
|
|
46
|
+
const { workspace, memoryPath, ingest, intake, similarity, stylecheck, ppt, defense, codeWalkthrough, autopilot, recall, writingGate, destructiveGate, writingIntentPatterns, criteriaMarkers, bypassMarkers, maxQuestions, destructiveToolNames, destructiveRules, } = config;
|
|
46
47
|
return {
|
|
47
48
|
workspace,
|
|
48
49
|
memoryPath,
|
|
@@ -57,6 +58,7 @@ export function resolveConfig(config = {}) {
|
|
|
57
58
|
...codeWalkthrough,
|
|
58
59
|
gates: { ...PAPER_DEFAULTS.codeWalkthrough.gates, ...codeWalkthrough?.gates },
|
|
59
60
|
},
|
|
61
|
+
autopilot: { ...PAPER_DEFAULTS.autopilot, ...autopilot },
|
|
60
62
|
recall: { ...PAPER_DEFAULTS.recall, ...recall },
|
|
61
63
|
gates: {
|
|
62
64
|
...(writingGate !== undefined ? { writingGate } : {}),
|
|
@@ -118,6 +120,12 @@ export const Config = z.object({
|
|
|
118
120
|
})).default(PAPER_DEFAULTS.codeWalkthrough.gates),
|
|
119
121
|
maxCapturedOutput: z.natural().default(PAPER_DEFAULTS.codeWalkthrough.maxCapturedOutput),
|
|
120
122
|
}),
|
|
123
|
+
// ---- 自动驾驶 ----
|
|
124
|
+
autopilot: z.object({
|
|
125
|
+
enabled: z.boolean().default(PAPER_DEFAULTS.autopilot.enabled),
|
|
126
|
+
maxGoalRounds: z.natural().min(1).default(PAPER_DEFAULTS.autopilot.maxGoalRounds),
|
|
127
|
+
gateApproval: z.boolean().default(PAPER_DEFAULTS.autopilot.gateApproval),
|
|
128
|
+
}),
|
|
121
129
|
// ---- 跨会话事实 ----
|
|
122
130
|
recall: z.object({
|
|
123
131
|
maxEntries: z.natural().default(PAPER_DEFAULTS.recall.maxEntries),
|
package/lib/dedup/index.js
CHANGED
|
@@ -18,7 +18,6 @@ import * as nodePath from 'node:path';
|
|
|
18
18
|
import { bulletList, definePaperTool, sessionCwd, textOutput } from "../shared/index.js";
|
|
19
19
|
import { CHAPTER_META, isWrittenChapter } from "../paper/lib/layout.js";
|
|
20
20
|
import { findThesisRoot } from "../paper/lib/project.js";
|
|
21
|
-
import { dedupCommand } from "./commands.js";
|
|
22
21
|
import { segmentsFromText } from "./normalize.js";
|
|
23
22
|
import { scan } from "./similarity.js";
|
|
24
23
|
import { buildReport, parseBaseline, renderReport, renderVerifyReport, verifyDelta, weightedRateOf, } from "./report.js";
|
|
@@ -322,10 +321,5 @@ export function registerDedup(ctx, options) {
|
|
|
322
321
|
return await runDedup(ctx.fs, sessionCwd(exec), rawArgs, options.similarity, exec.signal);
|
|
323
322
|
},
|
|
324
323
|
}));
|
|
325
|
-
// `/thesis-originality [scan|verify|report] [语料路径] [--chapter 01-绪论]`:与工具同一执行层,
|
|
326
|
-
// 命令只是给人用的快捷入口(命令名只允许 [a-z][a-z0-9_-]*)。
|
|
327
|
-
if (ctx.commands !== undefined) {
|
|
328
|
-
ctx.commands.register(dedupCommand(ctx.fs, options.similarity));
|
|
329
|
-
}
|
|
330
324
|
}
|
|
331
325
|
export { segmentsFromText, scan, buildReport, renderReport, weightedRateOf };
|
package/lib/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* dsh-thesis:论文与答辩全流程助手(DeepSeek Harness 单一插件)。
|
|
3
3
|
*
|
|
4
4
|
* 本文件是**唯一装配层**:把各能力挂到同一个插件 fiber 上,对外只有一行
|
|
5
|
-
* `dsh plugin add dsh-thesis`、一个 Config
|
|
5
|
+
* `dsh plugin add dsh-thesis`、一个 Config、一套技能。**没有斜杠命令**:
|
|
6
|
+
* 插件在论文工作区里自己把流程推起来(开头一道选择题 + 关卡拍板)。
|
|
6
7
|
*
|
|
7
8
|
* | 目录 | 职责 |
|
|
8
9
|
* |---|---|
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
* | `memory/` | 跨会话事实:学校规范、导师要求、写作偏好(学生不必直接调用) |
|
|
15
16
|
* | `codewalk/` | 代码演练:把 AI 写的系统拆成答辩讲得清的模块 |
|
|
16
17
|
* | `gates/` | 闸门:写作意图闸门 + 不可逆操作闸门 + 意图规格闸门 |
|
|
18
|
+
* | `autopilot/` | 自动驾驶:开头一道选择题 + 建目标自动续轮 + 关卡拍板(无斜杠命令) |
|
|
17
19
|
* | `shared/` | 工具胶水、工具定义器、文本与文件系统错误工具 |
|
|
18
20
|
*
|
|
19
21
|
* **一个领域模型**:材料 → 要求 → 九阶段 → 论文与答辩。学生的工具清单里
|
package/lib/index.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* dsh-thesis:论文与答辩全流程助手(DeepSeek Harness 单一插件)。
|
|
3
3
|
*
|
|
4
4
|
* 本文件是**唯一装配层**:把各能力挂到同一个插件 fiber 上,对外只有一行
|
|
5
|
-
* `dsh plugin add dsh-thesis`、一个 Config
|
|
5
|
+
* `dsh plugin add dsh-thesis`、一个 Config、一套技能。**没有斜杠命令**:
|
|
6
|
+
* 插件在论文工作区里自己把流程推起来(开头一道选择题 + 关卡拍板)。
|
|
6
7
|
*
|
|
7
8
|
* | 目录 | 职责 |
|
|
8
9
|
* |---|---|
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
* | `memory/` | 跨会话事实:学校规范、导师要求、写作偏好(学生不必直接调用) |
|
|
15
16
|
* | `codewalk/` | 代码演练:把 AI 写的系统拆成答辩讲得清的模块 |
|
|
16
17
|
* | `gates/` | 闸门:写作意图闸门 + 不可逆操作闸门 + 意图规格闸门 |
|
|
18
|
+
* | `autopilot/` | 自动驾驶:开头一道选择题 + 建目标自动续轮 + 关卡拍板(无斜杠命令) |
|
|
17
19
|
* | `shared/` | 工具胶水、工具定义器、文本与文件系统错误工具 |
|
|
18
20
|
*
|
|
19
21
|
* **一个领域模型**:材料 → 要求 → 九阶段 → 论文与答辩。学生的工具清单里
|
|
@@ -22,8 +24,8 @@
|
|
|
22
24
|
* @module dsh-thesis
|
|
23
25
|
*/
|
|
24
26
|
import { fileURLToPath } from 'node:url';
|
|
27
|
+
import { installAutopilot } from "./autopilot/index.js";
|
|
25
28
|
import { registerCodewalk } from "./codewalk/register.js";
|
|
26
|
-
import { registerDefenseCommand } from "./commands.js";
|
|
27
29
|
import { Config, resolveConfig } from "./config.js";
|
|
28
30
|
import { registerDedup } from "./dedup/index.js";
|
|
29
31
|
import { installDangerousGate } from "./gates/dangerous.js";
|
|
@@ -36,7 +38,7 @@ import { setWorkspaceRoot } from "./paper/lib/project.js";
|
|
|
36
38
|
import { registerPaper } from "./paper/register.js";
|
|
37
39
|
import { registerPpt } from "./ppt/index.js";
|
|
38
40
|
export const name = 'dsh-thesis';
|
|
39
|
-
export const inject = ['tools', 'fs'
|
|
41
|
+
export const inject = ['tools', 'fs'];
|
|
40
42
|
export { Config };
|
|
41
43
|
export { PAPER_DEFAULTS, resolveConfig } from "./config.js";
|
|
42
44
|
/** 随包发布的技能目录(`thesis_init` 复制到论文仓库 `.dsh/skills/`)。 */
|
|
@@ -55,20 +57,21 @@ export function apply(ctx, config = {}) {
|
|
|
55
57
|
registerFacts(ctx, resolved.memoryPath);
|
|
56
58
|
// 事实回灌:相关时自动把已确认的稳定事实放回上下文(不相关则完全静默)。
|
|
57
59
|
installFactRecall(ctx, resolved.recall);
|
|
58
|
-
// 论文工作区:11
|
|
60
|
+
// 论文工作区:11 个工具。
|
|
59
61
|
registerPaper(ctx, { skillsDir: SKILLS_DIR });
|
|
60
62
|
// 代码演练(答辩层):3 个工具 + 进度卡注入 + code-walkthrough 技能。
|
|
61
63
|
registerCodewalk(ctx, resolved.codeWalkthrough);
|
|
62
64
|
// 材料入口:Office 摄取 → 文本与材料清单。
|
|
63
65
|
registerIngest(ctx, { ingest: resolved.ingest });
|
|
64
|
-
//
|
|
66
|
+
// 意图规格:逐题追问状态机。
|
|
65
67
|
registerIntake(ctx, { intake: resolved.intake });
|
|
66
68
|
// 原创性自查:本地度量 + 改写处方 + 复测。
|
|
67
69
|
registerDedup(ctx, { similarity: resolved.similarity });
|
|
68
70
|
// 答辩:Markdown 幻灯 + 外部转换器探测。
|
|
69
71
|
registerPpt(ctx, { ppt: resolved.ppt });
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
+
// 自动驾驶:论文工作区里开口就问一次意图 → 建目标自动续轮;关卡由人拍板。
|
|
73
|
+
// 不提供任何斜杠命令:人只在「开头选一次」与「过关卡」两处出现。
|
|
74
|
+
installAutopilot(ctx, { ...resolved.autopilot, specRel: resolved.intake.specRel });
|
|
72
75
|
// 闸门:写作意图闸门 + 不可逆操作闸门 + 意图规格闸门。
|
|
73
76
|
installWritingGate(ctx, resolved.gates);
|
|
74
77
|
installDangerousGate(ctx, resolved.gates);
|
package/lib/ingest/index.d.ts
CHANGED
|
@@ -175,5 +175,5 @@ export interface RegisterIngestOptions {
|
|
|
175
175
|
/** 摄取配置(`maxBytes`/`maxChars`,来自 `src/config.ts`)。 */
|
|
176
176
|
readonly ingest: IngestOptions;
|
|
177
177
|
}
|
|
178
|
-
/** 注册 `thesis_ingest`(同步注册,走仓库统一的 `definePaperTool
|
|
178
|
+
/** 注册 `thesis_ingest`(同步注册,走仓库统一的 `definePaperTool`;本插件不提供斜杠命令)。 */
|
|
179
179
|
export declare function registerIngest(ctx: Context, options: RegisterIngestOptions): void;
|
package/lib/ingest/index.js
CHANGED
|
@@ -14,7 +14,6 @@ import { readdir, readFile, stat } from 'node:fs/promises';
|
|
|
14
14
|
import * as nodePath from 'node:path';
|
|
15
15
|
import { findThesisRoot } from "../paper/lib/project.js";
|
|
16
16
|
import { bulletList, definePaperTool, sessionCwd, textOutput } from "../shared/index.js";
|
|
17
|
-
import { ingestCommand } from "./commands.js";
|
|
18
17
|
import { codeLanguageOf, detectTechStack, isManifestFile, renderProjectSummary, summarizeSource, } from "./code.js";
|
|
19
18
|
import { extractDocx } from "./docx.js";
|
|
20
19
|
import { extractPdf } from "./pdf.js";
|
|
@@ -615,7 +614,7 @@ export function renderOutcome(outcome, maxChars) {
|
|
|
615
614
|
return text;
|
|
616
615
|
return `${text.slice(0, Math.max(0, maxChars - 20))}\n…(清单过长已截断)`;
|
|
617
616
|
}
|
|
618
|
-
/** 注册 `thesis_ingest`(同步注册,走仓库统一的 `definePaperTool
|
|
617
|
+
/** 注册 `thesis_ingest`(同步注册,走仓库统一的 `definePaperTool`;本插件不提供斜杠命令)。 */
|
|
619
618
|
export function registerIngest(ctx, options) {
|
|
620
619
|
ctx.tools.register(definePaperTool({
|
|
621
620
|
name: 'thesis_ingest',
|
|
@@ -636,5 +635,4 @@ export function registerIngest(ctx, options) {
|
|
|
636
635
|
return renderOutcome(outcome, options.ingest.maxChars);
|
|
637
636
|
},
|
|
638
637
|
}));
|
|
639
|
-
ctx.commands.register(ingestCommand(ctx.fs, options.ingest));
|
|
640
638
|
}
|
package/lib/intake/index.d.ts
CHANGED
|
@@ -57,20 +57,3 @@ export interface IntakeExecLike {
|
|
|
57
57
|
};
|
|
58
58
|
};
|
|
59
59
|
}
|
|
60
|
-
/** 命令返回值(宿主 `CommandResult` 的形状)。 */
|
|
61
|
-
export interface IntakeCommandResult {
|
|
62
|
-
readonly kind: 'success' | 'error';
|
|
63
|
-
readonly text?: string;
|
|
64
|
-
}
|
|
65
|
-
/** 把 `/thesis-intake` 的一行输入翻译成追问动作。 */
|
|
66
|
-
export declare function parseCommandInput(raw: string): IntakeArgs;
|
|
67
|
-
/**
|
|
68
|
-
* 注册命令 `/thesis-intake`(命令名只允许 `[a-z][a-z0-9_-]*`)。
|
|
69
|
-
*
|
|
70
|
-
* 用法:`/thesis-intake` 取下一个问题;`/thesis-intake status|spec|done|start`;
|
|
71
|
-
* `/thesis-intake answer school.name XX大学`;`/thesis-intake skip ref.samples 暂时没有`。
|
|
72
|
-
* `ctx.commands` 不可用(未装配命令服务的宿主)时静默跳过,不影响工具能力。
|
|
73
|
-
*/
|
|
74
|
-
export declare function registerIntakeCommand(ctx: Context, options: {
|
|
75
|
-
intake: IntakeSubOptions;
|
|
76
|
-
}): boolean;
|