dsh-thesis 1.0.1 → 2.0.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.
Files changed (74) hide show
  1. package/LICENSE +1 -1
  2. package/NOTICE.md +1 -1
  3. package/README.md +51 -86
  4. package/docs/autopilot.md +73 -0
  5. package/docs/config.md +113 -0
  6. package/{DESIGN.md → docs/design.md} +69 -74
  7. package/docs/host-api.md +49 -6
  8. package/docs/release.md +138 -0
  9. package/docs/tools.md +76 -0
  10. package/docs/troubleshooting.md +88 -0
  11. package/docs/usage.md +87 -0
  12. package/lib/autopilot/index.d.ts +38 -0
  13. package/lib/autopilot/index.js +159 -0
  14. package/lib/autopilot/intent.d.ts +40 -0
  15. package/lib/autopilot/intent.js +82 -0
  16. package/lib/autopilot/messages.d.ts +23 -0
  17. package/lib/autopilot/messages.js +36 -0
  18. package/lib/autopilot/types.d.ts +60 -0
  19. package/lib/autopilot/types.js +10 -0
  20. package/lib/codewalk/engine.d.ts +1 -1
  21. package/lib/codewalk/engine.js +1 -1
  22. package/lib/codewalk/gate.d.ts +1 -1
  23. package/lib/codewalk/gate.js +1 -1
  24. package/lib/codewalk/host-types.d.ts +2 -31
  25. package/lib/codewalk/prestep.js +3 -3
  26. package/lib/codewalk/tools.js +2 -2
  27. package/lib/config.d.ts +17 -0
  28. package/lib/config.js +9 -1
  29. package/lib/dedup/index.js +0 -6
  30. package/lib/gates/messages.d.ts +7 -6
  31. package/lib/gates/messages.js +8 -5
  32. package/lib/index.d.ts +3 -1
  33. package/lib/index.js +10 -7
  34. package/lib/ingest/index.d.ts +1 -1
  35. package/lib/ingest/index.js +1 -3
  36. package/lib/intake/gate-rules.d.ts +1 -2
  37. package/lib/intake/gate-rules.js +2 -2
  38. package/lib/intake/index.d.ts +0 -17
  39. package/lib/intake/index.js +0 -51
  40. package/lib/intake/questions.d.ts +1 -1
  41. package/lib/intake/questions.js +1 -1
  42. package/lib/memory/recall.d.ts +1 -2
  43. package/lib/memory/recall.js +2 -2
  44. package/lib/paper/lib/layout.d.ts +1 -1
  45. package/lib/paper/lib/layout.js +2 -2
  46. package/lib/paper/lib/ledger.d.ts +1 -1
  47. package/lib/paper/lib/ledger.js +1 -1
  48. package/lib/paper/register.d.ts +2 -2
  49. package/lib/paper/register.js +2 -8
  50. package/lib/paper/tools/decision.js +1 -1
  51. package/lib/paper/tools/init.d.ts +1 -1
  52. package/lib/paper/tools/init.js +1 -1
  53. package/lib/ppt/index.d.ts +1 -2
  54. package/lib/ppt/index.js +1 -2
  55. package/lib/shared/define-tool.d.ts +1 -1
  56. package/lib/shared/define-tool.js +1 -1
  57. package/lib/shared/message-source.d.ts +21 -0
  58. package/lib/shared/message-source.js +19 -0
  59. package/package.json +17 -17
  60. package/skills/code-walkthrough/SKILL.md +5 -3
  61. package/skills/thesis-defense/SKILL.md +1 -1
  62. package/skills/thesis-pipeline/SKILL.md +3 -3
  63. package/lib/codewalk/commands.d.ts +0 -23
  64. package/lib/codewalk/commands.js +0 -168
  65. package/lib/commands.d.ts +0 -33
  66. package/lib/commands.js +0 -60
  67. package/lib/dedup/commands.d.ts +0 -21
  68. package/lib/dedup/commands.js +0 -93
  69. package/lib/ingest/commands.d.ts +0 -23
  70. package/lib/ingest/commands.js +0 -74
  71. package/lib/paper/commands.d.ts +0 -13
  72. package/lib/paper/commands.js +0 -84
  73. package/lib/ppt/commands.d.ts +0 -28
  74. package/lib/ppt/commands.js +0 -71
@@ -74,7 +74,7 @@ thesis_slides convert → 探测 marp / npx marp-cli / pandoc;有则产
74
74
  thesis_slides guide → 只输出转换指引,不执行
75
75
  ```
76
76
 
77
- 等价斜杠命令:`/thesis-defense prepare [页数] [undergrad|master] [force]`、`convert`、`check`、`guide`。
77
+ 本插件没有斜杠命令:幻灯侧全部通过 `thesis_slides` 工具驱动(`action` 取 `outline` / `convert` / `check` / `guide`)。
78
78
 
79
79
  硬约束(`check` 会逐条查):每页要点 3-6 条、单条 ≤ 30 字、每页必须有讲稿与证据锚点、
80
80
  不得出现"待补充/TODO"、预计时长 8-12 分钟(每页 40-60 秒)。
@@ -77,7 +77,7 @@ description: 计算机类本科毕业设计全流程总纲。任何涉及论文
77
77
  - 答辩前要讲清自己的系统:加载 `code-walkthrough`
78
78
  - 写作要求被质疑、导师口径模糊:加载 `thesis-inquiry`
79
79
 
80
- ## 插件工具地图(dsh-thesis 全集:21 个工具 / 9 个命令)
80
+ ## 插件工具地图(dsh-thesis 全集:21 个工具 / 1 条命令 9 个动词)
81
81
 
82
82
  命名空间规则:`thesis_*` 操作论文工作区里的东西;`fact_*` 操作跨会话的稳定事实;
83
83
  `defense_code_*` 是答辩层的代码演练。
@@ -91,9 +91,9 @@ description: 计算机类本科毕业设计全流程总纲。任何涉及论文
91
91
  | 写作 | `thesis_review` / `thesis_check` / `thesis_build` | 逐章评审 → 全文一致性检查 → Word 构建 |
92
92
  | 合规 | `thesis_stylecheck` / `thesis_originality` | 写作风格自查(AI 味)/ 原创性本地度量与复测 |
93
93
  | 答辩 | `thesis_defense` / `thesis_slides` | 答辩素材与问题库 / 幻灯 Markdown 与 pptx 转换 |
94
- | 答辩 | `defense_code_status` / `defense_code_next` / `defense_code_update` | 代码演练:把系统拆成讲得清的模块(`/thesis-defense` 命令) |
94
+ | 答辩 | `defense_code_status` / `defense_code_next` / `defense_code_update` | 代码演练:把系统拆成讲得清的模块 |
95
95
  | 事实 | `fact_search` / `fact_remember` / `fact_context` | 跨会话事实(学校规范、导师要求、写作偏好) |
96
- | 命令 | `/thesis-status` `/thesis-decide` `/thesis-lit` `/thesis-check` `/thesis-build` `/thesis-ingest` `/thesis-intake` `/thesis-originality` `/thesis-defense` | 上述工具的斜杠入口 |
96
+ | 入口 | 无斜杠命令 | 本插件不提供命令行:在论文工作区开口说话,插件问一道选择题后自己推进;过 G1-G4 时由人拍板 |
97
97
 
98
98
  ## 会话入口顺序(新会话照这个走)
99
99
 
@@ -1,23 +0,0 @@
1
- /**
2
- * 人类可用的 `/thesis-defense` 命令:`new`、`status`、`check`。
3
- *
4
- * 只有在组合了命令适配器时才通过 `ctx.commands` 注册(交互式部署);纯 headless
5
- * 与仅 ACP 的组合没有命令注册表,直接跳过这一层表面。命令面的结果直接渲染,
6
- * 不进入模型历史。
7
- *
8
- * @module dsh-thesis/codewalk
9
- */
10
- import type { Context } from '@deepseek-ai/cordis';
11
- import { AiLearningEngine } from './engine.ts';
12
- import type { CommandInvocationShape, CommandResultShape } from './host-types.ts';
13
- /** 代码演练侧的子命令(与幻灯侧区分)。 */
14
- export declare const CODEWALK_SUBCOMMANDS: readonly string[];
15
- /** 命令的展示元信息(装配层注册 `/thesis-defense` 时使用)。 */
16
- export declare const CODEWALK_COMMAND_HINT = "new <\u539F\u59CB\u9879\u76EE\u8DEF\u5F84> [--lang go] [--level beginner] [--module pkg] | status | check [\u91CC\u7A0B\u7891]";
17
- /**
18
- * 代码演练侧的命令处理器(由装配层在 `/thesis-defense` 下调度)。
19
- *
20
- * `rawInput` 是命令名之后的全部输入;本函数只认代码演练侧的子命令
21
- * (`new` / `status` / `check`),其余一律交回装配层。
22
- */
23
- export declare function handleCodewalkCommand(ctx: Context, engine: AiLearningEngine, invocation: CommandInvocationShape): Promise<CommandResultShape>;
@@ -1,168 +0,0 @@
1
- /**
2
- * 人类可用的 `/thesis-defense` 命令:`new`、`status`、`check`。
3
- *
4
- * 只有在组合了命令适配器时才通过 `ctx.commands` 注册(交互式部署);纯 headless
5
- * 与仅 ACP 的组合没有命令注册表,直接跳过这一层表面。命令面的结果直接渲染,
6
- * 不进入模型历史。
7
- *
8
- * @module dsh-thesis/codewalk
9
- */
10
- import { AiLearningEngine } from "./engine.js";
11
- import { isLearningError } from "./errors.js";
12
- import { runMilestoneGate } from "./gate.js";
13
- const DEFAULT_LANGUAGE = 'go';
14
- const LEVELS = ['beginner', 'intermediate', 'advanced'];
15
- /** 会话没有工作目录时的统一答复。 */
16
- const NO_WORKSPACE = {
17
- kind: 'error',
18
- text: '本次会话没有工作目录,无法定位代码演练状态。请在论文工作区目录里打开会话。',
19
- };
20
- /** 代码演练侧的子命令(与幻灯侧区分)。 */
21
- export const CODEWALK_SUBCOMMANDS = ['new', 'status', 'check'];
22
- /** 命令的展示元信息(装配层注册 `/thesis-defense` 时使用)。 */
23
- export const CODEWALK_COMMAND_HINT = 'new <原始项目路径> [--lang go] [--level beginner] [--module pkg] | status | check [里程碑]';
24
- /**
25
- * 代码演练侧的命令处理器(由装配层在 `/thesis-defense` 下调度)。
26
- *
27
- * `rawInput` 是命令名之后的全部输入;本函数只认代码演练侧的子命令
28
- * (`new` / `status` / `check`),其余一律交回装配层。
29
- */
30
- export async function handleCodewalkCommand(ctx, engine, invocation) {
31
- try {
32
- const tokens = invocation.rawInput.trim().split(/\s+/);
33
- const sub = tokens[0] ?? '';
34
- const args = tokens.slice(1);
35
- if (sub === 'new')
36
- return await handleNew(engine, invocation, args);
37
- if (sub === 'status')
38
- return await handleStatus(engine, invocation);
39
- if (sub === 'check')
40
- return await handleCheck(ctx, engine, invocation, args);
41
- return { kind: 'error', text: `未知子命令 "${sub}";代码演练侧应为 new | status | check` };
42
- }
43
- catch (error) {
44
- if (isLearningError(error))
45
- return { kind: 'error', text: error.message };
46
- const message = error instanceof Error ? error.message : String(error);
47
- return { kind: 'error', text: `内部错误:${message}` };
48
- }
49
- }
50
- function parseNewArgs(args) {
51
- const options = { language: DEFAULT_LANGUAGE, level: 'beginner' };
52
- const positional = [];
53
- for (let index = 0; index < args.length; index += 1) {
54
- const arg = args[index];
55
- if (arg === '--lang' || arg === '--level' || arg === '--module') {
56
- const value = args[index + 1];
57
- if (value === undefined)
58
- continue;
59
- if (arg === '--lang')
60
- options.language = value;
61
- else if (arg === '--level')
62
- options.level = value;
63
- else
64
- options.module = value;
65
- index += 1;
66
- continue;
67
- }
68
- if (arg.startsWith('--lang='))
69
- options.language = arg.slice('--lang='.length);
70
- else if (arg.startsWith('--level='))
71
- options.level = arg.slice('--level='.length);
72
- else if (arg.startsWith('--module='))
73
- options.module = arg.slice('--module='.length);
74
- else
75
- positional.push(arg);
76
- }
77
- const first = positional[0];
78
- if (first !== undefined)
79
- options.path = first;
80
- return options;
81
- }
82
- async function handleNew(engine, invocation, args) {
83
- const options = parseNewArgs(args);
84
- if (options.path === undefined) {
85
- return {
86
- kind: 'error',
87
- text: '用法:/thesis-defense new <原始项目路径> [--lang go] [--level beginner] [--module pkg]',
88
- };
89
- }
90
- if (!LEVELS.includes(options.level)) {
91
- return { kind: 'error', text: `非法的 level "${options.level}";应为 ${LEVELS.join(' | ')}` };
92
- }
93
- const cwd = invocation.agent.session.header.cwd;
94
- if (cwd === undefined || cwd === '')
95
- return NO_WORKSPACE;
96
- const state = await engine.create(cwd, {
97
- origin: { path: options.path, language: options.language },
98
- scope: { level: options.level, ...(options.module !== undefined ? { module: options.module } : {}) },
99
- });
100
- return {
101
- kind: 'success',
102
- text: `已为 ${options.path} 创建代码演练(${options.language},${options.level})。\n` +
103
- `${engine.describe(state)}\n` +
104
- `下一步:让 AI 分析该项目,用 defense_code_update 工具登记 todo 与里程碑。`,
105
- };
106
- }
107
- async function handleStatus(engine, invocation) {
108
- const cwd = invocation.agent.session.header.cwd;
109
- if (cwd === undefined || cwd === '')
110
- return NO_WORKSPACE;
111
- const state = await engine.load(cwd);
112
- if (state === undefined) {
113
- return { kind: 'error', text: `${cwd} 下没有代码演练状态;请先执行 "/thesis-defense new <原始项目路径>"` };
114
- }
115
- return { kind: 'success', text: engine.describe(state) };
116
- }
117
- async function handleCheck(ctx, engine, invocation, args) {
118
- const cwd = invocation.agent.session.header.cwd;
119
- if (cwd === undefined || cwd === '')
120
- return NO_WORKSPACE;
121
- const state = await engine.load(cwd);
122
- if (state === undefined) {
123
- return { kind: 'error', text: `${cwd} 下没有代码演练状态;请先执行 "/thesis-defense new <原始项目路径>"` };
124
- }
125
- const milestone = pickMilestone(state.milestones, args[0]);
126
- if (milestone === undefined) {
127
- return {
128
- kind: 'error',
129
- text: args[0] === undefined
130
- ? '当前没有 in_progress 或 failed 的里程碑,没有可检查的对象'
131
- : `未知里程碑 "${args[0]}"`,
132
- };
133
- }
134
- if (milestone.status === 'failed')
135
- engine.retryMilestone(state, milestone.id);
136
- const settled = await runMilestoneGate(ctx, engine, cwd, state, milestone, invocation.signal, invocation.agent.session);
137
- if (settled.interrupted) {
138
- return { kind: 'error', text: `验证门 "${settled.command.join(' ')}" 被中断;该里程碑已标记为 failed` };
139
- }
140
- if (settled.outcome.kind === 'failed') {
141
- return {
142
- kind: 'error',
143
- text: `验证门 "${settled.command.join(' ')}" 失败,退出码 ${settled.exitCode};该里程碑已标记为 failed。\n` +
144
- (settled.stderr.trim() === '' ? `stdout 末尾:\n${settled.stdout}` : `stderr 末尾:\n${settled.stderr}`),
145
- };
146
- }
147
- if (settled.outcome.kind === 'pending-questions') {
148
- return {
149
- kind: 'success',
150
- text: `验证门 "${settled.command.join(' ')}" 通过,但仍有 ${milestone.questions.filter((question) => question.status !== 'passed').length} 个问题未通过。\n` +
151
- '让 AI 把追问做完;当验证门为绿且全部问题通过时,该里程碑才算 verified。',
152
- };
153
- }
154
- const next = state.milestones.find((entry) => entry.status === 'in_progress');
155
- const complete = state.phase === 'complete';
156
- return {
157
- kind: 'success',
158
- text: `验证门 "${settled.command.join(' ')}" 通过 —— 里程碑 "${milestone.id}" 已验证。\n` +
159
- (complete ? '全部里程碑已验证:本次代码演练完成。' : `下一个里程碑:${next?.title ?? '无'}。`),
160
- };
161
- }
162
- function pickMilestone(milestones, requestedId) {
163
- if (requestedId !== undefined) {
164
- return milestones.find((milestone) => milestone.id === requestedId);
165
- }
166
- return (milestones.find((milestone) => milestone.status === 'in_progress') ??
167
- milestones.find((milestone) => milestone.status === 'failed'));
168
- }
package/lib/commands.d.ts DELETED
@@ -1,33 +0,0 @@
1
- /**
2
- * 斜杠命令的聚合层。
3
- *
4
- * `/thesis-defense` 同时覆盖**答辩幻灯**与**代码演练**两块能力,因此它的注册
5
- * 放在装配层:`ppt/` 与 `codewalk/` 各自导出处理器,由本文件按子命令调度,
6
- * 两个模块之间没有反向依赖(见 DESIGN.md 的分层规则)。
7
- *
8
- * 命令只做薄封装:解析输入 → 交给对应模块的纯逻辑入口,因此命令行为与工具完全一致。
9
- *
10
- * @module dsh-thesis/commands
11
- */
12
- import type { CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands';
13
- import type { Context } from '@deepseek-ai/cordis';
14
- import type { PptOptions } from './config.ts';
15
- import type { PptDeps } from './ppt/actions.ts';
16
- /** 答辩命令两侧的子命令集合(仅用于错误提示)。 */
17
- export declare const DEFENSE_COMMAND_HINT = "prepare | convert | check | guide [\u9875\u6570] [undergrad|master] [force] | new <\u539F\u59CB\u9879\u76EE\u8DEF\u5F84> [--lang go] [--level beginner] [--module pkg] | status | check [\u91CC\u7A0B\u7891]";
18
- /**
19
- * 注册 `/thesis-defense`:答辩幻灯 + 代码演练。
20
- *
21
- * 幻灯侧子命令:`prepare`(= outline)/ `convert` / `check` / `guide`;
22
- * 代码演练侧子命令:`new` / `status` / `check`。`check` 两侧同名,按是否给出
23
- * 里程碑参数区分——幻灯侧的 check 不接受位置参数。
24
- *
25
- * headless/ACP 组合没有命令面时静默跳过。
26
- */
27
- export declare function registerDefenseCommand(ctx: Context, options: {
28
- ppt: PptOptions;
29
- }, deps?: PptDeps): void;
30
- /** 按子命令把一次 `/thesis-defense` 调用分派到幻灯侧或代码演练侧。 */
31
- export declare function dispatchDefenseCommand(ctx: Context, options: {
32
- ppt: PptOptions;
33
- }, invocation: CommandInvocation, deps?: PptDeps): Promise<CommandResult>;
package/lib/commands.js DELETED
@@ -1,60 +0,0 @@
1
- /**
2
- * 斜杠命令的聚合层。
3
- *
4
- * `/thesis-defense` 同时覆盖**答辩幻灯**与**代码演练**两块能力,因此它的注册
5
- * 放在装配层:`ppt/` 与 `codewalk/` 各自导出处理器,由本文件按子命令调度,
6
- * 两个模块之间没有反向依赖(见 DESIGN.md 的分层规则)。
7
- *
8
- * 命令只做薄封装:解析输入 → 交给对应模块的纯逻辑入口,因此命令行为与工具完全一致。
9
- *
10
- * @module dsh-thesis/commands
11
- */
12
- import { CODEWALK_COMMAND_HINT, handleCodewalkCommand } from "./codewalk/commands.js";
13
- import { handlePptCommand, PPT_COMMAND_DESCRIPTION, PPT_COMMAND_HINT, PPT_SUBCOMMANDS } from "./ppt/commands.js";
14
- /** 答辩命令两侧的子命令集合(仅用于错误提示)。 */
15
- export const DEFENSE_COMMAND_HINT = `${PPT_COMMAND_HINT} | ${CODEWALK_COMMAND_HINT}`;
16
- /**
17
- * 注册 `/thesis-defense`:答辩幻灯 + 代码演练。
18
- *
19
- * 幻灯侧子命令:`prepare`(= outline)/ `convert` / `check` / `guide`;
20
- * 代码演练侧子命令:`new` / `status` / `check`。`check` 两侧同名,按是否给出
21
- * 里程碑参数区分——幻灯侧的 check 不接受位置参数。
22
- *
23
- * headless/ACP 组合没有命令面时静默跳过。
24
- */
25
- export function registerDefenseCommand(ctx, options, deps = {}) {
26
- const commands = ctx.get('commands');
27
- if (commands === undefined)
28
- return;
29
- commands.register({
30
- name: 'thesis-defense',
31
- description: `答辩准备:幻灯与演练。${PPT_COMMAND_DESCRIPTION};代码演练:new <原始项目路径> | status | check [里程碑]`,
32
- input: { hint: DEFENSE_COMMAND_HINT },
33
- handler: async (invocation) => {
34
- return await dispatchDefenseCommand(ctx, options, invocation, deps);
35
- },
36
- });
37
- }
38
- /** 按子命令把一次 `/thesis-defense` 调用分派到幻灯侧或代码演练侧。 */
39
- export async function dispatchDefenseCommand(ctx, options, invocation, deps = {}) {
40
- const tokens = invocation.rawInput.trim().split(/\s+/).filter(token => token !== '');
41
- const sub = (tokens[0] ?? 'prepare').toLowerCase();
42
- if (PPT_SUBCOMMANDS.includes(sub)) {
43
- return await handlePptCommand(ctx.fs, options, invocation, deps);
44
- }
45
- if (sub === 'new' || sub === 'status' || (sub === 'check' && tokens.length > 1)) {
46
- const engine = ctx.get('paperCodeWalkthrough');
47
- if (engine === undefined) {
48
- return { kind: 'error', text: '代码演练引擎未装配:请确认插件配置里 codeWalkthrough 已启用。' };
49
- }
50
- return await handleCodewalkCommand(ctx, engine, invocation);
51
- }
52
- if (sub === 'check') {
53
- // 无里程碑参数的 check 属于幻灯侧质量检查。
54
- return await handlePptCommand(ctx.fs, options, invocation, deps);
55
- }
56
- return {
57
- kind: 'error',
58
- text: `未知子命令「${sub}」。可用:${DEFENSE_COMMAND_HINT}`,
59
- };
60
- }
@@ -1,21 +0,0 @@
1
- /**
2
- * 斜杠命令:`/thesis-originality`。
3
- *
4
- * 命令名遵循 DSH 的 `[a-z][a-z0-9_-]*` 约束(kebab-case),与 `/thesis-check` 等同一套写法。
5
- * 命令只是 `thesis_originality` 工具的薄封装:把命令行输入解析成 `DedupArgs` 后交给 `runDedup`,
6
- * 因此行为与工具**完全一致**,不存在"命令和工具两条逻辑"的漂移。
7
- *
8
- * @module dsh-thesis/dedup
9
- */
10
- import type { FileSystem } from '@deepseek-ai/dsh-fs';
11
- import type { SimilarityOptions } from '../config.ts';
12
- import { type DedupArgs } from './index.ts';
13
- /**
14
- * 解析 `/thesis-originality` 的输入。
15
- *
16
- * 支持:`scan|verify|report`、位置参数(语料路径)、`--chapter <章节>`、
17
- * `--baseline <报告路径>`、`--rate <重复率>`、`--system <检测系统名>`。
18
- */
19
- export declare function parseDedupCommandInput(rawInput: string): DedupArgs;
20
- /** 构造 `/thesis-originality` 命令定义。 */
21
- export declare function dedupCommand(fs: FileSystem, similarity: SimilarityOptions): unknown;
@@ -1,93 +0,0 @@
1
- /**
2
- * 斜杠命令:`/thesis-originality`。
3
- *
4
- * 命令名遵循 DSH 的 `[a-z][a-z0-9_-]*` 约束(kebab-case),与 `/thesis-check` 等同一套写法。
5
- * 命令只是 `thesis_originality` 工具的薄封装:把命令行输入解析成 `DedupArgs` 后交给 `runDedup`,
6
- * 因此行为与工具**完全一致**,不存在"命令和工具两条逻辑"的漂移。
7
- *
8
- * @module dsh-thesis/dedup
9
- */
10
- import { runDedup } from "./index.js";
11
- const ACTIONS = ['scan', 'verify', 'report'];
12
- async function safe(work) {
13
- try {
14
- return { kind: 'success', text: await work() };
15
- }
16
- catch (error) {
17
- return { kind: 'error', text: error instanceof Error ? error.message : String(error) };
18
- }
19
- }
20
- /**
21
- * 解析 `/thesis-originality` 的输入。
22
- *
23
- * 支持:`scan|verify|report`、位置参数(语料路径)、`--chapter <章节>`、
24
- * `--baseline <报告路径>`、`--rate <重复率>`、`--system <检测系统名>`。
25
- */
26
- export function parseDedupCommandInput(rawInput) {
27
- const tokens = rawInput.trim().split(/\s+/).filter(token => token !== '');
28
- let action = 'scan';
29
- const paths = [];
30
- let chapter;
31
- let baseline;
32
- let detectedRate;
33
- let detectedSystem;
34
- for (let index = 0; index < tokens.length; index += 1) {
35
- const token = tokens[index];
36
- const lowered = token.toLowerCase();
37
- if (ACTIONS.includes(lowered)) {
38
- action = lowered;
39
- continue;
40
- }
41
- if (lowered === '--chapter' || lowered === '-c') {
42
- chapter = tokens[++index];
43
- if (chapter === undefined)
44
- throw new Error('--chapter 后面要跟章节名,如 --chapter 01-绪论');
45
- continue;
46
- }
47
- if (lowered === '--baseline' || lowered === '-b') {
48
- baseline = tokens[++index];
49
- if (baseline === undefined)
50
- throw new Error('--baseline 后面要跟报告路径');
51
- continue;
52
- }
53
- if (lowered === '--rate' || lowered === '-r') {
54
- const raw = tokens[++index];
55
- const rate = raw === undefined ? Number.NaN : Number.parseFloat(raw);
56
- if (!Number.isFinite(rate))
57
- throw new Error('--rate 后面要跟数字,如 --rate 12 或 --rate 0.12');
58
- detectedRate = rate;
59
- continue;
60
- }
61
- if (lowered === '--system' || lowered === '-s') {
62
- detectedSystem = tokens[++index];
63
- if (detectedSystem === undefined)
64
- throw new Error('--system 后面要跟检测系统名,如 --system 知网');
65
- continue;
66
- }
67
- if (token.startsWith('-'))
68
- throw new Error(`无法识别的选项「${token}」`);
69
- paths.push(token);
70
- }
71
- return {
72
- action,
73
- ...(paths.length > 0 ? { corpus: paths.join(',') } : {}),
74
- ...(chapter !== undefined ? { chapter } : {}),
75
- ...(baseline !== undefined ? { baseline } : {}),
76
- ...(detectedRate !== undefined ? { detected_rate: detectedRate } : {}),
77
- ...(detectedSystem !== undefined ? { detected_system: detectedSystem } : {}),
78
- };
79
- }
80
- /** 构造 `/thesis-originality` 命令定义。 */
81
- export function dedupCommand(fs, similarity) {
82
- return {
83
- name: 'thesis-originality',
84
- description: '原创性自查:本地相似度扫描(scan)/ 复测降幅(verify)/ 汇总并回填学校检测结果(report);不接入收费查重系统',
85
- input: { hint: 'scan | verify | report [语料路径] [--chapter 01-绪论] [--rate 12] [--system 知网]' },
86
- async handler(invocation) {
87
- return await safe(async () => {
88
- const args = parseDedupCommandInput(invocation.rawInput);
89
- return await runDedup(fs, invocation.agent.session.header.cwd, args, similarity, invocation.signal);
90
- });
91
- },
92
- };
93
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * 斜杠命令:`/thesis-ingest`。
3
- *
4
- * 命令名遵循 DSH 的 `[a-z][a-z0-9_-]*` 约束(kebab-case),与 `/thesis-check` 等同一套写法。
5
- * 命令只是 `thesis_ingest` 工具的薄封装:把命令行输入解析成 `IngestArgs` 后交给 `runIngest`,
6
- * 因此行为与工具**完全一致**。
7
- *
8
- * @module dsh-thesis/ingest
9
- */
10
- import type { FileSystem } from '@deepseek-ai/dsh-fs';
11
- import type { IngestOptions } from '../config.ts';
12
- import { type IngestArgs } from './index.ts';
13
- /**
14
- * 解析 `/thesis-ingest` 的输入。
15
- *
16
- * 用法:`/thesis-ingest <绝对路径> [--glob docx,pdf] [--dry]`
17
- * - `<绝对路径>`:文件或目录(目录递归;源码目录会产出代码结构摘要)
18
- * - `--glob`:后缀过滤,逗号分隔
19
- * - `--dry`:只读不落盘(不写材料清单与摘要文件)
20
- */
21
- export declare function parseIngestCommandInput(rawInput: string): IngestArgs;
22
- /** 构造 `/thesis-ingest` 命令定义。 */
23
- export declare function ingestCommand(fs: FileSystem, options: IngestOptions): unknown;
@@ -1,74 +0,0 @@
1
- /**
2
- * 斜杠命令:`/thesis-ingest`。
3
- *
4
- * 命令名遵循 DSH 的 `[a-z][a-z0-9_-]*` 约束(kebab-case),与 `/thesis-check` 等同一套写法。
5
- * 命令只是 `thesis_ingest` 工具的薄封装:把命令行输入解析成 `IngestArgs` 后交给 `runIngest`,
6
- * 因此行为与工具**完全一致**。
7
- *
8
- * @module dsh-thesis/ingest
9
- */
10
- import { diskIO, renderOutcome, runIngest } from "./index.js";
11
- async function safe(work) {
12
- try {
13
- return { kind: 'success', text: await work() };
14
- }
15
- catch (error) {
16
- return { kind: 'error', text: error instanceof Error ? error.message : String(error) };
17
- }
18
- }
19
- /**
20
- * 解析 `/thesis-ingest` 的输入。
21
- *
22
- * 用法:`/thesis-ingest <绝对路径> [--glob docx,pdf] [--dry]`
23
- * - `<绝对路径>`:文件或目录(目录递归;源码目录会产出代码结构摘要)
24
- * - `--glob`:后缀过滤,逗号分隔
25
- * - `--dry`:只读不落盘(不写材料清单与摘要文件)
26
- */
27
- export function parseIngestCommandInput(rawInput) {
28
- const tokens = rawInput.trim().split(/\s+/).filter(token => token !== '');
29
- let path;
30
- let glob;
31
- let write = true;
32
- for (let index = 0; index < tokens.length; index += 1) {
33
- const token = tokens[index];
34
- const lowered = token.toLowerCase();
35
- if (lowered === '--glob' || lowered === '-g') {
36
- glob = tokens[++index];
37
- if (glob === undefined)
38
- throw new Error('--glob 后面要跟后缀列表,如 --glob docx,pdf');
39
- continue;
40
- }
41
- if (lowered === '--dry' || lowered === '--no-write') {
42
- write = false;
43
- continue;
44
- }
45
- if (token.startsWith('-'))
46
- throw new Error(`无法识别的选项「${token}」`);
47
- if (path !== undefined)
48
- throw new Error(`只能指定一个路径(多余的是「${token}」)`);
49
- path = token;
50
- }
51
- if (path === undefined) {
52
- throw new Error('用法:/thesis-ingest <绝对路径> [--glob docx,pdf] [--dry]');
53
- }
54
- return {
55
- path,
56
- ...(glob !== undefined ? { glob } : {}),
57
- ...(write ? {} : { write: false }),
58
- };
59
- }
60
- /** 构造 `/thesis-ingest` 命令定义。 */
61
- export function ingestCommand(fs, options) {
62
- return {
63
- name: 'thesis-ingest',
64
- description: '摄取材料:docx/xlsx/pptx/pdf/md/csv/bib + 源码目录 → 文本与代码结构摘要,写 00-管理/材料清单.md',
65
- input: { hint: '<绝对路径> [--glob docx,pdf] [--dry]' },
66
- async handler(invocation) {
67
- return await safe(async () => {
68
- const args = parseIngestCommandInput(invocation.rawInput);
69
- const outcome = await runIngest(fs, diskIO, args, invocation.agent.session.header.cwd, options, invocation.signal);
70
- return renderOutcome(outcome, options.maxChars);
71
- });
72
- },
73
- };
74
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * 斜杠命令:/thesis-status 与 /thesis-decide。
3
- *
4
- * 注:DSH 命令名只允许 [a-z][a-z0-9_-]*,因此使用 kebab-case
5
- * (DESIGN.md 中的 /thesis:status 在此修正为 /thesis-status)。
6
- * 其余命令(/thesis-lit、/thesis-check、/thesis-build)随 M2/M4 工具一并注册。
7
- */
8
- import type { FileSystem } from '@deepseek-ai/dsh-fs';
9
- export declare function statusCommand(fs: FileSystem): unknown;
10
- export declare function decideCommand(fs: FileSystem): unknown;
11
- export declare function litCommand(fs: FileSystem): unknown;
12
- export declare function checkCommand(fs: FileSystem): unknown;
13
- export declare function buildCommand(fs: FileSystem): unknown;
@@ -1,84 +0,0 @@
1
- /**
2
- * 斜杠命令:/thesis-status 与 /thesis-decide。
3
- *
4
- * 注:DSH 命令名只允许 [a-z][a-z0-9_-]*,因此使用 kebab-case
5
- * (DESIGN.md 中的 /thesis:status 在此修正为 /thesis-status)。
6
- * 其余命令(/thesis-lit、/thesis-check、/thesis-build)随 M2/M4 工具一并注册。
7
- */
8
- import { writeFile } from 'node:fs/promises';
9
- import { runDecision } from "./tools/decision.js";
10
- import { runLitSearch } from "./tools/lit.js";
11
- import { runProgress } from "./tools/progress.js";
12
- import { buildSummary, runBuild } from "./tools/build.js";
13
- import { runCheck } from "./tools/check.js";
14
- function cwdOf(invocation) {
15
- return invocation.agent.session.header.cwd;
16
- }
17
- async function safe(work) {
18
- try {
19
- return { kind: 'success', text: await work() };
20
- }
21
- catch (error) {
22
- return { kind: 'error', text: error instanceof Error ? error.message : String(error) };
23
- }
24
- }
25
- export function statusCommand(fs) {
26
- return {
27
- name: 'thesis-status',
28
- description: '查看毕业设计进度台账:当前阶段、人工关卡、任务状态',
29
- async handler(invocation) {
30
- return await safe(async () => runProgress(fs, { action: 'report' }, cwdOf(invocation), invocation.signal));
31
- },
32
- };
33
- }
34
- export function decideCommand(fs) {
35
- return {
36
- name: 'thesis-decide',
37
- description: '记录一条关键决定(内容/理由/备选),写入决定日志',
38
- input: { hint: '决定内容' },
39
- async handler(invocation) {
40
- const content = invocation.rawInput.trim();
41
- if (content === '') {
42
- return { kind: 'error', text: '用法:/thesis-decide <决定内容>。内容不能为空。' };
43
- }
44
- return await safe(async () => runDecision(fs, { content }, cwdOf(invocation), invocation.signal));
45
- },
46
- };
47
- }
48
- export function litCommand(fs) {
49
- return {
50
- name: 'thesis-lit',
51
- description: '从真实学术数据库检索文献(结果写入检索缓存与审计记录)',
52
- input: { hint: '检索关键词' },
53
- async handler(invocation) {
54
- const query = invocation.rawInput.trim();
55
- if (query === '') {
56
- return { kind: 'error', text: '用法:/thesis-lit <关键词>。例如 /thesis-lit 目标检测 小目标' };
57
- }
58
- return await safe(async () => runLitSearch(fs, { query, limit: 5 }, cwdOf(invocation), invocation.signal, fetch));
59
- },
60
- };
61
- }
62
- export function checkCommand(fs) {
63
- return {
64
- name: 'thesis-check',
65
- description: '全文综合检查:引用/字数/图表/术语/模板,报告写入 08-合规/',
66
- async handler(invocation) {
67
- return await safe(async () => runCheck(fs, cwdOf(invocation), invocation.signal));
68
- },
69
- };
70
- }
71
- export function buildCommand(fs) {
72
- return {
73
- name: 'thesis-build',
74
- description: '构建论文.docx(优先 pandoc + 学校模板,否则内置过渡引擎)',
75
- async handler(invocation) {
76
- return await safe(async () => {
77
- const outcome = await runBuild(fs, {}, cwdOf(invocation), invocation.signal, async (absPath, data) => {
78
- await writeFile(absPath, data);
79
- });
80
- return buildSummary(outcome);
81
- });
82
- },
83
- };
84
- }
@@ -1,28 +0,0 @@
1
- /**
2
- * 斜杠命令 `/thesis-defense` 的**幻灯侧**输入解析。
3
- *
4
- * 命令名遵循 DSH 的 `[a-z][a-z0-9_-]*` 约束(kebab-case)。命令注册本身在
5
- * 装配层(`src/index.ts`)完成:`/thesis-defense` 同时覆盖答辩幻灯与代码演练,
6
- * 由装配层聚合两侧的处理器——这样 `ppt/` 与 `codewalk/` 之间没有反向依赖。
7
- *
8
- * @module dsh-thesis/ppt
9
- */
10
- import type { CommandInvocation, CommandResult } from '@deepseek-ai/dsh-commands';
11
- import type { FileSystem } from '@deepseek-ai/dsh-fs';
12
- import type { PptOptions } from '../config.ts';
13
- import { type PptArgs, type PptDeps } from './actions.ts';
14
- /** 幻灯侧的子命令(与代码演练侧区分)。 */
15
- export declare const PPT_SUBCOMMANDS: readonly string[];
16
- /** 把 `/thesis-defense <输入>` 的幻灯侧部分解析成工具参数。 */
17
- export declare function parsePptCommandInput(rawInput: string): PptArgs;
18
- /**
19
- * 幻灯侧的命令处理器(由装配层在 `/thesis-defense` 下调度)。
20
- *
21
- * `rawInput` 仍是命令名之后的全部输入;本函数只关心幻灯侧子命令。
22
- */
23
- export declare function handlePptCommand(fs: FileSystem, options: {
24
- ppt: PptOptions;
25
- }, invocation: CommandInvocation, deps?: PptDeps): Promise<CommandResult>;
26
- /** 命令的展示元信息(装配层注册时使用)。 */
27
- export declare const PPT_COMMAND_DESCRIPTION = "\u7B54\u8FA9\u5E7B\u706F\uFF1A\u751F\u6210 Marp Markdown\uFF08prepare/outline\uFF09/ \u6267\u884C\u5916\u90E8\u8F6C\u6362\uFF08convert\uFF09/ \u8D28\u91CF\u68C0\u67E5\uFF08check\uFF09/ \u8F6C\u6362\u6307\u5F15\uFF08guide\uFF09";
28
- export declare const PPT_COMMAND_HINT = "prepare | convert | check | guide [\u9875\u6570] [undergrad|master] [force]";