@waterwx/dsh-novel-forge 1.3.1 → 1.3.2-alpha

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 (161) hide show
  1. package/README.md +288 -279
  2. package/lib/client.js +56309 -12221
  3. package/lib/index.js +14239 -5595
  4. package/lib/types/action-guard.d.ts +61 -0
  5. package/lib/types/ai-scan.d.ts +36 -0
  6. package/lib/types/assets.d.ts +24 -9
  7. package/lib/types/assistant.d.ts +29 -0
  8. package/lib/types/author-assets.d.ts +25 -0
  9. package/lib/types/bookshelf.d.ts +11 -1
  10. package/lib/types/client/api.d.ts +186 -10
  11. package/lib/types/client/chip-prefs.d.ts +36 -0
  12. package/lib/types/client/docx.d.ts +2 -0
  13. package/lib/types/client/index.d.ts +1 -1
  14. package/lib/types/client/llmLive.d.ts +31 -0
  15. package/lib/types/client/locales.d.ts +28 -67
  16. package/lib/types/client/panel/AdaptModeView.d.ts +5 -0
  17. package/lib/types/client/panel/AiTag.d.ts +1 -0
  18. package/lib/types/client/panel/AssistantTab.d.ts +4 -1
  19. package/lib/types/client/panel/AuthorAssetsView.d.ts +4 -0
  20. package/lib/types/client/panel/AuthorHome.d.ts +34 -0
  21. package/lib/types/client/panel/BookAnalysisView.d.ts +4 -0
  22. package/lib/types/client/panel/CmdPalette.d.ts +12 -0
  23. package/lib/types/client/panel/CreateBookView.d.ts +5 -1
  24. package/lib/types/client/panel/DirectorView.d.ts +7 -0
  25. package/lib/types/client/panel/GlobalAssetLibraryView.d.ts +4 -0
  26. package/lib/types/client/panel/IdeaInspirationView.d.ts +6 -0
  27. package/lib/types/client/panel/ImportModal.d.ts +7 -0
  28. package/lib/types/client/panel/KnowledgeBaseView.d.ts +4 -0
  29. package/lib/types/client/panel/LiveFeedLog.d.ts +13 -0
  30. package/lib/types/client/panel/MarketRadarView.d.ts +5 -0
  31. package/lib/types/client/panel/ModelManager.d.ts +14 -0
  32. package/lib/types/client/panel/NovelPanel.d.ts +6 -1
  33. package/lib/types/client/panel/PageHeader.d.ts +18 -0
  34. package/lib/types/client/panel/ProgressConsole.d.ts +17 -0
  35. package/lib/types/client/panel/ProgressHomeView.d.ts +5 -0
  36. package/lib/types/client/panel/PromptSlotsView.d.ts +11 -0
  37. package/lib/types/client/panel/ReasoningSection.d.ts +10 -0
  38. package/lib/types/client/panel/SettingsView.d.ts +19 -0
  39. package/lib/types/client/panel/ShelfView.d.ts +12 -1
  40. package/lib/types/client/panel/SubPage.d.ts +27 -0
  41. package/lib/types/client/panel/TensionView.d.ts +18 -0
  42. package/lib/types/client/panel/TimelineView.d.ts +18 -0
  43. package/lib/types/client/panel/helpers.d.ts +5 -3
  44. package/lib/types/client/panel/toast.d.ts +2 -0
  45. package/lib/types/client/panel/views.d.ts +25 -7
  46. package/lib/types/client/run-status.d.ts +21 -0
  47. package/lib/types/client/text.d.ts +9 -0
  48. package/lib/types/docx-core.d.ts +16 -0
  49. package/lib/types/docx.d.ts +2 -3
  50. package/lib/types/engine.d.ts +186 -45
  51. package/lib/types/global-assets.d.ts +6 -0
  52. package/lib/types/index.d.ts +22 -8
  53. package/lib/types/llm-live.d.ts +75 -0
  54. package/lib/types/llm-retry.d.ts +38 -0
  55. package/lib/types/market-radar-scan.d.ts +19 -0
  56. package/lib/types/market-radar-sources.d.ts +31 -0
  57. package/lib/types/migrate-output.d.ts +25 -0
  58. package/lib/types/novel-context.d.ts +80 -0
  59. package/lib/types/prompt-slots.d.ts +57 -0
  60. package/lib/types/prompt-utils.d.ts +35 -0
  61. package/lib/types/prompting.d.ts +10 -0
  62. package/lib/types/protocol.d.ts +1362 -166
  63. package/lib/types/review-policy.d.ts +97 -0
  64. package/lib/types/revision.d.ts +76 -0
  65. package/lib/types/routes.d.ts +4 -0
  66. package/lib/types/run.d.ts +23 -1
  67. package/lib/types/snapshots.d.ts +46 -0
  68. package/lib/types/stage-contract.d.ts +60 -0
  69. package/lib/types/tension.d.ts +53 -0
  70. package/lib/types/timeline.d.ts +37 -0
  71. package/package.json +52 -28
  72. package/src/action-guard.ts +127 -0
  73. package/src/ai-scan.ts +179 -0
  74. package/src/assets.ts +1806 -520
  75. package/src/assistant.ts +1098 -770
  76. package/src/author-assets.ts +149 -0
  77. package/src/bookshelf.ts +187 -151
  78. package/src/client/api.ts +378 -21
  79. package/src/client/chip-prefs.ts +121 -0
  80. package/src/client/docx.ts +37 -69
  81. package/src/client/index.ts +3 -1
  82. package/src/client/llmLive.ts +111 -0
  83. package/src/client/locales.ts +28 -253
  84. package/src/client/mount.tsx +1 -1
  85. package/src/client/panel/AdaptModeView.tsx +535 -0
  86. package/src/client/panel/AiTag.tsx +7 -0
  87. package/src/client/panel/AssetsTab.tsx +448 -458
  88. package/src/client/panel/AssistantTab.tsx +355 -301
  89. package/src/client/panel/AuthorAssetsView.tsx +206 -0
  90. package/src/client/panel/AuthorHome.tsx +309 -0
  91. package/src/client/panel/BookAnalysisView.tsx +63 -0
  92. package/src/client/panel/CmdPalette.tsx +102 -0
  93. package/src/client/panel/CreateBookView.tsx +366 -348
  94. package/src/client/panel/DirectorView.tsx +200 -0
  95. package/src/client/panel/GlobalAssetLibraryView.tsx +168 -0
  96. package/src/client/panel/IdeaInspirationView.tsx +76 -0
  97. package/src/client/panel/ImportModal.tsx +303 -0
  98. package/src/client/panel/KnowledgeBaseView.tsx +125 -0
  99. package/src/client/panel/LiveFeedLog.tsx +204 -0
  100. package/src/client/panel/MarketRadarView.tsx +377 -0
  101. package/src/client/panel/ModelManager.tsx +205 -0
  102. package/src/client/panel/NovelPanel.tsx +3135 -2651
  103. package/src/client/panel/PageHeader.tsx +37 -0
  104. package/src/client/panel/ProgressConsole.tsx +63 -0
  105. package/src/client/panel/PromptSlotsView.tsx +97 -0
  106. package/src/client/panel/ReaderView.tsx +306 -306
  107. package/src/client/panel/ReasoningSection.tsx +31 -0
  108. package/src/client/panel/RunPanel.tsx +64 -32
  109. package/src/client/panel/SettingsView.tsx +354 -0
  110. package/src/client/panel/ShelfView.tsx +266 -236
  111. package/src/client/panel/SubPage.tsx +42 -0
  112. package/src/client/panel/TensionView.tsx +212 -0
  113. package/src/client/panel/TimelineView.tsx +208 -0
  114. package/src/client/panel/WorldTab.tsx +202 -202
  115. package/src/client/panel/helpers.ts +7 -5
  116. package/src/client/panel/panel.module.css +4481 -1655
  117. package/src/client/panel/toast.ts +51 -0
  118. package/src/client/panel/views.tsx +401 -314
  119. package/src/client/run-status.ts +171 -0
  120. package/src/client/sidebar-entry.ts +1 -1
  121. package/src/client/text.ts +28 -0
  122. package/src/docx-core.ts +53 -0
  123. package/src/docx.ts +44 -83
  124. package/src/engine.ts +4672 -2943
  125. package/src/global-assets.ts +59 -0
  126. package/src/index.ts +89 -33
  127. package/src/llm-live.ts +260 -0
  128. package/src/llm-retry.ts +77 -0
  129. package/src/market-radar-scan.ts +41 -0
  130. package/src/market-radar-sources.ts +173 -0
  131. package/src/migrate-output.ts +76 -0
  132. package/src/novel-context.ts +286 -0
  133. package/src/prompt-slots.ts +125 -0
  134. package/src/prompting.ts +26 -0
  135. package/src/protocol.ts +1388 -158
  136. package/src/review-policy.ts +161 -0
  137. package/src/revision.ts +283 -0
  138. package/src/routes.ts +1934 -434
  139. package/src/run.ts +118 -21
  140. package/src/snapshots.ts +174 -0
  141. package/src/stage-contract.ts +189 -0
  142. package/src/tension.ts +251 -0
  143. package/src/timeline.ts +159 -0
  144. package/lib/client.js.map +0 -1
  145. package/lib/index.js.map +0 -1
  146. package/lib/types/comic-presets.d.ts +0 -12
  147. package/scripts/assistant-frames.txt +0 -8
  148. package/scripts/diagnose-adapter.mts +0 -81
  149. package/scripts/diagnose-assistant.mjs +0 -58
  150. package/scripts/diagnose-bible.mjs +0 -72
  151. package/scripts/diagnose-harness.mts +0 -75
  152. package/scripts/novel-forge-restart.log +0 -10
  153. package/scripts/novel-forge-web.stderr.log +0 -2
  154. package/scripts/novel-forge-web.stdout.log +0 -1
  155. package/scripts/outline-sample.txt +0 -449
  156. package/scripts/probe.mjs +0 -6
  157. package/scripts/restart-web.ps1 +0 -88
  158. package/scripts/smoke.mts +0 -72
  159. package/scripts/upload-github.ps1 +0 -51
  160. package/src/client/panel/BookshelfBar.tsx +0 -170
  161. package/src/comic-presets.ts +0 -67
@@ -0,0 +1,97 @@
1
+ /**
2
+ * 审稿规则注册表
3
+ *
4
+ * 生成用 compact 模式(核心规则),审稿用 full 模式(全部规则),
5
+ * 消除 reviewSystemPrompt 内嵌规则与 renderAllAssets() 两套打架的问题。
6
+ */
7
+ /** 通用写作资产规则(兼容 ProjectAssets 中的各类规则) */
8
+ export interface WritingRule {
9
+ name: string;
10
+ description: string;
11
+ type?: string;
12
+ enabled?: boolean;
13
+ }
14
+ export interface ReviewRule {
15
+ id: string;
16
+ /** 规则维度 */
17
+ dimension: 'character' | 'setting' | 'redline' | 'writing' | 'pacing' | 'logic' | 'anti-ai' | 'presentation' | 'compliance';
18
+ /** 严重度 */
19
+ severity: 'high' | 'medium' | 'low';
20
+ /** 规则类型:forbidden=禁止类(命中即问题),encourage=鼓励类(不命中不算错) */
21
+ type: 'forbidden' | 'encourage';
22
+ /** 规则描述 */
23
+ description: string;
24
+ /** 是否启用 */
25
+ enabled: boolean;
26
+ /** 作用域 */
27
+ scope: Array<'writing' | 'review' | 'polish' | 'all'>;
28
+ }
29
+ /** 内置审稿维度(结构化评分用) */
30
+ export declare const REVIEW_DIMENSIONS: readonly [{
31
+ readonly id: "character";
32
+ readonly name: "人设一致性";
33
+ readonly weight: 0.2;
34
+ }, {
35
+ readonly id: "setting";
36
+ readonly name: "设定一致性";
37
+ readonly weight: 0.15;
38
+ }, {
39
+ readonly id: "redline";
40
+ readonly name: "红线检查";
41
+ readonly weight: 0.15;
42
+ }, {
43
+ readonly id: "writing";
44
+ readonly name: "文笔质量";
45
+ readonly weight: 0.15;
46
+ }, {
47
+ readonly id: "pacing";
48
+ readonly name: "节奏与爽点";
49
+ readonly weight: 0.1;
50
+ }, {
51
+ readonly id: "logic";
52
+ readonly name: "逻辑漏洞";
53
+ readonly weight: 0.1;
54
+ }, {
55
+ readonly id: "anti-ai";
56
+ readonly name: "反 AI 规则";
57
+ readonly weight: 0.05;
58
+ }, {
59
+ readonly id: "presentation";
60
+ readonly name: "呈现方式";
61
+ readonly weight: 0.05;
62
+ }, {
63
+ readonly id: "compliance";
64
+ readonly name: "内容合规";
65
+ readonly weight: 0.05;
66
+ }];
67
+ export type ReviewDimensionId = typeof REVIEW_DIMENSIONS[number]['id'];
68
+ /** 维度结构化评分 */
69
+ export interface DimensionScore {
70
+ dimension: ReviewDimensionId;
71
+ score: number;
72
+ note: string;
73
+ }
74
+ /**
75
+ * 渲染审稿规则(compact 或 full 模式)
76
+ *
77
+ * compact: 只渲染禁止类 + high 严重度(用于生成时的约束)
78
+ * full: 渲染全部启用规则(用于审稿时的检查清单)
79
+ */
80
+ export declare function renderReviewRules(assets: WritingRule[] | undefined, mode?: 'compact' | 'full'): string;
81
+ /**
82
+ * 从审稿 issues 中按维度统计
83
+ * 用于结构化评分和 UI 展示。
84
+ */
85
+ export declare function groupIssuesByDimension(issues: Array<{
86
+ severity: string;
87
+ item: string;
88
+ dimension?: string;
89
+ }>): Record<string, Array<{
90
+ severity: string;
91
+ item: string;
92
+ }>>;
93
+ /**
94
+ * 计算维度加权总分
95
+ * 用于审稿报告的结构化评分。
96
+ */
97
+ export declare function calculateWeightedScore(dimensions: DimensionScore[]): number;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * 修订指令合并层(arbitration)。
3
+ *
4
+ * 问题:审稿、时间线、张力三条通道各自给建议,如果各自改一轮,会出现「改完 A 又违反 B」
5
+ * 的互相打脸:同一章可能被改三遍,最后一轮的改动把第一轮的修正覆盖掉。
6
+ *
7
+ * 规则(与面板/生产单共享同一份实现,避免两头各写一套):
8
+ * 1. 同一章只交付**一份**修订指令(本模块负责汇总,调用方不得自行拼装)。
9
+ * 2. 固定优先级,不做权重打分(可解释、可复现):
10
+ * 0 审稿 high(必改,是硬门)
11
+ * 1 时间线 high(硬矛盾:时间倒流、地点瞬移)
12
+ * 2 张力 medium(曲线偏差,建议性最强)
13
+ * 3 其余(审稿 medium / 时间线 medium)
14
+ * 4 低优先(审稿 low)
15
+ * 3. 一次修订最多吃 REVISION_ITEM_CAP 条,超出按优先级截断;截断数量显式回传,不静默丢弃。
16
+ * 4. 每章修订轮次上限 MAX_REVISION_ROUNDS,超出转人工待办。
17
+ * 5. 验证基准 = 本轮实际下发的全部条目(审稿 + 时间线 + 张力),
18
+ * 这样「复核」核对的就是「我们要求改的东西」,而不是只核对审稿那部分。
19
+ */
20
+ import type { ProjectState, ReviewIssue, ReviewReport, RevisionItem, RevisionSource, Severity } from './protocol.js';
21
+ /** 每章修订轮次上限:两轮仍不过即转人工,不做「无限自动修改」。 */
22
+ export declare const MAX_REVISION_ROUNDS = 2;
23
+ /** 单次修订指令最多携带的条目数(防止指令过长把正文挤出上下文)。 */
24
+ export declare const REVISION_ITEM_CAP = 8;
25
+ export declare const REVISION_SOURCE_LABELS: Record<RevisionSource, string>;
26
+ /** 固定优先级:数值越小越先改。 */
27
+ export declare function revisionPriority(source: RevisionSource, severity: Severity): number;
28
+ /** 排序 + 去重 + 截断。去重时保留优先级更高的那条,并累加 count。 */
29
+ export declare function mergeRevisionItems(items: RevisionItem[], cap?: number): {
30
+ items: RevisionItem[];
31
+ omitted: number;
32
+ };
33
+ export interface RevisionCollectOptions {
34
+ /** 目标章;缺省表示全书(用于整本批量修订)。 */
35
+ chapterNo?: number;
36
+ /** 显式指定的审稿意见;缺省取该章已落盘的审稿报告。 */
37
+ reviewIssues?: ReviewIssue[];
38
+ /** 是否纳入时间线矛盾(默认纳入)。 */
39
+ includeTimeline?: boolean;
40
+ /** 是否纳入张力偏差(默认纳入)。 */
41
+ includeTension?: boolean;
42
+ }
43
+ /** 汇总一章(或全书)的三类建议。纯函数,不改项目状态。 */
44
+ export declare function collectRevisionItems(project: ProjectState, options?: RevisionCollectOptions): RevisionItem[];
45
+ export interface RevisionPlan {
46
+ /** 已排序、去重、截断后的条目。 */
47
+ items: RevisionItem[];
48
+ /** 下发给模型的修订指令(唯一入口,调用方不要自行拼装)。 */
49
+ instruction: string;
50
+ counts: Record<RevisionSource, number>;
51
+ /** 因超出上限被截断的条数(显式回传,便于提示作者)。 */
52
+ omitted: number;
53
+ /** 验证基准:把本轮下发的全部条目当作「上一轮意见」,复核时逐条核对。 */
54
+ baseline: ReviewReport;
55
+ }
56
+ export interface BuildRevisionPlanOptions extends RevisionCollectOptions {
57
+ /** 审稿基线报告(提供后 baseline 会保留其分数与时间)。 */
58
+ baseReport?: ReviewReport;
59
+ /** 指令抬头(默认「按合并建议修订」)。 */
60
+ title?: string;
61
+ cap?: number;
62
+ }
63
+ /** 渲染修订指令。格式与历史「按审稿意见修订」保持同族,模型无需重新学习。 */
64
+ export declare function renderRevisionInstruction(items: RevisionItem[], title?: string): string;
65
+ /**
66
+ * 构造验证基准:把时间线/张力条目转成「上一轮意见」的编号项,
67
+ * 与审稿意见合并成一份报告。复核模型据此逐条核对,避免
68
+ * 「审稿说已解决、时间线说没改」这类互相打架的判定。
69
+ */
70
+ export declare function buildRevisionBaseline(items: RevisionItem[], base?: ReviewReport): ReviewReport;
71
+ /** 汇总 → 排序 → 指令 → 基准。面板与生产单都走这里。 */
72
+ export declare function buildRevisionPlan(project: ProjectState, options?: BuildRevisionPlanOptions): RevisionPlan;
73
+ /** 一行摘要,用于日志与面板提示("审稿 3 · 时间线 1 · 张力 1,另截断 2 条")。 */
74
+ export declare function describeRevisionPlan(plan: RevisionPlan): string;
75
+ /** 待办文本(生产单只提示不阻塞时写入项目待办,供作者人工决定)。 */
76
+ export declare function revisionTodoText(item: RevisionItem): string;
@@ -18,6 +18,10 @@ export interface NovelRoutesDeps {
18
18
  getConfig: () => NovelConfig;
19
19
  /** Persist a config patch through the settings seam. */
20
20
  patchConfig: (patch: ConfigPatch) => Promise<NovelConfig>;
21
+ /** Raw settings config(未与激活书合并)——目录迁移判断默认目录是否需同步用。 */
22
+ rawConfig?: () => {
23
+ outputDir?: string;
24
+ };
21
25
  }
22
26
  /**
23
27
  * Build every /api/dsh-novel-forge route.
@@ -1,5 +1,17 @@
1
1
  import type { Context } from '@deepseek-ai/cordis';
2
2
  import type { NovelConfig, RunState } from './protocol.ts';
3
+ /**
4
+ * 解析某个输出目录的书名(用于旧 run-state.json 的兜底)。
5
+ * 优先项目文件里的 bookName,其次目录名——两者都没有时返回空串。
6
+ * @param outputDir - 该书输出目录。
7
+ */
8
+ export declare function resolveRunBookName(outputDir: string): string;
9
+ /**
10
+ * 一句话描述"正在跑的是哪本书的哪一章"。
11
+ * 面板状态行与 409 报错共用,避免两处各写各的(也便于单测覆盖)。
12
+ * @param state - 运行中的生产单状态。
13
+ */
14
+ export declare function describeRunningBatch(state: RunState): string;
3
15
  export declare class ProductionRunner {
4
16
  private deps;
5
17
  private state;
@@ -17,13 +29,23 @@ export declare class ProductionRunner {
17
29
  private persist;
18
30
  private log;
19
31
  /** 启动/续跑生产单:startNo..endNo 区间,endNo 超出计划时先自动补计划。 */
20
- start(startNo: number, endNo: number): Promise<RunState>;
32
+ start(startNo: number, endNo: number, runDir?: string): Promise<RunState>;
21
33
  pause(): void;
22
34
  resume(): void;
23
35
  stop(): void;
36
+ /** 是否有生产单正在执行(目录迁移等磁盘级操作前须为 false)。 */
37
+ isWorking(): boolean;
24
38
  /** 主循环:从 currentNo 扫描到 endNo,逐章处理;支持暂停/停止。 */
25
39
  private loop;
26
40
  private processChapter;
41
+ /**
42
+ * 出章即核对曲线(建议性,不阻塞):
43
+ * 规则初筛得到的时间线矛盾 + 张力曲线偏差写进运行日志;本章已完结时再落成项目待办。
44
+ * 默认**不**触发任何自动修订——要看/要改由作者在面板发起「一键按建议修订」;
45
+ * 只有当本章因审稿 high 本来就要修订时,这些问题才搭同一轮车(见 handleRejected),
46
+ * 因此不额外增加轮次,也不会出现「改完甲又违反乙」。
47
+ */
48
+ private auditChapterAdvisories;
27
49
  /** 完整质量门:生成 → 摘要+事实 → 伏笔标记 → 审稿 → 作者复盘。 */
28
50
  private produce;
29
51
  /** 被拒分级处理:无 high 豁免;有 high 按意见修订(最多 2 轮)+ 验证模式;仍不过 → 待人工。 */
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 章节历史版本(快照):覆盖写入前自动留存正文,支持列表 / 回滚 / 清理。
3
+ *
4
+ * 为什么需要它:生成、按意见重写、去 AI 味润色、采纳草稿都会**覆盖** `第N章_*.md`,
5
+ * 一旦改差(尤其是自动修订把好稿改坏)就没有退路。这里在覆盖前把旧稿存到
6
+ * `<书目录>/snapshots/`,并维护一份 `snapshots/index.json` 索引。
7
+ *
8
+ * 纯 Node 文件操作,不依赖模型;路由层只做参数校验与响应。
9
+ */
10
+ import type { ChapterPlan, ChapterSnapshot } from './protocol.ts';
11
+ /** 快照目录名(书目录下)。 */
12
+ export declare const SNAPSHOT_DIR = "snapshots";
13
+ /** 每章默认保留的快照数(超出后按时间淘汰最旧的)。 */
14
+ export declare const DEFAULT_SNAPSHOT_KEEP = 20;
15
+ /** 快照目录绝对路径。 */
16
+ export declare function snapshotDir(outputDir: string): string;
17
+ /** 读取索引(缺失/损坏时返回空数组)。 */
18
+ export declare function loadSnapshots(outputDir: string): ChapterSnapshot[];
19
+ /** 快照占用字节数(索引 + 正文)。 */
20
+ export declare function snapshotsBytes(outputDir: string): number;
21
+ /**
22
+ * 为一个章节留存快照(覆盖写入前调用)。
23
+ *
24
+ * @param outputDir 书目录。
25
+ * @param chapter 章节(需要已存在正文文件)。
26
+ * @param reason 留存原因(生成覆盖 / 采纳草稿 / 修订 / 润色 / 手工)。
27
+ * @param keep 每章保留数量(超出淘汰最旧)。
28
+ * @returns 新建的快照;无正文文件时返回 undefined(不产生空快照)。
29
+ */
30
+ export declare function createSnapshot(outputDir: string, chapter: ChapterPlan, reason: string, keep?: number): ChapterSnapshot | undefined;
31
+ /**
32
+ * 回滚到某个快照:把快照内容写回章节正文文件。
33
+ *
34
+ * 回滚前会**先给当前正文留一份快照**(reason=回滚前自动留存),所以回滚本身也可撤销。
35
+ *
36
+ * @param outputDir 书目录。
37
+ * @param chapter 目标章节。
38
+ * @param snapshotId 快照 id。
39
+ * @returns 回滚后的字符数。
40
+ * @throws 快照不存在或章节无正文文件时抛错。
41
+ */
42
+ export declare function restoreSnapshot(outputDir: string, chapter: ChapterPlan, snapshotId: string): number;
43
+ /** 删除一个快照(连同文件)。 */
44
+ export declare function removeSnapshot(outputDir: string, snapshotId: string): boolean;
45
+ /** 按每章保留数量清理(连文件一起删)。 */
46
+ export declare function pruneSnapshots(outputDir: string, keep?: number): number;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * 阶段契约(Stage Contract)——宿主计算的「当前阶段 → 本轮允许的动作」。
3
+ *
4
+ * 设计来源(「作者请求中的当前阶段决定唯一允许命令」这一约定):
5
+ * 允许的动作必须由宿主根据真实项目状态算出,而不是指望模型记住或自律。
6
+ * 宿主计算结果同时用于三处:
7
+ * 1) 注入助手系统提示词:模型知道自己在哪一步、下一步该做什么;
8
+ * 2) 通用续写指令(「继续 / 下一步」)下的写操作白名单强约束(阶段即唯一命令);
9
+ * 3) 拒绝写操作时给出可执行的、阶段相关的理由,而不是一句「不允许」。
10
+ *
11
+ * 注意:作者显式点名的写操作永远优先于阶段白名单(作者终审权);
12
+ * 阶段强约束只在作者没有指定具体对象(通用续写)时生效。
13
+ */
14
+ import type { ChapterPlan, ProjectState, StageInfo } from './protocol.ts';
15
+ /** 创作阶段标识(线上形状见 protocol.ts 的 StageInfo)。 */
16
+ export type BookStageId = StageInfo['id'];
17
+ /** 一个阶段契约。 */
18
+ export type BookStage = StageInfo;
19
+ /**
20
+ * 作者这一轮是否只说了「继续/下一步」这类没有具体对象的指令。
21
+ *
22
+ * @param message 本轮用户消息。
23
+ * @returns 是通用续写指令时为 true。
24
+ */
25
+ export declare function isGenericContinue(message: string): boolean;
26
+ /**
27
+ * 根据项目真实状态计算当前创作阶段。
28
+ *
29
+ * @param project 当前项目(未加载时为 undefined)。
30
+ * @returns 阶段契约(推荐动作 + 写操作白名单 + 判定理由)。
31
+ */
32
+ export declare function computeBookStage(project: ProjectState | undefined): BookStage;
33
+ /**
34
+ * 判断某写操作是否在当前阶段的允许白名单内。
35
+ *
36
+ * @param stage 阶段契约。
37
+ * @param tool 助手工具名。
38
+ * @returns 允许时为 true。
39
+ */
40
+ export declare function stageAllows(stage: BookStage, tool: string): boolean;
41
+ /**
42
+ * 按阶段把一组动作分成「本阶段可用」与「阶段外」。
43
+ *
44
+ * @param stage 阶段契约。
45
+ * @param tools 动作名列表(通常是写操作登记表)。
46
+ * @returns 两个保持输入顺序的子集。
47
+ */
48
+ export declare function splitByStage(stage: BookStage, tools: readonly string[]): {
49
+ available: string[];
50
+ outside: string[];
51
+ };
52
+ /**
53
+ * 渲染阶段契约为可注入提示词的一段文字。
54
+ *
55
+ * @param stage 阶段契约。
56
+ * @returns 多行文本。
57
+ */
58
+ export declare function renderStageContract(stage: BookStage): string;
59
+ /** 汇总章节状态计数(供面板/提示词展示,避免各处重复统计)。 */
60
+ export declare function summarizeChapters(chapters: ChapterPlan[]): Record<string, number>;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 张力曲线:把「每章该有多紧」显式化,并在出章后核对实际走向。
3
+ *
4
+ * 为什么要有它:长篇最常见的两种崩法——**一直高能没有呼吸**(读者疲劳)与
5
+ * **长时间低位打转**(读者流失);这两种都不是单章问题,而是"曲线"问题,
6
+ * 单章审稿看不见。这里提供:
7
+ * 1) 参考曲线(本项目自行设计,仅作形状参照);
8
+ * 2) 把「本章目标张力」注入写作提示词;
9
+ * 3) 规则核对:实际 vs 目标偏差、连续同值、高潮后不回落、长期低位。
10
+ *
11
+ * 本模块只放纯逻辑(可离线单测);模型交互与落盘在 engine/routes。
12
+ */
13
+ import type { ChapterPlan, ProjectState, TensionCurvePreset, TensionIssue } from './protocol.ts';
14
+ /** 单章张力的取值范围。 */
15
+ export declare const TENSION_MIN = 0;
16
+ export declare const TENSION_MAX = 100;
17
+ /** 参考曲线的中文标签(面板展示)。 */
18
+ export declare const TENSION_PRESET_LABELS: Record<TensionCurvePreset, string>;
19
+ /** 全部可选预设(UI 下拉用)。 */
20
+ export declare const TENSION_PRESETS: readonly TensionCurvePreset[];
21
+ /** 把参考形状按章节数重采样(最近邻,保持形状不引入平滑失真)。 */
22
+ export declare function referenceCurve(preset: TensionCurvePreset, chapterCount: number): number[];
23
+ /** 读一章的目标张力(未设置时返回 undefined)。 */
24
+ export declare function targetTension(chapter: ChapterPlan): number | undefined;
25
+ /** 读一章的实际张力(审稿时模型打分,未设置时返回 undefined)。 */
26
+ export declare function actualTension(chapter: ChapterPlan): number | undefined;
27
+ /** 收敛到合法区间并取整。 */
28
+ export declare function clampTension(value: number): number;
29
+ /**
30
+ * 渲染注入写作提示词的「本章张力」块。
31
+ *
32
+ * @param project 项目状态。
33
+ * @param chapterNo 本章章号。
34
+ * @returns 提示词片段;没有张力信息时返回空串。
35
+ */
36
+ export declare function renderTensionBlock(project: ProjectState, chapterNo: number): string;
37
+ export declare function detectTensionIssues(project: ProjectState): TensionIssue[];
38
+ /**
39
+ * 组装面板需要的曲线数据(章节号 / 目标 / 实际 / 参考)。
40
+ *
41
+ * @param project 项目状态。
42
+ * @returns 逐章数据点与预设名。
43
+ */
44
+ export declare function buildCurveData(project: ProjectState): {
45
+ preset: TensionCurvePreset;
46
+ points: Array<{
47
+ no: number;
48
+ title: string;
49
+ target?: number;
50
+ actual?: number;
51
+ reference?: number;
52
+ }>;
53
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 故事时间线:把「章节正文」压成可排序的时间事件,用于后续章节的时间连续性。
3
+ *
4
+ * 与编年录的分工:编年录记「事实」(人物状态/资源/关系),时间线记「顺序」
5
+ * (什么时候、在哪里、谁在场、发生了什么),专门用来抓时间倒流、地点瞬移与
6
+ * 跨越章节的时序矛盾。
7
+ *
8
+ * 本模块只放纯逻辑(可离线单测):排序、注入渲染、规则初筛、事件归一化;
9
+ * 模型交互(抽取/复核)在 engine.ts,落盘与路由在 routes.ts。
10
+ */
11
+ import type { ProjectState, TimelineEvent, TimelineIssue } from './protocol.ts';
12
+ /** 注入后续章节的时间线锚点条数(超过则显式声明省略,遵循截断显式化规则)。 */
13
+ export declare const TIMELINE_CONTEXT_LIMIT = 12;
14
+ /** 归一化事件(清洗字符串、补 id/时间戳)。 */
15
+ export declare function normalizeTimelineEvent(raw: Partial<TimelineEvent>, chapterNo: number, index: number, now?: string): TimelineEvent;
16
+ /** 时间线排序:先章节、再 order(缺省排在该章末尾)、最后 createdAt。 */
17
+ export declare function sortTimeline(events: readonly TimelineEvent[]): TimelineEvent[];
18
+ /**
19
+ * 渲染注入到「写作/规划」提示词的时间线锚点块。
20
+ *
21
+ * @param project 项目状态。
22
+ * @param beforeChapter 只注入该章之前的锚点(含该章本身不注入,避免剧透)。
23
+ * @returns 提示词片段(无事件时返回空串)。带显式截断声明。
24
+ */
25
+ export declare function renderTimelineBlock(project: ProjectState, beforeChapter: number): string;
26
+ /**
27
+ * 规则初筛时间线矛盾(不调模型,瞬时计算)。
28
+ *
29
+ * 覆盖三类最容易犯又最容易用规则抓到的错:
30
+ * 1) 同一章内事件顺序号倒退;
31
+ * 2) 全书顺序号在章节推进后倒退(时间倒流);
32
+ * 3) 相邻两章之间地点瞬移(同一角色在未交代移动的情况下跨地点)。
33
+ *
34
+ * @param project 项目状态。
35
+ * @returns 问题清单(可能为空)。
36
+ */
37
+ export declare function detectTimelineIssues(project: ProjectState): TimelineIssue[];
package/package.json CHANGED
@@ -1,8 +1,32 @@
1
1
  {
2
2
  "name": "@waterwx/dsh-novel-forge",
3
- "description": "AI 编译小说工作台 for the dsh web GUI: import a novel outline (docx/text), plan chapters with LLM, generate chapter by chapter (3000-4000 chars each), and save every chapter as Markdown into your chosen output folder.",
4
- "version": "1.3.1",
3
+ "description": "AI 编译纯文本小说工作台 for the dsh web GUI: import a novel outline (docx/text), plan volumes & chapters with LLM, generate chapter by chapter (3000-4000 chars each), auto AI review & book audit, and export the full book as Markdown/TXT into your chosen output folder.",
4
+ "version": "1.3.2-alpha",
5
5
  "type": "module",
6
+ "author": "waterwx",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/watersxya/dsh-novel-forge.git"
10
+ },
11
+ "homepage": "https://github.com/watersxya/dsh-novel-forge",
12
+ "bugs": {
13
+ "url": "https://github.com/watersxya/dsh-novel-forge/issues"
14
+ },
15
+ "keywords": [
16
+ "dsh",
17
+ "dsh-plugin",
18
+ "deepseek-harness",
19
+ "小说工坊",
20
+ "novel-forge",
21
+ "novel-writing-workbench",
22
+ "novel-writing",
23
+ "写作助手",
24
+ "小说创作",
25
+ "网文写作",
26
+ "章节生成",
27
+ "AI审稿",
28
+ "story-writing"
29
+ ],
6
30
  "engines": {
7
31
  "node": "^22.19.0 || >=24.0.0"
8
32
  },
@@ -26,7 +50,6 @@
26
50
  },
27
51
  "client": {
28
52
  "inject": [
29
- "@deepseek-ai/dsh-client-runtime",
30
53
  "@deepseek-ai/dsh-client-connection",
31
54
  "@deepseek-ai/dsh-client-ui-settings"
32
55
  ],
@@ -35,36 +58,35 @@
35
58
  },
36
59
  "dependencies": {
37
60
  "fflate": "^0.8.2",
61
+ "lucide-react": "^1.33.0",
38
62
  "schemastery": "^3.18.0"
39
63
  },
40
64
  "peerDependencies": {
41
- "@deepseek-ai/dsh-client-connection": "^0.1.0-rc.5",
42
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.5",
43
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.5",
44
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.5",
45
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.5",
46
- "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.5",
47
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.5",
48
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.5",
49
- "@deepseek-ai/dsh-skill": "^0.1.0-rc.5",
50
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.5",
51
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.5",
65
+ "@deepseek-ai/dsh-client-connection": "0.1.2-alpha.3",
66
+ "@deepseek-ai/dsh-client-locale": "0.1.2-alpha.3",
67
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.2-alpha.3",
68
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-alpha.3",
69
+ "@deepseek-ai/dsh-host-webserver": "0.1.2-alpha.3",
70
+ "@deepseek-ai/dsh-llm": "0.1.2-alpha.3",
71
+ "@deepseek-ai/dsh-settings": "0.1.2-alpha.3",
72
+ "@deepseek-ai/dsh-skill": "0.1.2-alpha.3",
73
+ "@deepseek-ai/dsh-system-prompt": "0.1.2-alpha.3",
74
+ "@deepseek-ai/dsh-tools": "0.1.2-alpha.3",
52
75
  "react": "^18.2.0",
53
76
  "react-dom": "^18.2.0"
54
77
  },
55
78
  "devDependencies": {
56
79
  "@deepseek-ai/cordis": "^4.0.1",
57
- "@deepseek-ai/dsh-client-connection": "^0.1.0-rc.5",
58
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.5",
59
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.5",
60
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.5",
61
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.5",
62
- "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.5",
63
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.5",
64
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.5",
65
- "@deepseek-ai/dsh-skill": "^0.1.0-rc.5",
66
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.5",
67
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.5",
80
+ "@deepseek-ai/dsh-client-connection": "0.1.2-alpha.3",
81
+ "@deepseek-ai/dsh-client-locale": "0.1.2-alpha.3",
82
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.2-alpha.3",
83
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-alpha.3",
84
+ "@deepseek-ai/dsh-host-webserver": "0.1.2-alpha.3",
85
+ "@deepseek-ai/dsh-llm": "0.1.2-alpha.3",
86
+ "@deepseek-ai/dsh-settings": "0.1.2-alpha.3",
87
+ "@deepseek-ai/dsh-skill": "0.1.2-alpha.3",
88
+ "@deepseek-ai/dsh-system-prompt": "0.1.2-alpha.3",
89
+ "@deepseek-ai/dsh-tools": "0.1.2-alpha.3",
68
90
  "@types/node": "^22.20.0",
69
91
  "@types/react": "~18.3.1",
70
92
  "@types/react-dom": "^18.3.5",
@@ -77,11 +99,9 @@
77
99
  },
78
100
  "files": [
79
101
  "lib/**/*.js",
80
- "lib/**/*.js.map",
81
102
  "lib/**/*.d.ts",
82
- "lib/**/*.d.ts.map",
83
103
  "src",
84
- "scripts",
104
+ "scripts/patch-schemastery.mjs",
85
105
  "cordis.patch.yml",
86
106
  "README.md"
87
107
  ],
@@ -92,6 +112,10 @@
92
112
  "bundle": "tsdown",
93
113
  "watch": "tsdown --watch",
94
114
  "typecheck": "tsc --noEmit",
115
+ "check:styles": "node scripts/check-theme-sizes.mjs",
116
+ "check:third-party": "node scripts/check-third-party.mjs",
117
+ "release": "node scripts/release.mjs",
118
+ "release:dry": "node scripts/release.mjs --dry-run",
95
119
  "test": "vitest run"
96
120
  }
97
121
  }