@cloud411716/fancy-webnovel 2.2.0 → 3.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.
Files changed (211) hide show
  1. package/client.js +201 -15
  2. package/cordis.patch.yml +5 -0
  3. package/docs/douyin-plugin.md +118 -0
  4. package/docs/material-library/README.md +404 -0
  5. package/docs/material-library/import.js +437 -0
  6. package/docs/material-library/prompt.md +370 -0
  7. package/docs/material-library/queries.surql +94 -0
  8. package/docs/material-library/sample.jsonl +14 -0
  9. package/docs/material-library/schema.surql +46 -0
  10. package/docs/mcp-profile.example.yml +24 -0
  11. package/docs/story-graph-design.md +602 -0
  12. package/docs/story-graph-validation.md +81 -0
  13. package/docs/workspace-mcp.md +284 -0
  14. package/index.js +11 -7
  15. package/lib/atomic-write.js +109 -0
  16. package/lib/chapter-order.js +100 -0
  17. package/lib/confirmation.js +80 -0
  18. package/lib/db-init.js +123 -0
  19. package/lib/deployed-state.js +11 -11
  20. package/lib/direction-labels.js +265 -0
  21. package/lib/error-codes.js +31 -31
  22. package/lib/infra.js +29 -4
  23. package/lib/material-db.js +149 -0
  24. package/lib/path-guard.js +150 -0
  25. package/lib/projects.js +400 -0
  26. package/lib/scraper-utils.js +140 -0
  27. package/lib/surreal-server.js +338 -0
  28. package/lib/topic-decision.js +860 -0
  29. package/lib/topic-fields.js +251 -0
  30. package/lib/topic-schema.js +225 -0
  31. package/lib/workspace.js +501 -0
  32. package/package.json +29 -4
  33. package/plugins/fancy-analyze/analyze-definitions.md +519 -0
  34. package/plugins/fancy-analyze/index.js +1128 -77
  35. package/plugins/fancy-analyze/ingest-coverage.js +107 -0
  36. package/plugins/fancy-analyze/schema-analyze.md +355 -107
  37. package/plugins/fancy-analyze/scripts/append-summary-chapter.js +612 -0
  38. package/plugins/fancy-analyze/summary-progress.js +71 -7
  39. package/plugins/fancy-analyze/templates.js +437 -139
  40. package/plugins/fancy-bootstrap/index.js +276 -131
  41. package/plugins/fancy-bootstrap/scripts/init.js +59 -25
  42. package/plugins/fancy-bootstrap/templates.js +35 -18
  43. package/plugins/fancy-db/index.js +172 -0
  44. package/plugins/fancy-douyin/deploy.js +134 -0
  45. package/plugins/fancy-douyin/index.js +551 -0
  46. package/plugins/fancy-douyin/prompts/01-analysis-prompt.md +84 -0
  47. package/plugins/fancy-douyin/scripts/crawl-note.cjs +194 -0
  48. package/plugins/fancy-douyin/scripts/lib.cjs +197 -0
  49. package/plugins/fancy-douyin/scripts/scan-account.cjs +128 -0
  50. package/plugins/fancy-douyin/scripts/start-chrome.cjs +203 -0
  51. package/plugins/fancy-douyin/state.js +197 -0
  52. package/plugins/fancy-douyin/templates.js +127 -0
  53. package/plugins/fancy-export/exporter.js +805 -0
  54. package/plugins/fancy-export/index.js +63 -0
  55. package/plugins/fancy-import/import-driver.js +29 -6
  56. package/plugins/fancy-import/index.js +13 -5
  57. package/plugins/fancy-import/prompts/01-import-prompt.md +39 -14
  58. package/plugins/fancy-import/templates.js +17 -8
  59. package/plugins/fancy-material/exporter.js +153 -0
  60. package/plugins/fancy-material/genres.js +256 -0
  61. package/plugins/fancy-material/index.js +835 -0
  62. package/plugins/fancy-material/inventory.js +143 -0
  63. package/plugins/fancy-material/kinds.js +313 -0
  64. package/plugins/fancy-material/queue.js +55 -0
  65. package/plugins/fancy-material/scripts/query.js +254 -0
  66. package/plugins/fancy-material/templates.js +258 -0
  67. package/plugins/fancy-material/tool-deploy.js +138 -0
  68. package/plugins/fancy-material/validate.js +149 -0
  69. package/plugins/fancy-project/index.js +133 -0
  70. package/plugins/fancy-scan/existing-block.js +100 -0
  71. package/plugins/fancy-scan/index.js +410 -21
  72. package/plugins/fancy-scan/prompts/01-analysis-prompt.md +302 -0
  73. package/plugins/fancy-scan/scripts/append-block.js +725 -0
  74. package/plugins/fancy-scan/scripts/report-validate.js +824 -0
  75. package/plugins/fancy-scan/scripts/run-scan.js +169 -22
  76. package/plugins/fancy-scan/scripts/scrapers/fanqie-rank-scraper.js +145 -16
  77. package/plugins/fancy-scan/scripts/scrapers/jjwxc-rank-scraper.js +179 -49
  78. package/plugins/fancy-scan/scripts/scrapers/qimao-rank-scraper.js +38 -20
  79. package/plugins/fancy-scan/templates/blocks.js +155 -0
  80. package/plugins/fancy-scan/templates/platforms.js +372 -0
  81. package/plugins/fancy-scan/templates/report-schema.js +90 -0
  82. package/plugins/fancy-scan/templates.js +545 -146
  83. package/plugins/fancy-scan/tool-deploy.js +335 -0
  84. package/plugins/fancy-scan/wait-turn.js +46 -0
  85. package/plugins/fancy-story-builder/db/README.md +106 -0
  86. package/plugins/fancy-story-builder/db/client.js +228 -0
  87. package/plugins/fancy-story-builder/db/commands.js +145 -0
  88. package/plugins/fancy-story-builder/db/ids.js +39 -0
  89. package/plugins/fancy-story-builder/db/import.js +798 -0
  90. package/plugins/fancy-story-builder/db/project.js +299 -0
  91. package/plugins/fancy-story-builder/db/query.js +361 -0
  92. package/plugins/fancy-story-builder/db/schema.js +417 -0
  93. package/plugins/fancy-story-builder/db/stage-sync.js +238 -0
  94. package/plugins/fancy-story-builder/flesh-output.js +19 -6
  95. package/plugins/fancy-story-builder/index.js +4105 -396
  96. package/plugins/fancy-story-builder/patch-driver.js +390 -0
  97. package/plugins/fancy-story-builder/prompts/01-skeleton-prompt.md +29 -19
  98. package/plugins/fancy-story-builder/prompts/02-flesh-prompt.md +72 -10
  99. package/plugins/fancy-story-builder/prompts/03-rhythm-prompt.md +40 -5
  100. package/plugins/fancy-story-builder/prompts/04-writing-prompt.md +37 -0
  101. package/plugins/fancy-story-builder/purpose-template.md +42 -66
  102. package/plugins/fancy-story-builder/references/formulas-skeleton.json +229 -56
  103. package/plugins/fancy-story-builder/references/genre-profiles.json +3514 -0
  104. package/plugins/fancy-story-builder/references/glossary.md +15 -6
  105. package/plugins/fancy-story-builder/references/index.md +97 -6
  106. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/00-index.md +1 -1
  107. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/07-audit.md +25 -7
  108. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/01-character.md +5 -1
  109. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/03-relations.md +6 -2
  110. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/05-audit.md +15 -5
  111. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/00-index.md +1 -1
  112. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/01-positioning.md +2 -1
  113. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/03-entities.md +1 -1
  114. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/06-audit.md +19 -1
  115. package/plugins/fancy-story-builder/schema-setting.json +1100 -0
  116. package/plugins/fancy-story-builder/schema-setting.md +201 -293
  117. package/plugins/fancy-story-builder/scripts/brief-validate.js +164 -0
  118. package/plugins/fancy-story-builder/scripts/content-quality.js +4 -1
  119. package/plugins/fancy-story-builder/scripts/flesh-validate.js +135 -12
  120. package/plugins/fancy-story-builder/scripts/graph-utils.js +146 -47
  121. package/plugins/fancy-story-builder/scripts/reference-log.js +110 -0
  122. package/plugins/fancy-story-builder/scripts/rhythm-validate.js +100 -4
  123. package/plugins/fancy-story-builder/scripts/schema-pages.js +2668 -0
  124. package/plugins/fancy-story-builder/scripts/semantic-lint.js +5 -1
  125. package/plugins/fancy-story-builder/scripts/skeleton-validate.js +158 -17
  126. package/plugins/fancy-story-builder/scripts/stage-batches.js +36 -2
  127. package/plugins/fancy-story-builder/scripts/stage-contract.js +12 -0
  128. package/plugins/fancy-story-builder/scripts/story-artifacts.js +933 -0
  129. package/plugins/fancy-story-builder/scripts/upstream-query.js +1 -1
  130. package/plugins/fancy-story-builder/scripts/v2/baseline.js +112 -0
  131. package/plugins/fancy-story-builder/scripts/v2/cast.js +517 -0
  132. package/plugins/fancy-story-builder/scripts/v2/context.js +434 -0
  133. package/plugins/fancy-story-builder/scripts/v2/contracts.js +493 -0
  134. package/plugins/fancy-story-builder/scripts/v2/core.js +419 -0
  135. package/plugins/fancy-story-builder/scripts/v2/engine.js +1068 -0
  136. package/plugins/fancy-story-builder/scripts/v2/export-markdown.js +794 -0
  137. package/plugins/fancy-story-builder/scripts/v2/genre.js +195 -0
  138. package/plugins/fancy-story-builder/scripts/v2/knowledge.js +321 -0
  139. package/plugins/fancy-story-builder/scripts/v2/macro.js +409 -0
  140. package/plugins/fancy-story-builder/scripts/v2/manifest.js +538 -0
  141. package/plugins/fancy-story-builder/scripts/v2/migrate.js +137 -0
  142. package/plugins/fancy-story-builder/scripts/v2/prompts.js +1335 -0
  143. package/plugins/fancy-story-builder/scripts/v2/rhythm.js +618 -0
  144. package/plugins/fancy-story-builder/scripts/v2/scale.js +843 -0
  145. package/plugins/fancy-story-builder/scripts/v2/snapshot.js +121 -0
  146. package/plugins/fancy-story-builder/scripts/v2/stages.js +273 -0
  147. package/plugins/fancy-story-builder/scripts/v2/writing.js +216 -0
  148. package/plugins/fancy-story-builder/storydb/README.md +271 -0
  149. package/plugins/fancy-story-builder/storydb/cli.js +458 -0
  150. package/plugins/fancy-story-builder/storydb/client.js +351 -0
  151. package/plugins/fancy-story-builder/storydb/compile.js +452 -0
  152. package/plugins/fancy-story-builder/storydb/context.js +288 -0
  153. package/plugins/fancy-story-builder/storydb/definitions.js +699 -0
  154. package/plugins/fancy-story-builder/storydb/driver.js +106 -0
  155. package/plugins/fancy-story-builder/storydb/dsh-tools.js +313 -0
  156. package/plugins/fancy-story-builder/storydb/export.js +175 -0
  157. package/plugins/fancy-story-builder/storydb/ids.js +31 -0
  158. package/plugins/fancy-story-builder/storydb/impact.js +170 -0
  159. package/plugins/fancy-story-builder/storydb/import.js +559 -0
  160. package/plugins/fancy-story-builder/storydb/mcp.js +99 -0
  161. package/plugins/fancy-story-builder/storydb/prompts.js +952 -0
  162. package/plugins/fancy-story-builder/storydb/stage-command.js +379 -0
  163. package/plugins/fancy-story-builder/storydb/stage.js +426 -0
  164. package/plugins/fancy-story-builder/storydb/store.js +891 -0
  165. package/plugins/fancy-story-builder/storydb/tools.js +592 -0
  166. package/plugins/fancy-story-builder/storydb/validation.js +183 -0
  167. package/plugins/fancy-story-builder/templates/v2/10-scale/scale-template.json +144 -0
  168. package/plugins/fancy-story-builder/templates/v2/20-macro/emotion-template.json +15 -0
  169. package/plugins/fancy-story-builder/templates/v2/20-macro/endings-template.json +16 -0
  170. package/plugins/fancy-story-builder/templates/v2/20-macro/lines-template.json +28 -0
  171. package/plugins/fancy-story-builder/templates/v2/20-macro/roles-template.json +10 -0
  172. package/plugins/fancy-story-builder/templates/v2/20-macro/story-template.json +19 -0
  173. package/plugins/fancy-story-builder/templates/v2/20-macro/volumes-template.json +24 -0
  174. package/plugins/fancy-story-builder/templates/v2/20-macro/world-template.json +24 -0
  175. package/plugins/fancy-story-builder/templates/v2/30-cast/characters-template.json +49 -0
  176. package/plugins/fancy-story-builder/templates/v2/30-cast/climaxes-template.json +14 -0
  177. package/plugins/fancy-story-builder/templates/v2/30-cast/conflicts-template.json +30 -0
  178. package/plugins/fancy-story-builder/templates/v2/30-cast/factions-template.json +18 -0
  179. package/plugins/fancy-story-builder/templates/v2/30-cast/foreshadows-template.json +29 -0
  180. package/plugins/fancy-story-builder/templates/v2/30-cast/information-template.json +29 -0
  181. package/plugins/fancy-story-builder/templates/v2/30-cast/items-template.json +13 -0
  182. package/plugins/fancy-story-builder/templates/v2/30-cast/locations-template.json +12 -0
  183. package/plugins/fancy-story-builder/templates/v2/30-cast/payoffs-template.json +16 -0
  184. package/plugins/fancy-story-builder/templates/v2/30-cast/relations-template.json +44 -0
  185. package/plugins/fancy-story-builder/templates/v2/40-rhythm/chapter-map-template.json +81 -0
  186. package/plugins/fancy-story-builder/templates/v2/40-rhythm/outline-template.json +162 -0
  187. package/plugins/fancy-story-builder/templates/v2/50-writing/ledger-template.json +46 -0
  188. package/plugins/fancy-story-builder/templates/v2/60-deslop/report-template.json +10 -0
  189. 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 +267 -44
  190. 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 +121 -19
  191. 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 +129 -21
  192. package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/205/266/344/273/226/345/256/236/344/275/223/346/250/241/346/235/277.json +62 -0
  193. 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 +76 -17
  194. 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 +56 -8
  195. 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 +63 -13
  196. 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 +34 -6
  197. 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 +81 -18
  198. 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 +202 -42
  199. 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 +160 -27
  200. 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 +217 -41
  201. 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 +215 -41
  202. package/plugins/fancy-story-builder/templates.js +272 -74
  203. package/plugins/fancy-story-builder/wiki-references.js +429 -93
  204. package/plugins/fancy-story-builder/writing-driver.js +336 -9
  205. package/plugins/fancy-topic/index.js +414 -61
  206. package/plugins/fancy-topic/prompts/01-exploration-prompt.md +115 -0
  207. package/plugins/fancy-topic/scripts/save-suggestion.js +219 -0
  208. package/plugins/fancy-topic/scripts/select-topic.js +106 -63
  209. package/plugins/fancy-topic/templates.js +225 -122
  210. package/plugins/fancy-topic/tool-deploy.js +200 -0
  211. package/lib/safe-rm.js +0 -94
@@ -0,0 +1,699 @@
1
+ /**
2
+ * storydb schema definitions — the single source of truth.
3
+ *
4
+ * From this file the compiler generates:
5
+ * - SurrealDB DDL
6
+ * - tool JSON schemas / schema cards
7
+ * - requirement rules
8
+ * - export mappings
9
+ *
10
+ * Field types:
11
+ * string | text | int | float | bool | datetime
12
+ * array | object | record | enum
13
+ */
14
+
15
+ const S = (extra = {}) => ({ type: 'string', ...extra });
16
+ const T = (extra = {}) => ({ type: 'text', ...extra });
17
+ const I = (extra = {}) => ({ type: 'int', ...extra });
18
+ const F = (extra = {}) => ({ type: 'float', ...extra });
19
+ const B = (extra = {}) => ({ type: 'bool', ...extra });
20
+ const D = (extra = {}) => ({ type: 'datetime', ...extra });
21
+ const A = (extra = {}) => ({ type: 'array', ...extra });
22
+ const O = (extra = {}) => ({ type: 'object', ...extra });
23
+ const R = (table, extra = {}) => ({ type: 'record', table, ...extra });
24
+ const E = (values, extra = {}) => ({ type: 'enum', values, ...extra });
25
+
26
+ export const STATUS = ['seed', 'draft', 'complete', 'locked', 'archived'];
27
+
28
+ export const ENTITIES = {
29
+ story: {
30
+ table: 'story',
31
+ id: 'main',
32
+ label: '故事',
33
+ fields: {
34
+ title: S({ required: true }),
35
+ genre: S(),
36
+ platform: S(),
37
+ target_chapters: I({ required: true }),
38
+ target_words: I({ required: true }),
39
+ avg_words_per_chapter: I(),
40
+ status: E(STATUS, { default: 'seed' }),
41
+ },
42
+ requirements: [],
43
+ },
44
+
45
+ scale: {
46
+ table: 'scale',
47
+ label: '规模',
48
+ fields: {
49
+ capacity: O(),
50
+ structure: O(),
51
+ quotas: O(),
52
+ rhythm_budget: O(),
53
+ distribution: O(),
54
+ constraints: O(),
55
+ formulas: O(),
56
+ derivations: O(),
57
+ genre_profile: S(),
58
+ formula_version: S(),
59
+ status: E(STATUS, { default: 'seed' }),
60
+ },
61
+ requirements: [],
62
+ },
63
+
64
+ volume: {
65
+ table: 'volume',
66
+ label: '卷',
67
+ fields: {
68
+ volume_id: S({ required: true }),
69
+ title: S(),
70
+ chapter_start: I({ required: true }),
71
+ chapter_end: I({ required: true }),
72
+ goal: T(),
73
+ core_conflict: T(),
74
+ emotional_arc: T(),
75
+ ending_hook: T(),
76
+ status: E(STATUS, { default: 'seed' }),
77
+ },
78
+ requirements: [],
79
+ },
80
+
81
+ act: {
82
+ table: 'act',
83
+ label: '幕',
84
+ fields: {
85
+ act_id: S({ required: true }),
86
+ volume_id: S({ required: true }),
87
+ act_index: I({ required: true }),
88
+ chapter_start: I({ required: true }),
89
+ chapter_end: I({ required: true }),
90
+ goal: T(),
91
+ climax: T(),
92
+ status: E(STATUS, { default: 'seed' }),
93
+ },
94
+ requirements: [],
95
+ },
96
+
97
+ world_doc: {
98
+ table: 'world_doc',
99
+ label: '世界观',
100
+ fields: {
101
+ premise: T(),
102
+ theme: A(),
103
+ tone: S(),
104
+ world_rules: A(),
105
+ power_system: T(),
106
+ social_order: T(),
107
+ taboos: A(),
108
+ core_imagery: A(),
109
+ reader_promise: T(),
110
+ status: E(STATUS, { default: 'seed' }),
111
+ },
112
+ requirements: [],
113
+ },
114
+
115
+ story_doc: {
116
+ table: 'story_doc',
117
+ label: '故事',
118
+ fields: {
119
+ core_question: T(),
120
+ core_conflict: T(),
121
+ structure: O(),
122
+ anti_tropes: A(),
123
+ promise: T(),
124
+ status: E(STATUS, { default: 'seed' }),
125
+ },
126
+ requirements: [],
127
+ },
128
+
129
+ line: {
130
+ table: 'line',
131
+ label: '线索',
132
+ fields: {
133
+ line_id: S({ required: true }),
134
+ kind: E(['main', 'branch'], { default: 'main' }),
135
+ name: S({ required: true }),
136
+ goal: T(),
137
+ start: S(),
138
+ turning: S(),
139
+ resolution: S(),
140
+ interweaves: A(),
141
+ status: E(STATUS, { default: 'seed' }),
142
+ },
143
+ requirements: [],
144
+ },
145
+
146
+ line_node: {
147
+ table: 'line_node',
148
+ label: '线索节点',
149
+ fields: {
150
+ node_id: S({ required: true }),
151
+ line_id: S({ required: true }),
152
+ volume: S(),
153
+ act: S(),
154
+ goal: T(),
155
+ turn: T(),
156
+ status: E(STATUS, { default: 'seed' }),
157
+ },
158
+ requirements: [],
159
+ },
160
+
161
+ ending: {
162
+ table: 'ending',
163
+ label: '结局',
164
+ fields: {
165
+ ending_id: S({ required: true }),
166
+ level: S(),
167
+ type: S(),
168
+ direction: T(),
169
+ required_foreshadow_recovery: A(),
170
+ status: E(STATUS, { default: 'seed' }),
171
+ },
172
+ requirements: [],
173
+ },
174
+
175
+ role_slot: {
176
+ table: 'role_slot',
177
+ label: '角色功能位',
178
+ fields: {
179
+ role_id: S({ required: true }),
180
+ name: S({ required: true }),
181
+ slot_function: T(),
182
+ quota_key: S(),
183
+ status: E(STATUS, { default: 'seed' }),
184
+ },
185
+ requirements: [],
186
+ },
187
+
188
+ emotion_doc: {
189
+ table: 'emotion_doc',
190
+ label: '情绪曲线',
191
+ fields: {
192
+ volumes: A(),
193
+ status: E(STATUS, { default: 'seed' }),
194
+ },
195
+ requirements: [],
196
+ },
197
+
198
+ character: {
199
+ table: 'character',
200
+ label: '人物',
201
+ fields: {
202
+ role_slot: S(),
203
+ name: S({ required: true }),
204
+ type: E([
205
+ '主角', '核心配角', '贯穿级核心配角', '卷级配角', '卷级重要配角',
206
+ '核心反派', '贯穿级核心反派', '卷级反派',
207
+ ]),
208
+ faction: S(),
209
+ description: T(),
210
+ goal: O(),
211
+ flaw: T(),
212
+ golden_finger: T(),
213
+ information_state: O(),
214
+ arc: O(),
215
+ voice: O(),
216
+ traits: A(),
217
+ exit: O(),
218
+ status: E(STATUS, { default: 'seed' }),
219
+ },
220
+ requirements: [
221
+ { kind: 'character_arc', hard: false, label: '角色弧光' },
222
+ { kind: 'character_voice', hard: false, label: '说话风格' },
223
+ ],
224
+ },
225
+
226
+ faction: {
227
+ table: 'faction',
228
+ label: '势力',
229
+ fields: {
230
+ name: S({ required: true }),
231
+ type: S(),
232
+ description: T(),
233
+ goal: T(),
234
+ resources: A(),
235
+ territory: A(),
236
+ internal_conflict: T(),
237
+ status: E(STATUS, { default: 'seed' }),
238
+ },
239
+ requirements: [],
240
+ },
241
+
242
+ location: {
243
+ table: 'location',
244
+ label: '地点',
245
+ fields: {
246
+ name: S({ required: true }),
247
+ type: S(),
248
+ description: T(),
249
+ narrative_function: T(),
250
+ rules: A(),
251
+ status: E(STATUS, { default: 'seed' }),
252
+ },
253
+ requirements: [],
254
+ },
255
+
256
+ item: {
257
+ table: 'item',
258
+ label: '道具',
259
+ fields: {
260
+ name: S({ required: true }),
261
+ category: E(['core', 'branch', 'symbolic', '核心', '支线', '象征']),
262
+ description: T(),
263
+ item_function: T(),
264
+ owner: S(),
265
+ limit: T(),
266
+ related_characters: A(),
267
+ related_conflicts: A(),
268
+ status: E(STATUS, { default: 'seed' }),
269
+ },
270
+ requirements: [],
271
+ },
272
+
273
+ conflict: {
274
+ table: 'conflict',
275
+ label: '冲突',
276
+ fields: {
277
+ name: S({ required: true }),
278
+ type: E(['core', 'phase', 'branch', '核心', '阶段', '交织']),
279
+ description: T(),
280
+ core_goal: T(),
281
+ stakes: T(),
282
+ escalation_path: A(),
283
+ resolution_plan: O(),
284
+ participants: A(),
285
+ status: E(STATUS, { default: 'seed' }),
286
+ },
287
+ requirements: [],
288
+ },
289
+
290
+ foreshadow: {
291
+ table: 'foreshadow',
292
+ label: '伏笔',
293
+ fields: {
294
+ name: S({ required: true }),
295
+ type: E(['small', 'medium', 'large', '小', '中', '大']),
296
+ setup: T(),
297
+ clues: A(),
298
+ payoff_target: S(),
299
+ recovery_requirement: T(),
300
+ red_herrings: A(),
301
+ scope: E(['book', 'volume', 'arc', 'chapter']),
302
+ initial_knowledge: A(),
303
+ status: E(STATUS, { default: 'seed' }),
304
+ },
305
+ requirements: [
306
+ { kind: 'foreshadow_plant', hard: true, label: '埋设', due: 'chapter' },
307
+ { kind: 'foreshadow_progress', hard: false, label: '推进', due: 'chapter' },
308
+ { kind: 'foreshadow_reveal', hard: true, label: '回收/揭示', due: 'chapter' },
309
+ ],
310
+ },
311
+
312
+ payoff: {
313
+ table: 'payoff',
314
+ label: '爽点',
315
+ fields: {
316
+ name: S({ required: true }),
317
+ type: S(),
318
+ scale: E(['small', 'medium', 'large', '小', '中', '大']),
319
+ setup: T(),
320
+ payoff: T(),
321
+ emotional_effect: T(),
322
+ required_setup: A(),
323
+ status: E(STATUS, { default: 'seed' }),
324
+ },
325
+ requirements: [
326
+ { kind: 'payoff_setup', hard: true, label: '铺垫' },
327
+ { kind: 'payoff_payoff', hard: true, label: '兑现' },
328
+ ],
329
+ },
330
+
331
+ climax: {
332
+ table: 'climax',
333
+ label: '高潮',
334
+ fields: {
335
+ name: S({ required: true }),
336
+ type: E(['small', 'medium', 'large', '小', '中', '大']),
337
+ description: T(),
338
+ payoff: S(),
339
+ participants: A(),
340
+ status: E(STATUS, { default: 'seed' }),
341
+ },
342
+ requirements: [],
343
+ },
344
+
345
+ information: {
346
+ table: 'information',
347
+ label: '信息差',
348
+ fields: {
349
+ conflict: S(),
350
+ info_name: S({ required: true }),
351
+ who_knows: A(),
352
+ who_does_not_know: A(),
353
+ resolution_method: T(),
354
+ writing_ban: T(),
355
+ initial_knowledge: A(),
356
+ status: E(STATUS, { default: 'seed' }),
357
+ },
358
+ requirements: [],
359
+ },
360
+
361
+ chapter: {
362
+ table: 'chapter',
363
+ label: '章节',
364
+ fields: {
365
+ chapter_id: S({ required: true }),
366
+ display_no: I({ required: true }),
367
+ title: S({ required: true }),
368
+ volume: S({ required: true }),
369
+ act: S({ required: true }),
370
+ pov: S(),
371
+ time: O(),
372
+ why: O(),
373
+ how: O(),
374
+ emotion: O(),
375
+ hook: O(),
376
+ word_target: I({ required: true }),
377
+ status: E(STATUS, { default: 'seed' }),
378
+ },
379
+ requirements: [
380
+ { kind: 'chapter_outline', hard: true, label: '细纲' },
381
+ { kind: 'chapter_draft', hard: true, label: '正文' },
382
+ ],
383
+ },
384
+
385
+ outline: {
386
+ table: 'outline',
387
+ label: '细纲',
388
+ fields: {
389
+ chapter_id: S({ required: true }),
390
+ number: I(),
391
+ title: S(),
392
+ volume: S(),
393
+ act: S(),
394
+ word_target: I(),
395
+ pov: O(),
396
+ time: O(),
397
+ why: O(),
398
+ continuity: O(),
399
+ coverage: O(),
400
+ must_write: A({ required: true, nonEmpty: true }),
401
+ must_not_write: A({ required: true, nonEmpty: true }),
402
+ ending: O(),
403
+ status: E(STATUS, { default: 'seed' }),
404
+ },
405
+ requirements: [
406
+ { kind: 'outline_scenes', hard: true, label: '场景' },
407
+ { kind: 'outline_cast', hard: false, label: '出场人物' },
408
+ ],
409
+ },
410
+
411
+ scene: {
412
+ table: 'scene',
413
+ label: '场景',
414
+ fields: {
415
+ outline_id: S({ required: true }),
416
+ scene_order: I({ required: true }),
417
+ goal: T(),
418
+ obstacle: T(),
419
+ turn: T(),
420
+ outcome: T(),
421
+ location: S(),
422
+ cast: A(),
423
+ status: E(STATUS, { default: 'seed' }),
424
+ },
425
+ requirements: [],
426
+ },
427
+
428
+ chapter_text: {
429
+ table: 'chapter_text',
430
+ label: '正文',
431
+ fields: {
432
+ chapter_id: S({ required: true }),
433
+ display_no: I({ required: true }),
434
+ content: T({ required: true }),
435
+ word_count: I(),
436
+ status: E(['draft', 'final'], { default: 'draft' }),
437
+ },
438
+ requirements: [],
439
+ },
440
+
441
+ handoff: {
442
+ table: 'handoff',
443
+ label: '章末交接',
444
+ fields: {
445
+ chapter_id: S({ required: true }),
446
+ end_location: S(),
447
+ end_time: O(),
448
+ end_characters: A(),
449
+ open_threads: A(),
450
+ next_scene_hint: T(),
451
+ continuity_notes: T(),
452
+ status: E(['draft', 'final'], { default: 'draft' }),
453
+ },
454
+ requirements: [],
455
+ },
456
+
457
+ requirement: {
458
+ table: 'requirement',
459
+ label: '待办',
460
+ internal: true,
461
+ fields: {
462
+ kind: S({ required: true }),
463
+ subject: S({ required: true }),
464
+ status: E(['pending', 'filled', 'waived'], { default: 'pending' }),
465
+ hard: B({ default: true }),
466
+ due_chapter: I(),
467
+ filled_by: S(),
468
+ label: S(),
469
+ },
470
+ requirements: [],
471
+ },
472
+
473
+ change_set: {
474
+ table: 'change_set',
475
+ label: '写入事务',
476
+ internal: true,
477
+ fields: {
478
+ workflow: S(),
479
+ summary: T(),
480
+ status: E(['committed', 'rolled_back', 'superseded'], { default: 'committed' }),
481
+ parent: S(),
482
+ },
483
+ requirements: [],
484
+ },
485
+
486
+ decision: {
487
+ table: 'decision',
488
+ label: '设计决策',
489
+ internal: true,
490
+ fields: {
491
+ change_set: S({ required: true }),
492
+ scope: S(),
493
+ subject: S(),
494
+ text: T({ required: true }),
495
+ source: S(),
496
+ reference: S(),
497
+ reason: T(),
498
+ status: E(['active', 'superseded', 'rolled_back'], { default: 'active' }),
499
+ supersedes: S(),
500
+ },
501
+ requirements: [],
502
+ },
503
+
504
+ validation_issue: {
505
+ table: 'validation_issue',
506
+ label: '校验问题',
507
+ internal: true,
508
+ fields: {
509
+ change_set: S(),
510
+ scope: S(),
511
+ code: S({ required: true }),
512
+ severity: E(['error', 'warning', 'pending'], { default: 'warning' }),
513
+ message: T({ required: true }),
514
+ hint: T(),
515
+ status: E(['open', 'resolved', 'ignored'], { default: 'open' }),
516
+ },
517
+ requirements: [],
518
+ },
519
+ };
520
+
521
+ /**
522
+ * Graph edges. `fills` maps an edge action to a requirement kind:
523
+ * fills: { actionValue: 'requirement_kind' }
524
+ */
525
+ export const EDGES = {
526
+ has_volume: {
527
+ table: 'has_volume', in: 'story', out: 'volume', label: '故事→卷',
528
+ fields: {},
529
+ },
530
+ has_act: {
531
+ table: 'has_act', in: 'volume', out: 'act', label: '卷→幕',
532
+ fields: {},
533
+ },
534
+ has_chapter: {
535
+ table: 'has_chapter', in: 'act', out: 'chapter', label: '幕→章',
536
+ fields: {},
537
+ },
538
+ chapter_cast: {
539
+ table: 'chapter_cast', in: 'chapter', out: 'character', label: '章节出场',
540
+ fields: {
541
+ role: E(['pov', 'cast', 'mentioned'], { required: true }),
542
+ },
543
+ },
544
+ chapter_location: {
545
+ table: 'chapter_location', in: 'chapter', out: 'location', label: '章节地点',
546
+ fields: {},
547
+ },
548
+ chapter_line_node: {
549
+ table: 'chapter_line_node', in: 'chapter', out: 'line_node', label: '章节主线节点',
550
+ fields: {},
551
+ },
552
+ chapter_branch: {
553
+ table: 'chapter_branch', in: 'chapter', out: 'line', label: '章节支线',
554
+ fields: {},
555
+ },
556
+ chapter_conflict: {
557
+ table: 'chapter_conflict', in: 'chapter', out: 'conflict', label: '章节冲突',
558
+ fields: {},
559
+ },
560
+ chapter_climax: {
561
+ table: 'chapter_climax', in: 'chapter', out: 'climax', label: '章节高潮',
562
+ fields: {},
563
+ },
564
+ chapter_item: {
565
+ table: 'chapter_item', in: 'chapter', out: 'item', label: '章节道具',
566
+ fields: { role: S() },
567
+ },
568
+ relation: {
569
+ table: 'relation', in: 'character', out: 'character', label: '人物关系',
570
+ fields: {
571
+ kind: S({ required: true }),
572
+ initial_state: T(),
573
+ changes: A(),
574
+ },
575
+ },
576
+ faction_relation: {
577
+ table: 'faction_relation', in: 'faction', out: 'faction', label: '势力关系',
578
+ fields: {
579
+ kind: S({ required: true }),
580
+ initial_state: T(),
581
+ changes: A(),
582
+ },
583
+ },
584
+ membership: {
585
+ table: 'membership', in: 'character', out: 'faction', label: '成员关系',
586
+ fields: { role: S() },
587
+ },
588
+ conflict_participant: {
589
+ table: 'conflict_participant', in: 'conflict', out: 'character', label: '冲突参与者',
590
+ fields: {},
591
+ },
592
+ climax_participant: {
593
+ table: 'climax_participant', in: 'climax', out: 'character', label: '高潮参与者',
594
+ fields: {},
595
+ },
596
+ foreshadow_payoff: {
597
+ table: 'foreshadow_payoff', in: 'foreshadow', out: 'payoff', label: '伏笔→爽点',
598
+ fields: { relation: S() },
599
+ },
600
+ foreshadow_action: {
601
+ table: 'foreshadow_action', in: 'chapter', out: 'foreshadow', label: '伏笔动作',
602
+ fields: {
603
+ action: E(['plant', 'progress', 'reveal'], { required: true }),
604
+ chapter_no: I({ required: true }),
605
+ detail: T(),
606
+ },
607
+ fills: {
608
+ plant: 'foreshadow_plant',
609
+ progress: 'foreshadow_progress',
610
+ reveal: 'foreshadow_reveal',
611
+ },
612
+ },
613
+ payoff_action: {
614
+ table: 'payoff_action', in: 'chapter', out: 'payoff', label: '爽点动作',
615
+ fields: {
616
+ action: E(['setup', 'payoff'], { required: true }),
617
+ chapter_no: I({ required: true }),
618
+ detail: T(),
619
+ },
620
+ fills: {
621
+ setup: 'payoff_setup',
622
+ payoff: 'payoff_payoff',
623
+ },
624
+ },
625
+ info_action: {
626
+ table: 'info_action', in: 'chapter', out: 'information', label: '信息差动作',
627
+ fields: {
628
+ action: E(['created', 'progressed', 'resolved'], { required: true }),
629
+ chapter_no: I({ required: true }),
630
+ detail: T(),
631
+ },
632
+ },
633
+ ending_action: {
634
+ table: 'ending_action', in: 'chapter', out: 'ending', label: '结局动作',
635
+ fields: {
636
+ action: E(['close'], { required: true }),
637
+ chapter_no: I({ required: true }),
638
+ detail: T(),
639
+ },
640
+ },
641
+ outline_scene: {
642
+ table: 'outline_scene', in: 'outline', out: 'scene', label: '细纲→场景',
643
+ fields: { scene_order: I({ required: true }) },
644
+ },
645
+ outline_cast: {
646
+ table: 'outline_cast', in: 'outline', out: 'character', label: '细纲出场',
647
+ fields: { role: S() },
648
+ },
649
+ outline_location: {
650
+ table: 'outline_location', in: 'outline', out: 'location', label: '细纲地点',
651
+ fields: {},
652
+ },
653
+ outline_foreshadow: {
654
+ table: 'outline_foreshadow', in: 'outline', out: 'foreshadow', label: '细纲伏笔',
655
+ fields: { intent: S() },
656
+ },
657
+ scene_cast: {
658
+ table: 'scene_cast', in: 'scene', out: 'character', label: '场景人物',
659
+ fields: { role: S() },
660
+ },
661
+ scene_location: {
662
+ table: 'scene_location', in: 'scene', out: 'location', label: '场景地点',
663
+ fields: {},
664
+ },
665
+ scene_foreshadow: {
666
+ table: 'scene_foreshadow', in: 'scene', out: 'foreshadow', label: '场景伏笔',
667
+ fields: { action: E(['plant', 'progress', 'reveal']) },
668
+ },
669
+ };
670
+
671
+ /** Table name -> entity definition. */
672
+ export const TABLE_TO_ENTITY = Object.fromEntries(
673
+ Object.entries(ENTITIES).map(([key, def]) => [def.table, { key, ...def }]),
674
+ );
675
+
676
+ /** Edge table -> edge definition. */
677
+ export const TABLE_TO_EDGE = Object.fromEntries(
678
+ Object.entries(EDGES).map(([key, def]) => [def.table, { key, ...def }]),
679
+ );
680
+
681
+ export const HOOK_TYPES = [
682
+ // 章尾 13
683
+ '突然揭示', '紧急危机', '未完成动作', '身份反转', '两难抉择', '神秘物品',
684
+ '倒计时', '承诺/威胁', '离奇消失', '隐藏含义', '意象钩子', '回声钩子', '留白钩子',
685
+ // 章首 7
686
+ '悬念对话', '闪前碎片', '神秘独白', '反差场景', '意象预示',
687
+ ];
688
+
689
+ export const EMOTION_PHASES = [
690
+ '平流', '蓄力', '调动', '紧张', '释放', '回暖', '转折', '压抑', '谷底', '沉重', '结算', '冷', '紧绷',
691
+ ];
692
+
693
+ export function entityFor(table) {
694
+ return TABLE_TO_ENTITY[table] || null;
695
+ }
696
+
697
+ export function edgeFor(table) {
698
+ return TABLE_TO_EDGE[table] || null;
699
+ }