@cloud411716/fancy-webnovel 3.0.3 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/material-library/README.md +6 -5
- package/docs/material-library/prompt.md +3 -2
- package/docs/workspace-mcp.md +2 -2
- package/lib/material-topic.js +248 -0
- package/lib/topic-candidates.js +157 -0
- package/package.json +4 -2
- package/plugins/fancy-analyze/analyze-definitions.md +35 -35
- package/plugins/fancy-analyze/templates.js +3 -3
- package/plugins/fancy-import/prompts/01-import-prompt.md +20 -20
- package/plugins/fancy-material/genres.js +4 -36
- package/plugins/fancy-material/index.js +18 -32
- package/plugins/fancy-material/templates.js +11 -3
- package/plugins/fancy-scan/prompts/01-analysis-prompt.md +16 -23
- package/plugins/fancy-scan/scripts/report-validate.js +39 -76
- package/plugins/fancy-scan/templates/report-schema.js +11 -8
- package/plugins/fancy-scan/templates.js +2 -2
- package/plugins/fancy-story-builder/prompts/01-skeleton-prompt.md +40 -28
- package/plugins/fancy-story-builder/prompts/02-flesh-prompt.md +28 -10
- package/plugins/fancy-story-builder/prompts/03-rhythm-prompt.md +21 -9
- package/plugins/fancy-story-builder/prompts/04-writing-prompt.md +15 -15
- package/plugins/fancy-story-builder/prompts/05-deslop-prompt.md +18 -18
- package/plugins/fancy-story-builder/purpose-template.md +1 -1
- package/plugins/fancy-story-builder/references/glossary.md +8 -8
- package/plugins/fancy-story-builder/references/index.md +2 -2
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/00-index.md +2 -2
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/01-scene.md +2 -2
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/02-chapter-workflow.md +2 -2
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/03-opening-chapter.md +2 -2
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/04-dialogue-mastery.md +2 -2
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/05-genre-formulas.md +2 -2
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/06-tone-ensemble.md +2 -2
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/07-ledger-audit.md +2 -2
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/08-diagnosis-revision.md +2 -2
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/09-reading-power.md +7 -7
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/10-deslop.md +6 -6
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/00-index.md +2 -2
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/01-upgrade-rhythm.md +2 -2
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/02-arc-rhythm.md +2 -2
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/03-highpoint-hooks.md +2 -2
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/04-satisfaction.md +2 -2
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/05-foreshadow-density.md +5 -5
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/06-chapter-outline.md +2 -2
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/07-audit.md +1 -1
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/00-index.md +2 -2
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/01-character.md +2 -2
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/02-worldbuilding.md +5 -5
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/03-relations.md +3 -3
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/04-emotion-methods.md +3 -3
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/05-audit.md +1 -1
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/00-index.md +2 -2
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/01-positioning.md +1 -1
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/02-packaging.md +3 -3
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/03-entities.md +2 -2
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/04-conflict.md +2 -2
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/05-quota.md +2 -2
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/06-audit.md +1 -1
- package/plugins/fancy-story-builder/storydb/README.md +4 -4
- package/plugins/fancy-story-builder/storydb/prompts.js +1 -1
- package/plugins/fancy-topic/index.js +216 -546
- package/plugins/fancy-topic/prompts/01-exploration-prompt.md +6 -5
- package/plugins/fancy-topic/prompts/02-setup-prompt.md +112 -0
- package/plugins/fancy-topic/scripts/save-minimal-topic.js +97 -0
- package/plugins/fancy-topic/templates.js +104 -6
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# 网文创作分析术语定义(权威)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
本文件是 `schema.md` 的配套定义文件,和 `schema.md` 一起使用。
|
|
4
|
+
|
|
5
|
+
**执行任何章节分析前,必须先按本文件确认术语含义。**
|
|
6
|
+
不要用通用文学理论、学术论文或其它题材的定义替代这里的定义。
|
|
7
|
+
|
|
8
|
+
定义中的类型和例子是**判定框架,不是题材清单**;都市/玄幻/悬疑/言情/无限流/历史/科幻/体育/职场等题材的等价现象都算,按源文实际内容判断,不要硬套词表。
|
|
9
|
+
|
|
10
|
+
判定顺序:
|
|
11
|
+
1. 先判断“本章有没有这个现象”;
|
|
12
|
+
2. 再判断它属于哪个术语;
|
|
13
|
+
3. 最后才写分析。
|
|
14
|
+
|
|
15
|
+
如果拿不准,宁可写“无”,不要硬套术语。硬套会让后续骨架/血肉/节奏阶段学到错误模式。
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
- 把作者旁白当成视角人物的认知。
|
|
35
35
|
|
|
36
36
|
**网文例子**:
|
|
37
|
-
|
|
37
|
+
方平不知道“武科”是什么,读者也不知道。信息只从方平的疑问和同学的对话里释放——这是限知视角,不是全知旁白。
|
|
38
38
|
|
|
39
39
|
---
|
|
40
40
|
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
- 把整本书的卷结构当成单章结构。
|
|
54
54
|
|
|
55
55
|
**网文例子**:
|
|
56
|
-
|
|
56
|
+
开头方平确认重生 → 同学提到武科 → 方平听不懂 → 张浩带来八品之战消息 → 方平发现“剧本不对”。这是“确认异常—扩大信息—认知崩塌”的单章结构。
|
|
57
57
|
|
|
58
58
|
---
|
|
59
59
|
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
- 把“事件多”当成节奏快——事件多但全是说明,读者仍会觉得拖。
|
|
73
73
|
|
|
74
74
|
**网文例子**:
|
|
75
|
-
|
|
75
|
+
前半段课堂闲聊轻快,张浩一句“马宗师突破八品”后信息密度骤增,结尾用短句收束。这是“轻—重—紧”的节奏,不是单纯事件多。
|
|
76
76
|
|
|
77
77
|
---
|
|
78
78
|
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
- 忽略“沉默”“答非所问”“称呼变化”也是对话。
|
|
94
94
|
|
|
95
95
|
**网文例子**:
|
|
96
|
-
|
|
96
|
+
陈凡说“方平,直呼宗师之名,这是最大的不尊重!”——这句话同时在纠正规则、暴露陈凡对武道礼制的熟悉、建立两人关系里的“懂行/不懂行”落差。
|
|
97
97
|
|
|
98
98
|
---
|
|
99
99
|
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
- 环境描写与剧情完全无关也标“有”。
|
|
115
115
|
|
|
116
116
|
**网文例子**:
|
|
117
|
-
|
|
117
|
+
“方平满脸懵”是心理/表情描写;“手里只剩十五元”是动作性细节,同时推进资源困境。两者都比“方平很穷”有效。
|
|
118
118
|
|
|
119
119
|
---
|
|
120
120
|
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
- 机制只出现一次、没有后果,也标“有”。
|
|
135
135
|
|
|
136
136
|
**网文例子**:
|
|
137
|
-
|
|
137
|
+
重生不是“知道未来”这么简单:方平的旧记忆与当前武道世界不匹配,迫使他重新收集信息。这是“信息差 + 认知错位”机制在推动行动。
|
|
138
138
|
|
|
139
139
|
---
|
|
140
140
|
|
|
@@ -154,7 +154,7 @@
|
|
|
154
154
|
- 把“标题”当成开头钩子。
|
|
155
155
|
|
|
156
156
|
**网文例子**:
|
|
157
|
-
|
|
157
|
+
“2008 年,4 月 5 日。”——日期本身不钩人,但紧接着“方平确认自己重生”制造了“他为什么回到过去、他要怎么活”的问题,这才是钩子。
|
|
158
158
|
|
|
159
159
|
---
|
|
160
160
|
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
- 把“下一章标题”当成结尾收束。
|
|
175
175
|
|
|
176
176
|
**网文例子**:
|
|
177
|
-
|
|
177
|
+
“这剧本,好像有些不对劲啊!”——用人物判断点明异常,不解释原因,把“哪里不对”留给下一章。
|
|
178
178
|
|
|
179
179
|
---
|
|
180
180
|
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
- 全章都是短句也标“有”——那可能是单调,不是变化。
|
|
195
195
|
|
|
196
196
|
**网文例子**:
|
|
197
|
-
|
|
197
|
+
解释性长句写武科制度,紧接着“什么情况?”“啥情况?”两个短句写方平的震惊。长短交替制造认知落差。
|
|
198
198
|
|
|
199
199
|
---
|
|
200
200
|
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
- 只看排版,不看段落之间的逻辑推进。
|
|
216
216
|
|
|
217
217
|
**网文例子**:
|
|
218
|
-
|
|
218
|
+
课堂群像用较长段落铺陈,张浩爆料后切成短段和独立句,最后用一句“剧本不对”单独收束。段落长度跟着信息压力变化。
|
|
219
219
|
|
|
220
220
|
---
|
|
221
221
|
|
|
@@ -236,7 +236,7 @@
|
|
|
236
236
|
- 纯心理联想没有时空/焦点变化,不一定算场景转换。
|
|
237
237
|
|
|
238
238
|
**网文例子**:
|
|
239
|
-
|
|
239
|
+
方平在教室听同学聊武科,随后“去蓝天网吧查资料”——地点和行动目标都变了,这是场景转换。仅从方平的思考切到陈凡的台词,不算。
|
|
240
240
|
|
|
241
241
|
---
|
|
242
242
|
|
|
@@ -256,7 +256,7 @@
|
|
|
256
256
|
- 用旁白总结情绪,没有场景支撑。
|
|
257
257
|
|
|
258
258
|
**网文例子**:
|
|
259
|
-
|
|
259
|
+
全班兴奋崇拜,方平却“满脸懵”“整个人都傻了”。用群体情绪反衬主角的孤立和不安,比直接写“方平很困惑”更有渲染力。
|
|
260
260
|
|
|
261
261
|
---
|
|
262
262
|
|
|
@@ -276,7 +276,7 @@
|
|
|
276
276
|
- 把所有信息都解释完,张力消失。
|
|
277
277
|
|
|
278
278
|
**网文例子**:
|
|
279
|
-
|
|
279
|
+
方平只剩十五元,报名截止只剩两天,陈凡最多借十元——目标、资源、时间三重压力同时存在,读者会想知道他怎么凑钱。
|
|
280
280
|
|
|
281
281
|
---
|
|
282
282
|
|
|
@@ -296,7 +296,7 @@
|
|
|
296
296
|
- 只写“过渡章”而不说明过渡了什么。
|
|
297
297
|
|
|
298
298
|
**网文例子**:
|
|
299
|
-
|
|
299
|
+
第 1 章的功能不是“介绍世界观”,而是“用认知错位制造悬念,并让主角确立考武科目标”。这是可被后续章节继承的功能。
|
|
300
300
|
|
|
301
301
|
---
|
|
302
302
|
|
|
@@ -319,7 +319,7 @@
|
|
|
319
319
|
- 只写“他们是朋友”,不写朋友关系带来的义务和代价。
|
|
320
320
|
|
|
321
321
|
**网文例子**:
|
|
322
|
-
|
|
322
|
+
方平与陈凡不只是“同桌”:陈凡提供武道规则解释和现实提醒,方平提供跨世界视角;两人资源都很紧,但陈凡愿意借出仅有的十元。关系里有信息交换、情谊和资源落差。
|
|
323
323
|
|
|
324
324
|
---
|
|
325
325
|
|
|
@@ -341,7 +341,7 @@
|
|
|
341
341
|
- 势力只在背景里被提到,没有行动和后果,也不算有效设计。
|
|
342
342
|
|
|
343
343
|
**网文例子**:
|
|
344
|
-
|
|
344
|
+
企鹅集团不是“一个大公司”四个字:它有跨省法人资格、武道强者、商业资源、与谷歌亚太区的竞争关系。这些结构决定了它如何压迫/拉拢主角。
|
|
345
345
|
|
|
346
346
|
---
|
|
347
347
|
|
|
@@ -363,7 +363,7 @@
|
|
|
363
363
|
- 规则可以随意打破,没有代价,等于没有规则。
|
|
364
364
|
|
|
365
365
|
**网文例子**:
|
|
366
|
-
|
|
366
|
+
“武科”不是一句“有武道考试”:它有报名费、年龄上限、政审、体检、文化课线、区域录取差异、动态录取标准。这些规则共同制造了主角的资源困境。
|
|
367
367
|
|
|
368
368
|
---
|
|
369
369
|
|
|
@@ -385,7 +385,7 @@
|
|
|
385
385
|
- 冲突解决后没有任何后果,等于没有真正发生。
|
|
386
386
|
|
|
387
387
|
**网文例子**:
|
|
388
|
-
|
|
388
|
+
“方平要考武科”与“家里没钱、报名截止、陈凡只能借十元”构成资源冲突;它不是一场吵架,而是持续多章的目标—资源对立。
|
|
389
389
|
|
|
390
390
|
---
|
|
391
391
|
|
|
@@ -406,7 +406,7 @@
|
|
|
406
406
|
- 主线目标中途换掉,却没有解释。
|
|
407
407
|
|
|
408
408
|
**网文例子**:
|
|
409
|
-
|
|
409
|
+
主线是“方平考武科、改变阶层处境”;支线包括“重生身份疑云”“特殊钞票能力”“与陈凡/家人的关系”。支线不是平行小说,它们不断给主线提供资源/阻力/信息。
|
|
410
410
|
|
|
411
411
|
---
|
|
412
412
|
|
|
@@ -427,7 +427,7 @@
|
|
|
427
427
|
- 卷末高潮与下一卷开篇没有因果连接。
|
|
428
428
|
|
|
429
429
|
**网文例子**:
|
|
430
|
-
|
|
430
|
+
第一卷“阳城备考”以武科报名/体检为小高潮,卷末把舞台推向“南江武大/更大势力”;第二卷再升级资源和对手。这是分卷节奏,不是简单分章。
|
|
431
431
|
|
|
432
432
|
---
|
|
433
433
|
|
|
@@ -455,7 +455,7 @@
|
|
|
455
455
|
- 伏笔回收时没有改变读者理解,只是补一句说明。
|
|
456
456
|
|
|
457
457
|
**网文例子**:
|
|
458
|
-
|
|
458
|
+
“方平直呼马宗师之名,陈凡严肃纠正”——这里既埋了“武道礼制”规则,也埋了“方平为什么不懂”的身份疑云。后续“重生/记忆错位”回收了这条线。
|
|
459
459
|
|
|
460
460
|
---
|
|
461
461
|
|
|
@@ -486,7 +486,7 @@
|
|
|
486
486
|
- 角色每章都变,没有稳定内核,也不可信。
|
|
487
487
|
|
|
488
488
|
**网文例子**:
|
|
489
|
-
|
|
489
|
+
方平从“谨慎旁观、先求证”到“主动报名、承担风险”,中间经过资源困境、家庭责任、知识短板、信息不足的反复检验。这是弧光,不是单纯“变强”。
|
|
490
490
|
|
|
491
491
|
---
|
|
492
492
|
|
|
@@ -551,7 +551,7 @@ ${projectRoot}/对标/${bookName}/
|
|
|
551
551
|
| ... |
|
|
552
552
|
|
|
553
553
|
## 角色动机交织(最关键!)
|
|
554
|
-
|
|
554
|
+
这一段是整个 characters.md 的核心:让 fancy-bible 能直接据此设计角色生态
|
|
555
555
|
|
|
556
556
|
- 主角 ↔ 主反派:[冲突的动机点]
|
|
557
557
|
- 主角 ↔ 配角 A:[...]
|
|
@@ -1093,8 +1093,8 @@ FANCY_SUMMARY_EOF
|
|
|
1093
1093
|
**只处理下面明确列出的总结目录**,不要自行扩大范围。
|
|
1094
1094
|
|
|
1095
1095
|
${scaleNote}
|
|
1096
|
-
|
|
1097
|
-
|
|
1096
|
+
⚠️ 本提示词**故意不列具体来源名** —— 一本长篇可能几千章、上千个实体页,列出来会把提示词撑爆。
|
|
1097
|
+
哪些来源还没总结,由你按每个目录下方写的步骤**自己去目录里判断**。
|
|
1098
1098
|
|
|
1099
1099
|
## ⚠️ 语言强制要求
|
|
1100
1100
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# 01 导入执行须知
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
本文件是导入阶段的**产物契约**:从旧稿反推出什么文件、写成什么格式、什么会被拦下。
|
|
4
|
+
导入的因果方向和正向四阶段相反:
|
|
5
|
+
**旧稿已经在那里了 → 你把它读成结构化产物**,而不是照配额从零造。
|
|
6
|
+
|
|
7
|
+
导入阶段由插件驱动:它扫目录、切章、算配额、跑校验器、把错误发回来。
|
|
8
|
+
**你不需要自己决定写哪些文件,也不需要自己跑校验脚本。**
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -19,20 +19,20 @@
|
|
|
19
19
|
|
|
20
20
|
## 一、核心铁律
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
1. **只反推,不创作**。旧稿里没有的人物、势力、地点、伏笔**不许新增**。
|
|
23
|
+
你的任务是"把旧稿里已经有的东西整理成结构化数据",
|
|
24
|
+
不是"补全一部没写完的小说"。
|
|
25
|
+
旧稿确实没写到的部分**留空**,由插件在报告里列成待补清单。
|
|
26
|
+
|
|
27
|
+
2. **不改旧稿正文**。正文由插件直接落盘(保留原标题),
|
|
28
|
+
你只负责反推结构化产物。写了正文也算违反契约。
|
|
29
|
+
|
|
30
|
+
3. **不修改 `模板/` 和 `references/` 下的任何文件**。这两个目录是只读输入。
|
|
31
|
+
|
|
32
|
+
4. **只写本轮指定的文件**。本轮要写哪几个文件,驱动会在派活时列清楚。
|
|
33
|
+
不要顺手多写,也不要改别的阶段的文件。
|
|
34
|
+
|
|
35
|
+
5. **引用的实体只能用 id 引用**(如 `C001`),不要在关系里写名字。
|
|
36
36
|
|
|
37
37
|
---
|
|
38
38
|
|
|
@@ -15,42 +15,10 @@ import fs from 'fs';
|
|
|
15
15
|
import path from 'path';
|
|
16
16
|
import { workspacePaths } from '#workspace';
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'都市日常',
|
|
23
|
-
'都市修真',
|
|
24
|
-
'都市高武',
|
|
25
|
-
'历史古代',
|
|
26
|
-
'战神赘婿',
|
|
27
|
-
'都市种田',
|
|
28
|
-
'传统玄幻',
|
|
29
|
-
'历史脑洞',
|
|
30
|
-
'悬疑脑洞',
|
|
31
|
-
'都市脑洞',
|
|
32
|
-
'玄幻脑洞',
|
|
33
|
-
'悬疑灵异',
|
|
34
|
-
'抗战谍战',
|
|
35
|
-
'游戏体育',
|
|
36
|
-
'动漫衍生',
|
|
37
|
-
'男频衍生',
|
|
38
|
-
'古风世情',
|
|
39
|
-
'女频衍生',
|
|
40
|
-
'玄幻言情',
|
|
41
|
-
'种田',
|
|
42
|
-
'年代',
|
|
43
|
-
'现言脑洞',
|
|
44
|
-
'宫斗宅斗',
|
|
45
|
-
'古言脑洞',
|
|
46
|
-
'快穿',
|
|
47
|
-
'青春甜宠',
|
|
48
|
-
'星光璀璨',
|
|
49
|
-
'女频悬疑',
|
|
50
|
-
'职场婚恋',
|
|
51
|
-
'豪门总裁',
|
|
52
|
-
'民国言情',
|
|
53
|
-
];
|
|
18
|
+
import { BUILTIN_GENRES } from '#material-topic';
|
|
19
|
+
|
|
20
|
+
/** 兼容旧名字:内置全题材列表统一放在 lib/material-topic.js */
|
|
21
|
+
export const FALLBACK_GENRES = BUILTIN_GENRES;
|
|
54
22
|
|
|
55
23
|
function unique(values) {
|
|
56
24
|
const seen = new Set();
|
|
@@ -22,9 +22,10 @@ import {
|
|
|
22
22
|
workspacePaths,
|
|
23
23
|
} from '#workspace';
|
|
24
24
|
import { connectMaterial, ensureMaterialDatabase, materialDatabaseName, removeMaterialDatabase } from '#material-db';
|
|
25
|
+
import { BUILTIN_GENRES, resolveMaterialTopic } from '#material-topic';
|
|
25
26
|
import { makeWaitForTurn } from '../fancy-story-builder/writing-driver.js';
|
|
26
27
|
import { exportMaterials } from './exporter.js';
|
|
27
|
-
import { allKnownGenres
|
|
28
|
+
import { allKnownGenres } from './genres.js';
|
|
28
29
|
import { buildExistingIndex, countByKind, countKind } from './inventory.js';
|
|
29
30
|
import { KIND_LABELS, MATERIAL_KINDS, focusBlock, renderKindTable, resolveFocus } from './kinds.js';
|
|
30
31
|
import { nextBatchRequest, planMaterialQueue } from './queue.js';
|
|
@@ -388,38 +389,27 @@ async function runGenerate(ctx, invocation, session, focusRaw = '') {
|
|
|
388
389
|
return notify(session, 'error', '❌ 未识别素材类型:' + focusRaw
|
|
389
390
|
+ '\n可用示例:地名、人名、姓氏、门派、技能、超能力、武器、颜色、特效、脑洞、人物性格、关系、场景。');
|
|
390
391
|
}
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
if (topicFiles.length > 1) {
|
|
397
|
-
const options = topicFiles.map((file) => {
|
|
398
|
-
let genres = [];
|
|
399
|
-
try { genres = parseTopicDecisionGenres(fs.readFileSync(file, 'utf8')); } catch { /* preview only */ }
|
|
400
|
-
return {
|
|
401
|
-
label: path.basename(file),
|
|
402
|
-
description: genres.length ? genres.join('、') : '未解析到选题方向',
|
|
403
|
-
};
|
|
404
|
-
});
|
|
392
|
+
// 素材题材只认用户选题链路:
|
|
393
|
+
// _novel_topic.json > _novel_topic_suggestion.json > 内置全题材。
|
|
394
|
+
// 不再从 topic_decision 里猜题材,避免素材和用户最终选题脱节。
|
|
395
|
+
let plan = resolveMaterialTopic(root);
|
|
396
|
+
if (!plan.mode) {
|
|
405
397
|
const asked = await askUserQuestion(ctx, invocation, [{
|
|
406
|
-
id: 'material-
|
|
407
|
-
header: '
|
|
408
|
-
question: '
|
|
409
|
-
detail: '
|
|
410
|
-
options
|
|
398
|
+
id: 'material-builtin-confirm',
|
|
399
|
+
header: '没有选题记录',
|
|
400
|
+
question: '当前没有执行过扫榜和 /fancy-topic,将使用内置全题材生成素材(男频 + 女频)。是否继续?',
|
|
401
|
+
detail: '内置题材共 ' + BUILTIN_GENRES.length + ' 类;确认后所有 kind 都会按全题材生成。',
|
|
402
|
+
options: [
|
|
403
|
+
{ label: '确认', description: '使用内置全题材生成素材' },
|
|
404
|
+
{ label: '取消', description: '不生成任何素材' },
|
|
405
|
+
],
|
|
411
406
|
}]);
|
|
412
407
|
if (!asked.ok) {
|
|
413
408
|
return notify(session, 'error', asked.failure?.message || '❌ 提问失败');
|
|
414
409
|
}
|
|
415
410
|
const selected = asked.answer?.answers?.[0]?.selected?.[0];
|
|
416
|
-
if (
|
|
417
|
-
|
|
418
|
-
if (!selectedTopicFile) {
|
|
419
|
-
return notify(session, 'error', '❌ 未找到选中的选题文件:' + selected);
|
|
420
|
-
}
|
|
421
|
-
} else if (topicFiles.length === 1) {
|
|
422
|
-
selectedTopicFile = topicFiles[0];
|
|
411
|
+
if (selected !== '确认') return notify(session, 'success', '操作已取消。');
|
|
412
|
+
plan = { mode: 'builtin', genres: [...BUILTIN_GENRES], topicFile: null };
|
|
423
413
|
}
|
|
424
414
|
|
|
425
415
|
const config = generationConfig();
|
|
@@ -429,10 +419,6 @@ async function runGenerate(ctx, invocation, session, focusRaw = '') {
|
|
|
429
419
|
return notify(session, 'error', '❌ 部署素材查询脚本失败:\n' + (tools.problems.join('\n') || '未知错误'));
|
|
430
420
|
}
|
|
431
421
|
const database = materialDatabaseName(readWorkspace(root));
|
|
432
|
-
const plan = resolveMaterialGenres(root, { topicFile: selectedTopicFile || undefined });
|
|
433
|
-
if (selectedTopicFile && plan.mode !== 'topic_decision') {
|
|
434
|
-
return notify(session, 'error', '❌ 选中的文件没有解析到「选题方向」:' + path.basename(selectedTopicFile));
|
|
435
|
-
}
|
|
436
422
|
const counts = await countByKind(root, plan.genres);
|
|
437
423
|
const kinds = [...new Set(focus.kinds.length ? focus.kinds : MATERIAL_KINDS)];
|
|
438
424
|
const planned = planMaterialQueue({ kinds, target: config.targetPerKind });
|
|
@@ -791,7 +777,7 @@ export async function apply(ctx) {
|
|
|
791
777
|
try { root = workspaceOf(session); } catch { /* prompt also works outside a workspace */ }
|
|
792
778
|
const genres = allKnownGenres(root);
|
|
793
779
|
const rendered = renderBasePrompt({
|
|
794
|
-
mode: '
|
|
780
|
+
mode: 'builtin',
|
|
795
781
|
genres,
|
|
796
782
|
topicFile: '(prompt 子命令固定输出全题材)',
|
|
797
783
|
existingIndex: '(运行 /fancy-material 时由插件提供)',
|
|
@@ -17,8 +17,9 @@ export function helpText() {
|
|
|
17
17
|
说明:
|
|
18
18
|
- 素材是原子积木,每条只有 id / kind / name / tags / genres / note / source。
|
|
19
19
|
- 每次运行:每个 kind 新增 50 条;每批最多 10 条,插件自动分批直到完成。
|
|
20
|
-
- 未指定类型:54 个 kind
|
|
21
|
-
-
|
|
20
|
+
- 未指定类型:54 个 kind 全部排队。
|
|
21
|
+
- 题材来源:优先 _novel_topic.json,其次 _novel_topic_suggestion.json;都没有时弹框确认后使用内置全题材。
|
|
22
|
+
- 指定类型:只生成指定 kind,题材来源不变。
|
|
22
23
|
- 不设库内上限:运行两次就是每个 kind 累计 100 条;已有素材只用于去重。
|
|
23
24
|
- 中断后重跑会开始新一轮 50 条配额,已经导入的素材不会撤销。
|
|
24
25
|
- 只追加,不覆盖;同 kind 同 name 的素材会被跳过。
|
|
@@ -103,7 +104,14 @@ export function generationPrompt({
|
|
|
103
104
|
lines.push('- 工作区目录:' + workspace);
|
|
104
105
|
lines.push('- 目标文件(**绝对路径**):`' + target + '`');
|
|
105
106
|
lines.push('- 批次 ID:`' + batchId + '`');
|
|
106
|
-
|
|
107
|
+
const modeLabel = mode === 'topic'
|
|
108
|
+
? 'topic(_novel_topic.json)'
|
|
109
|
+
: mode === 'suggestion'
|
|
110
|
+
? 'suggestion(_novel_topic_suggestion.json)'
|
|
111
|
+
: mode === 'builtin'
|
|
112
|
+
? 'builtin(内置全题材)'
|
|
113
|
+
: String(mode || '未指定');
|
|
114
|
+
lines.push('- 题材模式:' + modeLabel);
|
|
107
115
|
if (Array.isArray(genres) && genres.length) lines.push('- 本次题材:' + genres.join('、'));
|
|
108
116
|
if (focus && focus.query) {
|
|
109
117
|
const kinds = (focus.kinds || []).map((item) => `\`${item}\``).join('、');
|
|
@@ -121,7 +121,7 @@ ${preflight_command}
|
|
|
121
121
|
|
|
122
122
|
### 3.2 读取与取样
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
**阶段边界**:3.1 的 `test` / `grep` / `ls` 禁令只约束预检阶段。进入本节后可用只读脚本(grep / awk / python)读取 `data_files` 聚合;但仍不得读取白名单外路径,不得修改或删除任何文件。
|
|
125
125
|
|
|
126
126
|
- 先读每个文件的开头和所有标题/表头,确认字段(书名、作者、排名、题材、${heat_metric_label}、状态、简介等);**不要假设固定格式**
|
|
127
127
|
- 把每个文件头部的 `抓取时间` **原样**写进"一、数据与方法"(每个数据文件都要写)。
|
|
@@ -144,10 +144,10 @@ ${tag_rule_block} - **开篇钩子**:文件里**没有第一章内容**,不
|
|
|
144
144
|
|
|
145
145
|
### 3.3 分析口径
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
**口径红线:需求规模 / 份额 / 集中度只能使用 ${heat_metric_label};其它指标只做交叉验证,不进入公式。**
|
|
148
148
|
|
|
149
149
|
- **本报告是单日快照**:数据只覆盖这一天${snapshot_scope}。
|
|
150
|
-
**结论只能描述当日结构,不能描述趋势** ——
|
|
150
|
+
**结论只能描述当日结构,不能描述趋势** —— 没有历史数据时,不得写“正在上升、持续走高、降温了”;确需给出趋势判断的,必须在"一、数据与方法"里写明所依据的对比日期,本次无对比数据则不写趋势。
|
|
151
151
|
${list_rule_block}- "爆款"不只看排名:结合**${heat_metric_label}规模**、题材内排名、题材集中度
|
|
152
152
|
${genre_rule_block}- **每个题材/结构结论至少 2 本书佐证**,写成"《书名》(题材 ${group_scope}第 X 名,${heat_metric_label} ${heat_sample_value})"
|
|
153
153
|
- **用户画像可以基于题材簇推断**(不必硬凑到两本书),但必须**标注"推断"**,并写明依据的是哪几类书
|
|
@@ -180,31 +180,24 @@ ${cross_list_block}- 用户画像从题材、卖点、情绪价值推断;**区
|
|
|
180
180
|
${section_titles_block}
|
|
181
181
|
|
|
182
182
|
- **第二节标题固定为"二、市场结构(单日快照)"** —— 数据只覆盖一天,讲的是当日结构,不是趋势
|
|
183
|
-
-
|
|
183
|
+
- 每个选题方向只写 4 项:题材、一句话定位、市场评分、推荐理由
|
|
184
184
|
- **方向标题用固定格式**:`### 方向 1:<名称>`、`### 方向 2:<名称>`……(序号从 1 连续编号)
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
每个方向**照抄下面这个格式**(内容替换成实际内容,标签一个字都不许改):
|
|
187
187
|
|
|
188
188
|
```markdown
|
|
189
189
|
### 方向 N:<名称>
|
|
190
|
-
|
|
190
|
+
- **题材**:<一个题材名>
|
|
191
|
+
- **一句话定位**:<题材、主角、核心卖点,一句话说清>
|
|
192
|
+
- **市场评分**:<1-10 分,可写小数>
|
|
193
|
+
- **推荐理由**:<为什么这个方向值得写;写清数据依据、竞争度和差异化>
|
|
191
194
|
```
|
|
192
195
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
+
4 个标签**全部加粗**,冒号用全角 `:`;不要改写、合并或省略。
|
|
197
|
+
**题材**至少 2 个汉字;**一句话定位**和**推荐理由**至少 10 个汉字 —— 写个标签名充数不算数。
|
|
198
|
+
**固定格式是为了让写入后的校验能确定性执行**(见 5.2 的 `append-block.js`)。
|
|
196
199
|
- **方向之间不能是"同一题材换皮"**:${min_directions}-${max_directions} 个方向中,任意两个至少在"题材 / 金手指 / 情绪价值"三项里的**两项**不同
|
|
197
|
-
-
|
|
198
|
-
- **数据支撑度**(有多少${heat_metric_label}数据支持)—— 权重 40%
|
|
199
|
-
- **竞争度**(该题材越挤越靠后)—— 权重 25%
|
|
200
|
-
- **差异化空间**(越空白越靠前)—— 权重 25%
|
|
201
|
-
- **作者匹配度** —— 权重 10%;**数据里没有作者画像**,没有用户提供的偏好时这一项**标"待确认"**,**不要编一个"擅长什么"**,也不要默认给 3 分
|
|
202
|
-
- 每项 1-5 分。**作者匹配度可用时**,总分按
|
|
203
|
-
`(数据支撑度×40 + 竞争度×25 + 差异化空间×25 + 作者匹配度×10)÷ 100` 计算;**作者匹配度标"待确认"时不计入总分**,总分按
|
|
204
|
-
`(数据支撑度×40 + 竞争度×25 + 差异化空间×25)÷ 90` 计算。四舍五入保留两位小数。
|
|
205
|
-
**必须写出各项原始分和算出的总分**,不能只写结论。
|
|
206
|
-
- **优先级计算示例**:数据支撑度 4 分、竞争度 2 分、差异化空间 4 分、作者匹配度待确认 → 总分 = `(4×40 + 2×25 + 4×25) ÷ 90 = 3.44` 分。竞争度是反向指标:竞争越激烈,这项分数越低。
|
|
207
|
-
- 按总分**从高到低**排;同分时依次比数据支撑度、竞争度,仍同分再比该题材主指标总量,并写明依据。
|
|
200
|
+
- **市场评分**按 1-10 分给出,综合数据支撑度、竞争度、差异化空间;在「推荐理由」里写清依据,不要只给分数。
|
|
208
201
|
|
|
209
202
|
**硬性要求**(写入时会被脚本逐条检查,不合格**写不进去**):
|
|
210
203
|
|
|
@@ -231,9 +224,9 @@ ${report_headers_block}
|
|
|
231
224
|
${genre_self_check}
|
|
232
225
|
- **指标用的是${heat_metric_label}数据**,不是恒等于常数的书目数占比
|
|
233
226
|
- **没有写没有数据支撑的趋势判断**
|
|
234
|
-
- ${min_directions}-${max_directions} 个方向,每个
|
|
227
|
+
- ${min_directions}-${max_directions} 个方向,每个 4 项齐全,题材 ≥2 汉字、定位/理由 ≥10 汉字,标题格式正确
|
|
235
228
|
- **任意两个方向至少两项不同**
|
|
236
|
-
-
|
|
229
|
+
- **每个方向都有 1-10 的市场评分和推荐理由**
|
|
237
230
|
- **字数、表格数与表头都符合上面的硬性要求**
|
|
238
231
|
- 题材表没有“全部”汇总行,至少有 3 个题材
|
|
239
232
|
- 结论有"书名 + 排名 + ${heat_metric_label}"佐证,且每个题材/结构结论至少 2 本书佐证
|
|
@@ -245,7 +238,7 @@ ${genre_self_check}
|
|
|
245
238
|
|
|
246
239
|
## 五、追加写入(校验在脚本里,跑不掉)
|
|
247
240
|
|
|
248
|
-
|
|
241
|
+
**红线:目标文件只能通过 5.2 的 `append-block.js` 写入。禁止 `cat >>`、write / edit、`touch` 或任何绕过脚本的写法。**
|
|
249
242
|
|
|
250
243
|
把本次分析报告**追加**到 `${topic_file}` 的**末尾**。
|
|
251
244
|
|