@cloud411716/fancy-webnovel 1.0.46 → 2.2.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.
Files changed (134) hide show
  1. package/client.js +183 -0
  2. package/cordis.patch.yml +0 -14
  3. package/index.js +21 -37
  4. package/lib/chinese-num.js +111 -0
  5. package/lib/deployed-state.js +141 -0
  6. package/lib/error-codes.js +104 -0
  7. package/lib/infra.js +569 -0
  8. package/lib/llm-wiki.js +109 -0
  9. package/lib/safe-rm.js +94 -0
  10. package/lib/scraper-utils.js +165 -0
  11. package/package.json +37 -5
  12. package/plugins/fancy-analyze/command-input.js +65 -0
  13. package/plugins/fancy-analyze/index.js +973 -0
  14. package/plugins/fancy-analyze/schema-analyze.md +379 -0
  15. package/plugins/fancy-analyze/schema-write.md +178 -0
  16. package/plugins/fancy-analyze/scripts/split_source.js +292 -0
  17. package/plugins/fancy-analyze/summary-progress.js +431 -0
  18. package/plugins/fancy-analyze/templates.js +881 -0
  19. package/plugins/fancy-bootstrap/index.js +168 -0
  20. package/plugins/fancy-bootstrap/scripts/init.js +143 -0
  21. package/plugins/fancy-bootstrap/scripts/path-utils.js +21 -0
  22. package/plugins/fancy-bootstrap/templates.js +54 -0
  23. package/plugins/fancy-import/import-core.js +1118 -0
  24. package/plugins/fancy-import/import-driver.js +581 -0
  25. package/plugins/fancy-import/index.js +730 -0
  26. package/plugins/fancy-import/prompts/01-import-prompt.md +106 -0
  27. package/plugins/fancy-import/templates.js +49 -0
  28. package/plugins/fancy-scan/index.js +319 -0
  29. package/plugins/fancy-scan/scripts/run-scan.js +520 -0
  30. package/plugins/fancy-scan/scripts/scrapers/fanqie-rank-scraper.js +794 -0
  31. package/plugins/fancy-scan/scripts/scrapers/jjwxc-rank-scraper.js +339 -0
  32. package/plugins/fancy-scan/scripts/scrapers/qimao-rank-scraper.js +611 -0
  33. package/plugins/fancy-scan/templates.js +295 -0
  34. package/plugins/fancy-story-builder/deploy-manifest.js +239 -0
  35. package/plugins/fancy-story-builder/deslop-driver.js +821 -0
  36. package/plugins/fancy-story-builder/flesh-output.js +155 -0
  37. package/plugins/fancy-story-builder/index.js +3152 -0
  38. package/plugins/fancy-story-builder/prompts/01-skeleton-prompt.md +553 -0
  39. package/plugins/fancy-story-builder/prompts/02-flesh-prompt.md +540 -0
  40. package/plugins/fancy-story-builder/prompts/03-rhythm-prompt.md +504 -0
  41. package/plugins/fancy-story-builder/prompts/04-writing-prompt.md +188 -0
  42. package/plugins/fancy-story-builder/prompts/05-deslop-prompt.md +96 -0
  43. package/plugins/fancy-story-builder/purpose-template.md +107 -0
  44. package/plugins/fancy-story-builder/references/formulas-rhythm.json +302 -0
  45. package/plugins/fancy-story-builder/references/formulas-skeleton.json +229 -0
  46. package/plugins/fancy-story-builder/references/glossary.md +585 -0
  47. package/plugins/fancy-story-builder/references/index.md +490 -0
  48. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/00-index.md +174 -0
  49. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/01-scene.md +150 -0
  50. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/02-chapter-workflow.md +99 -0
  51. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/03-opening-chapter.md +104 -0
  52. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/04-dialogue-mastery.md +274 -0
  53. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/05-genre-formulas.md +432 -0
  54. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/06-tone-ensemble.md +259 -0
  55. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/07-ledger-audit.md +222 -0
  56. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/08-diagnosis-revision.md +205 -0
  57. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/09-reading-power.md +135 -0
  58. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/10-deslop.md +129 -0
  59. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/00-index.md +98 -0
  60. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/01-upgrade-rhythm.md +300 -0
  61. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/02-arc-rhythm.md +168 -0
  62. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/03-highpoint-hooks.md +189 -0
  63. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/04-satisfaction.md +226 -0
  64. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/05-foreshadow-density.md +195 -0
  65. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/06-chapter-outline.md +226 -0
  66. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/07-audit.md +163 -0
  67. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/00-index.md +100 -0
  68. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/01-character.md +239 -0
  69. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/02-worldbuilding.md +245 -0
  70. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/03-relations.md +181 -0
  71. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/04-emotion-methods.md +286 -0
  72. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/05-audit.md +177 -0
  73. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/00-index.md +91 -0
  74. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/01-positioning.md +208 -0
  75. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/02-packaging.md +135 -0
  76. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/03-entities.md +305 -0
  77. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/04-conflict.md +328 -0
  78. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/05-quota.md +187 -0
  79. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/06-audit.md +142 -0
  80. package/plugins/fancy-story-builder/rhythm-output.js +140 -0
  81. package/plugins/fancy-story-builder/schema-setting.md +324 -0
  82. package/plugins/fancy-story-builder/scripts/ai-patterns.js +619 -0
  83. package/plugins/fancy-story-builder/scripts/check-guide-compliance.js +289 -0
  84. package/plugins/fancy-story-builder/scripts/content-quality.js +413 -0
  85. package/plugins/fancy-story-builder/scripts/deslop-validate.js +143 -0
  86. package/plugins/fancy-story-builder/scripts/error-classes.js +112 -0
  87. package/plugins/fancy-story-builder/scripts/flesh-validate.js +1189 -0
  88. package/plugins/fancy-story-builder/scripts/formula-engine.js +112 -0
  89. package/plugins/fancy-story-builder/scripts/graph-utils.js +1385 -0
  90. package/plugins/fancy-story-builder/scripts/label-coverage.js +171 -0
  91. package/plugins/fancy-story-builder/scripts/prompt-drift-check.js +441 -0
  92. package/plugins/fancy-story-builder/scripts/reading-power.js +298 -0
  93. package/plugins/fancy-story-builder/scripts/rhythm-validate.js +1216 -0
  94. package/plugins/fancy-story-builder/scripts/semantic-lint.js +377 -0
  95. package/plugins/fancy-story-builder/scripts/skeleton-quota.js +145 -0
  96. package/plugins/fancy-story-builder/scripts/skeleton-validate.js +1832 -0
  97. package/plugins/fancy-story-builder/scripts/stage-batches.js +1371 -0
  98. package/plugins/fancy-story-builder/scripts/stage-contract.js +417 -0
  99. package/plugins/fancy-story-builder/scripts/upstream-query.js +198 -0
  100. package/plugins/fancy-story-builder/scripts/writing-rules.js +167 -0
  101. package/plugins/fancy-story-builder/scripts/writing-validate.js +682 -0
  102. package/plugins/fancy-story-builder/skeleton-output.js +199 -0
  103. package/plugins/fancy-story-builder/stage-driver.js +1049 -0
  104. package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//346/227/266/345/272/217/346/230/240/345/260/204/346/250/241/346/235/277.json +204 -0
  105. package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//347/253/240/350/212/202/347/273/206/347/272/262/346/250/241/346/235/277.json +34 -0
  106. package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//347/253/240/350/212/202/350/247/204/345/210/222/346/250/241/346/235/277.json +24 -0
  107. package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//350/212/202/345/245/217/345/217/202/346/225/260/346/250/241/346/235/277.json +27 -0
  108. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//344/270/226/347/225/214/350/247/202/350/247/204/345/210/231/346/250/241/346/235/277.json +66 -0
  109. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/205/263/351/224/256/351/201/223/345/205/267/346/250/241/346/235/277.json +76 -0
  110. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/206/262/347/252/201/346/250/241/346/235/277.json +72 -0
  111. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/211/247/346/203/205/346/250/241/346/235/277.json +37 -0
  112. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/212/277/345/212/233/346/250/241/346/235/277.json +53 -0
  113. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/234/260/347/202/271/346/250/241/346/235/277.json +40 -0
  114. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//347/273/223/345/261/200/346/250/241/346/235/277.json +79 -0
  115. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//350/247/222/350/211/262/346/250/241/346/235/277.json +132 -0
  116. package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//345/205/263/347/263/273/346/250/241/346/235/277.json +133 -0
  117. package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//345/256/236/344/275/223/346/250/241/346/235/277.json +160 -0
  118. package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//345/261/202/347/272/247/346/250/241/346/235/277.json +65 -0
  119. package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//346/241/206/346/236/266/346/250/241/346/235/277.json +134 -0
  120. package/plugins/fancy-story-builder/templates.js +1248 -0
  121. package/plugins/fancy-story-builder/wiki-references.js +204 -0
  122. package/plugins/fancy-story-builder/writing-driver.js +1061 -0
  123. package/plugins/fancy-topic/index.js +704 -0
  124. package/plugins/fancy-topic/scripts/select-topic.js +274 -0
  125. package/plugins/fancy-topic/templates.js +373 -0
  126. package/commands/bootstrap/index.js +0 -164
  127. package/commands/bootstrap/init-project.js +0 -121
  128. package/commands/scan/index.js +0 -283
  129. package/commands/scan/platforms/fanqie.js +0 -146
  130. package/commands/scan/platforms/jinjiang.js +0 -143
  131. package/commands/scan/platforms/qidian.js +0 -102
  132. package/commands/scan/platforms/qimao.js +0 -127
  133. package/commands/scan/scraper.js +0 -195
  134. package/infra.js +0 -160
@@ -0,0 +1,199 @@
1
+ /**
2
+ * skeleton-output.js —— 骨架阶段的产出盘点
3
+ *
4
+ * 与 flesh-output.js / rhythm-output.js 同构,但对象是骨架阶段的 4 个产物文件。
5
+ *
6
+ * 盘点要回答两个问题(插件据此决定能不能报"完成"):
7
+ * 1. 4 个产物文件是不是都落地了?
8
+ * 2. 全量校验(--all)过没过?
9
+ *
10
+ * 第 2 条对骨架尤其要紧:引用完整性(SKELETON_REF_MISSING)、
11
+ * 图连通性(G_ISOLATED)、配额一致性(QUOTA_MISMATCH)
12
+ * 全都是跨文件口径,单文件校验过不代表阶段过。
13
+ */
14
+ import fs from 'node:fs';
15
+ import path from 'node:path';
16
+
17
+ // 骨架阶段的 4 个产出文件(顺序 = 生成依赖顺序)
18
+ export const SKELETON_OUTPUT_FILES = [
19
+ '框架.json',
20
+ '实体.json',
21
+ '关系.json',
22
+ '层级.json'
23
+ ];
24
+
25
+ /**
26
+ * 盘点骨架阶段的产出。
27
+ *
28
+ * @param {string} stageDir 骨架阶段目录
29
+ * @returns {Object} { present, missing, complete }
30
+ */
31
+ export function inspectSkeletonOutput(stageDir) {
32
+ const present = [];
33
+ const missing = [];
34
+ for (const f of SKELETON_OUTPUT_FILES) {
35
+ const p = path.join(stageDir, f);
36
+ let ok = false;
37
+ try {
38
+ ok = fs.existsSync(p) && fs.statSync(p).size > 0;
39
+ } catch {
40
+ ok = false;
41
+ }
42
+ if (ok) present.push(f);
43
+ else missing.push(f);
44
+ }
45
+ return { present, missing, complete: missing.length === 0 };
46
+ }
47
+
48
+ /**
49
+ * 读骨架阶段的 .stage-status.json,看全量校验的状态。
50
+ *
51
+ * @returns {Object} { allCheck, rounds, files, error }
52
+ */
53
+ export function readSkeletonStageStatus(stageDir) {
54
+ const p = path.join(stageDir, '.stage-status.json');
55
+ if (!fs.existsSync(p)) {
56
+ return { allCheck: null, rounds: 0, files: {}, error: '.stage-status.json 不存在(骨架阶段还没跑过校验)' };
57
+ }
58
+ let s;
59
+ try {
60
+ s = JSON.parse(fs.readFileSync(p, 'utf-8'));
61
+ } catch (e) {
62
+ return { allCheck: null, rounds: 0, files: {}, error: '.stage-status.json 解析失败:' + e.message };
63
+ }
64
+ if (!s || typeof s !== 'object') {
65
+ return { allCheck: null, rounds: 0, files: {}, error: '.stage-status.json 内容不是对象' };
66
+ }
67
+ const all = s.all_check && typeof s.all_check === 'object' ? s.all_check : {};
68
+ return {
69
+ allCheck: all.status || null,
70
+ rounds: all.rounds || 0,
71
+ files: (s.files && typeof s.files === 'object') ? s.files : {},
72
+ error: null
73
+ };
74
+ }
75
+
76
+ /**
77
+ * 生成骨架阶段收尾的结论说明(人类可读、可执行)。
78
+ */
79
+ /**
80
+ * 哪些产物文件已经定稿(单文件校验 PASSED,且校验之后没被外部改动)。
81
+ *
82
+ * 续跑用:重跑骨架阶段时,已定稿的文件不必再派活重做 ——
83
+ * 直接沿用磁盘上的产物(下游的批次规划要读它们:
84
+ * readTargetChapters 读 框架.json 的 capacity,关系批次要读 实体.json 的 id 表)。
85
+ *
86
+ * 为什么要续跑:tt5 实测(2026-10-25)骨架阶段一次要派 40+ 批,
87
+ * 中途任何一次失败(provider 报错 / 连接断开 / 用户 Ctrl-C)都会让
88
+ * 已定稿的文件被整段重做一遍 —— 框架 + 实体 13 个 turn 全部白烧额度。
89
+ *
90
+ * 判没被外部改动**看分片、不看产物**:
91
+ * 分片是真源(产物 = 分片合并出来的),插件会用分片覆盖被模型改坏的产物
92
+ * (makeSettledRestorer)—— 那是内容没变的重新落盘,只看产物的 mtime 会误判成
93
+ * "被改过"从而白白重做一遍(tt5 实测 2026-10-25 端到端验证时,
94
+ * 框架被重复派活就是这个原因)。
95
+ *
96
+ * @param {string} stageDir 骨架阶段目录
97
+ * @param {Object} [opts]
98
+ * @param {string} [opts.buildDir] 分片目录(默认 <stageDir>/.skeleton-build)
99
+ * @returns {Set<string>} 已定稿的文件名(形如 框架.json)
100
+ */
101
+ export function readSkeletonSettledFiles(stageDir, opts = {}) {
102
+ const status = readSkeletonStageStatus(stageDir);
103
+ const buildDir = opts.buildDir || path.join(stageDir, '.skeleton-build');
104
+ const settled = new Set();
105
+ for (const file of SKELETON_OUTPUT_FILES) {
106
+ // .stage-status.json 里的键是**类型名**(校验脚本写的:s.files[fileType],
107
+ // skeleton-validate.js:1445),不是带扩展名的文件名 —— 两种都认。
108
+ const rec = status.files[file.replace(/\.json$/, '')] || status.files[file];
109
+ if (!rec || rec.status !== 'PASSED') continue;
110
+ const checkedAt = Date.parse(rec.last_check || '');
111
+ if (!Number.isFinite(checkedAt)) continue;
112
+ // 产物要在位
113
+ const p = path.join(stageDir, file);
114
+ let st;
115
+ try {
116
+ st = fs.statSync(p);
117
+ } catch {
118
+ continue;
119
+ }
120
+ if (!st.size) continue;
121
+ // 分片是判据:分片在校验之后被改过 → 内容可能变了 → 得重做
122
+ const type = file.replace(/\.json$/, '');
123
+ const prefix = '骨架-' + type + '.b';
124
+ let names = [];
125
+ try {
126
+ names = fs.readdirSync(buildDir);
127
+ } catch {
128
+ names = [];
129
+ }
130
+ const mine = names.filter((n) => n.startsWith(prefix) && n.endsWith('.json'));
131
+ if (!mine.length) continue; // 分片没了 → 没法重建 → 重做
132
+ let touched = false;
133
+ for (const n of mine) {
134
+ try {
135
+ const s = fs.statSync(path.join(buildDir, n));
136
+ if (s.mtimeMs > checkedAt + 2000) {
137
+ touched = true;
138
+ break;
139
+ }
140
+ } catch {
141
+ touched = true;
142
+ break;
143
+ }
144
+ }
145
+ if (touched) continue;
146
+ settled.add(file);
147
+ }
148
+ return settled;
149
+ }
150
+
151
+ export function buildSkeletonCompletionNote(inspect, status) {
152
+ const lines = [];
153
+ const allCheck = status.allCheck;
154
+ const missing = inspect.missing;
155
+
156
+ if (inspect.complete && allCheck === 'PASSED') {
157
+ lines.push('骨架阶段产出检查:4 个 JSON 齐全,全量校验(--all)已通过。');
158
+ lines.push('');
159
+ lines.push('产出文件:');
160
+ for (const f of inspect.present) lines.push('- 设定/raw/sources/骨架/' + f);
161
+ return lines.join('\n');
162
+ }
163
+
164
+ lines.push('骨架阶段产出检查:**未通过**,当前状态如下。');
165
+ lines.push('');
166
+
167
+ if (!inspect.complete) {
168
+ lines.push('- 缺失文件(' + missing.length + '/' + SKELETON_OUTPUT_FILES.length + '):' + missing.join('、'));
169
+ lines.push(' 骨架阶段是逐批派活的,任一段没跑完,下游那一段就不会开工');
170
+ lines.push(' (框架 → 实体 → 关系+层级,段间有依赖门禁)。');
171
+ } else {
172
+ lines.push('- 4 个 JSON 都已产出。');
173
+ }
174
+
175
+ lines.push('');
176
+ if (allCheck === 'PASSED') {
177
+ lines.push('- 全量校验:PASSED(但文件集合与校验记录不一致,需要人工核对)。');
178
+ } else if (allCheck === 'FAILED') {
179
+ lines.push('- 全量校验:FAILED(跨文件一致性没通过:引用完整性 / 图连通性 / 配额一致性都在 --all 里查)。');
180
+ } else if (allCheck === 'STALE') {
181
+ lines.push('- 全量校验:**STALE**(单文件校验把已通过的全量结论作废了,必须重新跑一次 --all)。');
182
+ } else if (allCheck === 'PENDING') {
183
+ lines.push('- 全量校验:**PENDING**(从未跑过 --all,引用完整性、图连通性、配额一致性全都没验过)。');
184
+ } else {
185
+ lines.push('- 全量校验:**未运行**(' + (status.error || '状态不可读') + ')。');
186
+ }
187
+
188
+ lines.push('');
189
+ lines.push('下一步:');
190
+ if (!inspect.complete) {
191
+ lines.push('1. 重新运行骨架阶段,补齐缺失文件:' + missing.join('、'));
192
+ lines.push('2. 4 个文件齐了之后,必须再跑一次全量校验(--all),确认 all_check.status = PASSED。');
193
+ } else {
194
+ lines.push('1. 跑一次全量校验(--all),把 errors 逐条改掉;');
195
+ lines.push('2. 直到 all_check.status = PASSED 再进入血肉阶段。');
196
+ }
197
+
198
+ return lines.join('\n');
199
+ }