@cloud411716/fancy-webnovel 1.0.46 → 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 (271) hide show
  1. package/client.js +369 -0
  2. package/cordis.patch.yml +4 -13
  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 +26 -38
  15. package/lib/atomic-write.js +109 -0
  16. package/lib/chapter-order.js +100 -0
  17. package/lib/chinese-num.js +111 -0
  18. package/lib/confirmation.js +80 -0
  19. package/lib/db-init.js +123 -0
  20. package/lib/deployed-state.js +141 -0
  21. package/lib/direction-labels.js +265 -0
  22. package/lib/error-codes.js +104 -0
  23. package/lib/infra.js +594 -0
  24. package/lib/llm-wiki.js +109 -0
  25. package/lib/material-db.js +149 -0
  26. package/lib/path-guard.js +150 -0
  27. package/lib/projects.js +400 -0
  28. package/lib/scraper-utils.js +305 -0
  29. package/lib/surreal-server.js +338 -0
  30. package/lib/topic-decision.js +860 -0
  31. package/lib/topic-fields.js +251 -0
  32. package/lib/topic-schema.js +225 -0
  33. package/lib/workspace.js +501 -0
  34. package/package.json +62 -5
  35. package/plugins/fancy-analyze/analyze-definitions.md +519 -0
  36. package/plugins/fancy-analyze/command-input.js +65 -0
  37. package/plugins/fancy-analyze/index.js +2024 -0
  38. package/plugins/fancy-analyze/ingest-coverage.js +107 -0
  39. package/plugins/fancy-analyze/schema-analyze.md +627 -0
  40. package/plugins/fancy-analyze/schema-write.md +178 -0
  41. package/plugins/fancy-analyze/scripts/append-summary-chapter.js +612 -0
  42. package/plugins/fancy-analyze/scripts/split_source.js +292 -0
  43. package/plugins/fancy-analyze/summary-progress.js +495 -0
  44. package/plugins/fancy-analyze/templates.js +1179 -0
  45. package/plugins/fancy-bootstrap/index.js +313 -0
  46. package/plugins/fancy-bootstrap/scripts/init.js +177 -0
  47. package/plugins/fancy-bootstrap/scripts/path-utils.js +21 -0
  48. package/plugins/fancy-bootstrap/templates.js +71 -0
  49. package/plugins/fancy-db/index.js +172 -0
  50. package/plugins/fancy-douyin/deploy.js +134 -0
  51. package/plugins/fancy-douyin/index.js +551 -0
  52. package/plugins/fancy-douyin/prompts/01-analysis-prompt.md +84 -0
  53. package/plugins/fancy-douyin/scripts/crawl-note.cjs +194 -0
  54. package/plugins/fancy-douyin/scripts/lib.cjs +197 -0
  55. package/plugins/fancy-douyin/scripts/scan-account.cjs +128 -0
  56. package/plugins/fancy-douyin/scripts/start-chrome.cjs +203 -0
  57. package/plugins/fancy-douyin/state.js +197 -0
  58. package/plugins/fancy-douyin/templates.js +127 -0
  59. package/plugins/fancy-export/exporter.js +805 -0
  60. package/plugins/fancy-export/index.js +63 -0
  61. package/plugins/fancy-import/import-core.js +1118 -0
  62. package/plugins/fancy-import/import-driver.js +604 -0
  63. package/plugins/fancy-import/index.js +738 -0
  64. package/plugins/fancy-import/prompts/01-import-prompt.md +131 -0
  65. package/plugins/fancy-import/templates.js +58 -0
  66. package/plugins/fancy-material/exporter.js +153 -0
  67. package/plugins/fancy-material/genres.js +256 -0
  68. package/plugins/fancy-material/index.js +835 -0
  69. package/plugins/fancy-material/inventory.js +143 -0
  70. package/plugins/fancy-material/kinds.js +313 -0
  71. package/plugins/fancy-material/queue.js +55 -0
  72. package/plugins/fancy-material/scripts/query.js +254 -0
  73. package/plugins/fancy-material/templates.js +258 -0
  74. package/plugins/fancy-material/tool-deploy.js +138 -0
  75. package/plugins/fancy-material/validate.js +149 -0
  76. package/plugins/fancy-project/index.js +133 -0
  77. package/plugins/fancy-scan/existing-block.js +100 -0
  78. package/plugins/fancy-scan/index.js +708 -0
  79. package/plugins/fancy-scan/prompts/01-analysis-prompt.md +302 -0
  80. package/plugins/fancy-scan/scripts/append-block.js +725 -0
  81. package/plugins/fancy-scan/scripts/report-validate.js +824 -0
  82. package/plugins/fancy-scan/scripts/run-scan.js +667 -0
  83. package/plugins/fancy-scan/scripts/scrapers/fanqie-rank-scraper.js +923 -0
  84. package/plugins/fancy-scan/scripts/scrapers/jjwxc-rank-scraper.js +469 -0
  85. package/plugins/fancy-scan/scripts/scrapers/qimao-rank-scraper.js +629 -0
  86. package/plugins/fancy-scan/templates/blocks.js +155 -0
  87. package/plugins/fancy-scan/templates/platforms.js +372 -0
  88. package/plugins/fancy-scan/templates/report-schema.js +90 -0
  89. package/plugins/fancy-scan/templates.js +694 -0
  90. package/plugins/fancy-scan/tool-deploy.js +335 -0
  91. package/plugins/fancy-scan/wait-turn.js +46 -0
  92. package/plugins/fancy-story-builder/db/README.md +106 -0
  93. package/plugins/fancy-story-builder/db/client.js +228 -0
  94. package/plugins/fancy-story-builder/db/commands.js +145 -0
  95. package/plugins/fancy-story-builder/db/ids.js +39 -0
  96. package/plugins/fancy-story-builder/db/import.js +798 -0
  97. package/plugins/fancy-story-builder/db/project.js +299 -0
  98. package/plugins/fancy-story-builder/db/query.js +361 -0
  99. package/plugins/fancy-story-builder/db/schema.js +417 -0
  100. package/plugins/fancy-story-builder/db/stage-sync.js +238 -0
  101. package/plugins/fancy-story-builder/deploy-manifest.js +239 -0
  102. package/plugins/fancy-story-builder/deslop-driver.js +821 -0
  103. package/plugins/fancy-story-builder/flesh-output.js +168 -0
  104. package/plugins/fancy-story-builder/index.js +6861 -0
  105. package/plugins/fancy-story-builder/patch-driver.js +390 -0
  106. package/plugins/fancy-story-builder/prompts/01-skeleton-prompt.md +563 -0
  107. package/plugins/fancy-story-builder/prompts/02-flesh-prompt.md +602 -0
  108. package/plugins/fancy-story-builder/prompts/03-rhythm-prompt.md +539 -0
  109. package/plugins/fancy-story-builder/prompts/04-writing-prompt.md +225 -0
  110. package/plugins/fancy-story-builder/prompts/05-deslop-prompt.md +96 -0
  111. package/plugins/fancy-story-builder/purpose-template.md +83 -0
  112. package/plugins/fancy-story-builder/references/formulas-rhythm.json +302 -0
  113. package/plugins/fancy-story-builder/references/formulas-skeleton.json +402 -0
  114. package/plugins/fancy-story-builder/references/genre-profiles.json +3514 -0
  115. package/plugins/fancy-story-builder/references/glossary.md +594 -0
  116. package/plugins/fancy-story-builder/references/index.md +581 -0
  117. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/00-index.md +174 -0
  118. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/01-scene.md +150 -0
  119. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/02-chapter-workflow.md +99 -0
  120. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/03-opening-chapter.md +104 -0
  121. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/04-dialogue-mastery.md +274 -0
  122. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/05-genre-formulas.md +432 -0
  123. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/06-tone-ensemble.md +259 -0
  124. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/07-ledger-audit.md +222 -0
  125. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/08-diagnosis-revision.md +205 -0
  126. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/09-reading-power.md +135 -0
  127. package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/10-deslop.md +129 -0
  128. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/00-index.md +98 -0
  129. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/01-upgrade-rhythm.md +300 -0
  130. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/02-arc-rhythm.md +168 -0
  131. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/03-highpoint-hooks.md +189 -0
  132. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/04-satisfaction.md +226 -0
  133. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/05-foreshadow-density.md +195 -0
  134. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/06-chapter-outline.md +226 -0
  135. package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/07-audit.md +181 -0
  136. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/00-index.md +100 -0
  137. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/01-character.md +243 -0
  138. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/02-worldbuilding.md +245 -0
  139. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/03-relations.md +185 -0
  140. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/04-emotion-methods.md +286 -0
  141. package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/05-audit.md +187 -0
  142. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/00-index.md +91 -0
  143. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/01-positioning.md +209 -0
  144. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/02-packaging.md +135 -0
  145. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/03-entities.md +305 -0
  146. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/04-conflict.md +328 -0
  147. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/05-quota.md +187 -0
  148. package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/06-audit.md +160 -0
  149. package/plugins/fancy-story-builder/rhythm-output.js +140 -0
  150. package/plugins/fancy-story-builder/schema-setting.json +1100 -0
  151. package/plugins/fancy-story-builder/schema-setting.md +232 -0
  152. package/plugins/fancy-story-builder/scripts/ai-patterns.js +619 -0
  153. package/plugins/fancy-story-builder/scripts/brief-validate.js +164 -0
  154. package/plugins/fancy-story-builder/scripts/check-guide-compliance.js +289 -0
  155. package/plugins/fancy-story-builder/scripts/content-quality.js +416 -0
  156. package/plugins/fancy-story-builder/scripts/deslop-validate.js +143 -0
  157. package/plugins/fancy-story-builder/scripts/error-classes.js +112 -0
  158. package/plugins/fancy-story-builder/scripts/flesh-validate.js +1312 -0
  159. package/plugins/fancy-story-builder/scripts/formula-engine.js +112 -0
  160. package/plugins/fancy-story-builder/scripts/graph-utils.js +1484 -0
  161. package/plugins/fancy-story-builder/scripts/label-coverage.js +171 -0
  162. package/plugins/fancy-story-builder/scripts/prompt-drift-check.js +441 -0
  163. package/plugins/fancy-story-builder/scripts/reading-power.js +298 -0
  164. package/plugins/fancy-story-builder/scripts/reference-log.js +110 -0
  165. package/plugins/fancy-story-builder/scripts/rhythm-validate.js +1312 -0
  166. package/plugins/fancy-story-builder/scripts/schema-pages.js +2668 -0
  167. package/plugins/fancy-story-builder/scripts/semantic-lint.js +381 -0
  168. package/plugins/fancy-story-builder/scripts/skeleton-quota.js +145 -0
  169. package/plugins/fancy-story-builder/scripts/skeleton-validate.js +1973 -0
  170. package/plugins/fancy-story-builder/scripts/stage-batches.js +1405 -0
  171. package/plugins/fancy-story-builder/scripts/stage-contract.js +429 -0
  172. package/plugins/fancy-story-builder/scripts/story-artifacts.js +933 -0
  173. package/plugins/fancy-story-builder/scripts/upstream-query.js +198 -0
  174. package/plugins/fancy-story-builder/scripts/v2/baseline.js +112 -0
  175. package/plugins/fancy-story-builder/scripts/v2/cast.js +517 -0
  176. package/plugins/fancy-story-builder/scripts/v2/context.js +434 -0
  177. package/plugins/fancy-story-builder/scripts/v2/contracts.js +493 -0
  178. package/plugins/fancy-story-builder/scripts/v2/core.js +419 -0
  179. package/plugins/fancy-story-builder/scripts/v2/engine.js +1068 -0
  180. package/plugins/fancy-story-builder/scripts/v2/export-markdown.js +794 -0
  181. package/plugins/fancy-story-builder/scripts/v2/genre.js +195 -0
  182. package/plugins/fancy-story-builder/scripts/v2/knowledge.js +321 -0
  183. package/plugins/fancy-story-builder/scripts/v2/macro.js +409 -0
  184. package/plugins/fancy-story-builder/scripts/v2/manifest.js +538 -0
  185. package/plugins/fancy-story-builder/scripts/v2/migrate.js +137 -0
  186. package/plugins/fancy-story-builder/scripts/v2/prompts.js +1335 -0
  187. package/plugins/fancy-story-builder/scripts/v2/rhythm.js +618 -0
  188. package/plugins/fancy-story-builder/scripts/v2/scale.js +843 -0
  189. package/plugins/fancy-story-builder/scripts/v2/snapshot.js +121 -0
  190. package/plugins/fancy-story-builder/scripts/v2/stages.js +273 -0
  191. package/plugins/fancy-story-builder/scripts/v2/writing.js +216 -0
  192. package/plugins/fancy-story-builder/scripts/writing-rules.js +167 -0
  193. package/plugins/fancy-story-builder/scripts/writing-validate.js +682 -0
  194. package/plugins/fancy-story-builder/skeleton-output.js +199 -0
  195. package/plugins/fancy-story-builder/stage-driver.js +1049 -0
  196. package/plugins/fancy-story-builder/storydb/README.md +271 -0
  197. package/plugins/fancy-story-builder/storydb/cli.js +458 -0
  198. package/plugins/fancy-story-builder/storydb/client.js +351 -0
  199. package/plugins/fancy-story-builder/storydb/compile.js +452 -0
  200. package/plugins/fancy-story-builder/storydb/context.js +288 -0
  201. package/plugins/fancy-story-builder/storydb/definitions.js +699 -0
  202. package/plugins/fancy-story-builder/storydb/driver.js +106 -0
  203. package/plugins/fancy-story-builder/storydb/dsh-tools.js +313 -0
  204. package/plugins/fancy-story-builder/storydb/export.js +175 -0
  205. package/plugins/fancy-story-builder/storydb/ids.js +31 -0
  206. package/plugins/fancy-story-builder/storydb/impact.js +170 -0
  207. package/plugins/fancy-story-builder/storydb/import.js +559 -0
  208. package/plugins/fancy-story-builder/storydb/mcp.js +99 -0
  209. package/plugins/fancy-story-builder/storydb/prompts.js +952 -0
  210. package/plugins/fancy-story-builder/storydb/stage-command.js +379 -0
  211. package/plugins/fancy-story-builder/storydb/stage.js +426 -0
  212. package/plugins/fancy-story-builder/storydb/store.js +891 -0
  213. package/plugins/fancy-story-builder/storydb/tools.js +592 -0
  214. package/plugins/fancy-story-builder/storydb/validation.js +183 -0
  215. package/plugins/fancy-story-builder/templates/v2/10-scale/scale-template.json +144 -0
  216. package/plugins/fancy-story-builder/templates/v2/20-macro/emotion-template.json +15 -0
  217. package/plugins/fancy-story-builder/templates/v2/20-macro/endings-template.json +16 -0
  218. package/plugins/fancy-story-builder/templates/v2/20-macro/lines-template.json +28 -0
  219. package/plugins/fancy-story-builder/templates/v2/20-macro/roles-template.json +10 -0
  220. package/plugins/fancy-story-builder/templates/v2/20-macro/story-template.json +19 -0
  221. package/plugins/fancy-story-builder/templates/v2/20-macro/volumes-template.json +24 -0
  222. package/plugins/fancy-story-builder/templates/v2/20-macro/world-template.json +24 -0
  223. package/plugins/fancy-story-builder/templates/v2/30-cast/characters-template.json +49 -0
  224. package/plugins/fancy-story-builder/templates/v2/30-cast/climaxes-template.json +14 -0
  225. package/plugins/fancy-story-builder/templates/v2/30-cast/conflicts-template.json +30 -0
  226. package/plugins/fancy-story-builder/templates/v2/30-cast/factions-template.json +18 -0
  227. package/plugins/fancy-story-builder/templates/v2/30-cast/foreshadows-template.json +29 -0
  228. package/plugins/fancy-story-builder/templates/v2/30-cast/information-template.json +29 -0
  229. package/plugins/fancy-story-builder/templates/v2/30-cast/items-template.json +13 -0
  230. package/plugins/fancy-story-builder/templates/v2/30-cast/locations-template.json +12 -0
  231. package/plugins/fancy-story-builder/templates/v2/30-cast/payoffs-template.json +16 -0
  232. package/plugins/fancy-story-builder/templates/v2/30-cast/relations-template.json +44 -0
  233. package/plugins/fancy-story-builder/templates/v2/40-rhythm/chapter-map-template.json +81 -0
  234. package/plugins/fancy-story-builder/templates/v2/40-rhythm/outline-template.json +162 -0
  235. package/plugins/fancy-story-builder/templates/v2/50-writing/ledger-template.json +46 -0
  236. package/plugins/fancy-story-builder/templates/v2/60-deslop/report-template.json +10 -0
  237. 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 +427 -0
  238. 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
  239. 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
  240. 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
  241. 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 +168 -0
  242. 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 +184 -0
  243. 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
  244. 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 +131 -0
  245. 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 +85 -0
  246. 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 +103 -0
  247. 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 +68 -0
  248. 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 +142 -0
  249. 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 +292 -0
  250. 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 +266 -0
  251. 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 +336 -0
  252. 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
  253. 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 +308 -0
  254. package/plugins/fancy-story-builder/templates.js +1446 -0
  255. package/plugins/fancy-story-builder/wiki-references.js +540 -0
  256. package/plugins/fancy-story-builder/writing-driver.js +1388 -0
  257. package/plugins/fancy-topic/index.js +1057 -0
  258. package/plugins/fancy-topic/prompts/01-exploration-prompt.md +115 -0
  259. package/plugins/fancy-topic/scripts/save-suggestion.js +219 -0
  260. package/plugins/fancy-topic/scripts/select-topic.js +317 -0
  261. package/plugins/fancy-topic/templates.js +476 -0
  262. package/plugins/fancy-topic/tool-deploy.js +200 -0
  263. package/commands/bootstrap/index.js +0 -164
  264. package/commands/bootstrap/init-project.js +0 -121
  265. package/commands/scan/index.js +0 -283
  266. package/commands/scan/platforms/fanqie.js +0 -146
  267. package/commands/scan/platforms/jinjiang.js +0 -143
  268. package/commands/scan/platforms/qidian.js +0 -102
  269. package/commands/scan/platforms/qimao.js +0 -127
  270. package/commands/scan/scraper.js +0 -195
  271. package/infra.js +0 -160
@@ -0,0 +1,298 @@
1
+ /**
2
+ * reading-power.js —— 阅读动力的规则真源
3
+ *
4
+ * 这些规则原先散在旧版 skill 的两份 markdown 里(旧 fancy-write/references/
5
+ * hooks.md 与 reading-power.md),是纯给人读的规范,没法程序判定:
6
+ * · hooks.md —— 13 式章末钩子 + 7 式章首引子 + "严禁连续 3 章用同一种"
7
+ * · reading-power.md —— 5 大钩子分类 / 5 级强度 / 8 大爽点 / 7 种微兑现 / 30-40-30
8
+ *
9
+ * 现在落成**可判定的词表**:写作阶段每章把用了哪种钩子、含哪些微兑现写进章台账,
10
+ * writing-validate.js 拿这些词表逐条对账(见 validateChapter)。
11
+ *
12
+ * 设计要点:
13
+ * 1. 词表是**唯一真源**。旧 skill 里有三份互相漂移的副本(deslop 的 24 类、
14
+ * review 声称的"19 类"、代码里的 21 类),这里不再重复造第四份。
15
+ * 2. 每条都带 id。台账里记的是 id(不是中文名),改名不会让历史台账失效。
16
+ * 3. 判定与文案分离:这里只回答"哪些合法 / 哪些违规",
17
+ * "应该写成什么样"是提示词和写作指导的事。
18
+ */
19
+
20
+ /**
21
+ * 13 式章末钩子。
22
+ * 每章末必用其中一种 —— 这是"读者为什么要翻下一章"的答案。
23
+ */
24
+ export const TAIL_HOOK_TYPES = [
25
+ { id: 'sudden_reveal', name: '突然揭示', category: '悬念钩', desc: '一句话把读者以为知道的事掀翻' },
26
+ { id: 'urgent_crisis', name: '紧急危机', category: '危机钩', desc: '危险正在逼近,角色必须立刻行动' },
27
+ { id: 'unfinished_action', name: '未完成的动作', category: '选择钩', desc: '动作停在最要紧的一步' },
28
+ { id: 'identity_flip', name: '身份反转', category: '悬念钩', desc: '身份/立场的反转' },
29
+ { id: 'dilemma', name: '两难选择', category: '选择钩', desc: '二选一,且两边都有代价' },
30
+ { id: 'mystery_item', name: '神秘物品', category: '悬念钩', desc: '出现一个来历不明的物或线索' },
31
+ { id: 'time_limit', name: '时间限制', category: '危机钩', desc: '倒计时开始了' },
32
+ { id: 'promise_threat', name: '承诺威胁', category: '情绪钩', desc: '有人在预告/威胁后果' },
33
+ { id: 'odd_disappearance', name: '离奇消失', category: '悬念钩', desc: '人或物凭空不见' },
34
+ { id: 'implication', name: '言外之意', category: '情绪钩', desc: '话没说破,但读者听懂了' },
35
+ { id: 'imagery', name: '意象钩子', category: '悬念钩', desc: '用反复出现的意象收尾' },
36
+ { id: 'echo', name: '回声钩子', category: '情绪钩', desc: '结尾与开篇形成回响' },
37
+ { id: 'blank', name: '留白钩子', category: '悬念钩', desc: '停在关键信息之前,让读者自己脑补' }
38
+ ];
39
+
40
+ /**
41
+ * 7 式章首引子。
42
+ * 每章首必用其中一种 —— 决定读者第一段会不会滑走。
43
+ */
44
+ export const HEAD_HOOK_TYPES = [
45
+ { id: 'dialogue_suspense', name: '悬念对话引子', desc: '用一句对话把悬念抛出来' },
46
+ { id: 'inner_monologue', name: '内心独白引子', desc: '从角色的内心切进去' },
47
+ { id: 'contrast_scene', name: '反差场景引子', desc: '两个画面并置,反差制造张力' },
48
+ { id: 'unfinished_move', name: '未完成动作引子', desc: '动作停在半途' },
49
+ { id: 'imagery_foreshadow', name: '意象伏笔引子', desc: '用意象埋伏笔' },
50
+ { id: 'time_shift', name: '时间错位引子', desc: '时间跳跃制造疑问' },
51
+ { id: 'object_detail', name: '物件细节引子', desc: '从一个物件切进去' }
52
+ ];
53
+
54
+ /**
55
+ * 5 大钩子分类 —— 用来判断一章的钩子"有没有变化"。
56
+ * 同一分类连续用 3 章 = 钩子开始重复(比同一种更隐蔽,所以两层都要查)。
57
+ */
58
+ export const HOOK_CATEGORIES = ['危机钩', '悬念钩', '渴望钩', '情绪钩', '选择钩'];
59
+
60
+ /**
61
+ * 钩子强度 5 级。中间章用 1-2 级,高潮章用 3-5 级。
62
+ * 强度不是越高越好 —— 全书都是 5 级反而平。
63
+ */
64
+ export const HOOK_STRENGTHS = [
65
+ { id: 'curiosity', name: '好奇悬念', level: 1, usage: '中间章节' },
66
+ { id: 'concern', name: '关切悬念', level: 2, usage: '中间章节' },
67
+ { id: 'urgent', name: '迫切悬念', level: 3, usage: '高潮章节' },
68
+ { id: 'survival', name: '生存悬念', level: 4, usage: '高潮/结局前' },
69
+ { id: 'ultimate', name: '终极悬念', level: 5, usage: '全书结尾' }
70
+ ];
71
+
72
+ /**
73
+ * 8 大爽点模式。识别靠标识与结构,不靠词表 ——
74
+ * 这一项只能由模型判(LLM 报 finding),脚本只校验报上来的类型合法。
75
+ */
76
+ export const PAYOFF_PATTERNS = [
77
+ { id: 'flex_counter', name: '装逼打脸', en: 'Flex & Counter', arc: '嘲讽→反转→震惊' },
78
+ { id: 'underdog_reveal', name: '扮猪吃虎', en: 'Underdog Reveal', arc: '示弱→暴露→碾压' },
79
+ { id: 'underdog_victory', name: '越级反杀', en: 'Underdog Victory', arc: '差距→策略→逆转' },
80
+ { id: 'authority_challenge', name: '打脸权威', en: 'Authority Challenge', arc: '权威→挑战→成功' },
81
+ { id: 'villain_downfall', name: '反派翻车', en: 'Villain Downfall', arc: '得意→反杀→落幕' },
82
+ { id: 'sweet_surprise', name: '甜蜜超预期', en: 'Sweet Surprise', arc: '期待→超预期→升华' },
83
+ { id: 'misunderstanding', name: '迪化误解', en: 'Misunderstanding Elevation', arc: '随意行为→配角脑补升华' },
84
+ { id: 'identity_reveal', name: '身份掉马', en: 'Identity Reveal', arc: '身份伪装→关键时刻→揭露' }
85
+ ];
86
+
87
+ /**
88
+ * 7 种微兑现 —— 每章至少含 1 个。
89
+ * "微兑现"是让读者觉得这一章没白读的东西:哪怕很小,也得给一个。
90
+ */
91
+ export const MICRO_PAYOFFS = [
92
+ { id: 'info', name: '信息兑现', desc: '揭示了新信息或线索' },
93
+ { id: 'relation', name: '关系兑现', desc: '关系推进或确认' },
94
+ { id: 'ability', name: '能力兑现', desc: '能力提升或掌握新技能' },
95
+ { id: 'resource', name: '资源兑现', desc: '获得物品或资源' },
96
+ { id: 'recognition', name: '认可兑现', desc: '获得认可或面子' },
97
+ { id: 'emotion', name: '情绪兑现', desc: '情绪释放或共鸣' },
98
+ { id: 'clue', name: '线索兑现', desc: '伏笔回收或推进' }
99
+ ];
100
+
101
+ /**
102
+ * 30/40/30 结构 —— 一章内部的节奏配比。
103
+ * 这里的比例是"参考值",不作为硬判定(章节长度差异太大),但要在写作指导里讲清楚。
104
+ */
105
+ export const STRUCTURE_3030 = [
106
+ { id: 'setup', name: '铺垫', ratio: 0.3, role: '建立信息差、压力、期待' },
107
+ { id: 'delivery', name: '兑现', ratio: 0.4, role: '核心爽点执行' },
108
+ { id: 'aftermath', name: '余波', ratio: 0.3, role: '反应、收获、新期待' }
109
+ ];
110
+
111
+ /** 题材偏好的微兑现类型与每章数量(参考值,不作硬判定) */
112
+ export const GENRE_PAYOFF_PREFERENCE = {
113
+ 爽文: { types: ['ability', 'resource', 'recognition'], perChapter: '2-3' },
114
+ 言情: { types: ['relation', 'emotion', 'recognition'], perChapter: '1-2' },
115
+ 悬疑: { types: ['info', 'clue'], perChapter: '1-2' }
116
+ };
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // 判定辅助
120
+ // ---------------------------------------------------------------------------
121
+
122
+ const TAIL_BY_ID = new Map(TAIL_HOOK_TYPES.map((x) => [x.id, x]));
123
+ const TAIL_BY_NAME = new Map(TAIL_HOOK_TYPES.map((x) => [x.name, x]));
124
+ const HEAD_BY_ID = new Map(HEAD_HOOK_TYPES.map((x) => [x.id, x]));
125
+ const HEAD_BY_NAME = new Map(HEAD_HOOK_TYPES.map((x) => [x.name, x]));
126
+ const MICRO_BY_ID = new Map(MICRO_PAYOFFS.map((x) => [x.id, x]));
127
+ const MICRO_BY_NAME = new Map(MICRO_PAYOFFS.map((x) => [x.name, x]));
128
+
129
+ /**
130
+ * 查一个章末钩子(台账里可能写 id,也可能写中文名)。
131
+ * @returns {{id,name,category}|null}
132
+ */
133
+ export function resolveTailHook(value) {
134
+ if (typeof value !== 'string' || !value) return null;
135
+ const s = value.trim();
136
+ return TAIL_BY_ID.get(s) || TAIL_BY_NAME.get(s) || null;
137
+ }
138
+
139
+ /**
140
+ * 查一个章首引子。
141
+ * @returns {{id,name,desc}|null}
142
+ */
143
+ export function resolveHeadHook(value) {
144
+ if (typeof value !== 'string' || !value) return null;
145
+ const s = value.trim();
146
+ return HEAD_BY_ID.get(s) || HEAD_BY_NAME.get(s) || null;
147
+ }
148
+
149
+ /**
150
+ * 查一个微兑现类型。
151
+ * @returns {{id,name,desc}|null}
152
+ */
153
+ export function resolveMicroPayoff(value) {
154
+ if (typeof value !== 'string' || !value) return null;
155
+ const s = value.trim();
156
+ return MICRO_BY_ID.get(s) || MICRO_BY_NAME.get(s) || null;
157
+ }
158
+
159
+ /** 全部合法的章末钩子 id(给提示词/文档用) */
160
+ export function tailHookIds() {
161
+ return TAIL_HOOK_TYPES.map((x) => x.id);
162
+ }
163
+
164
+ /** 全部合法的章首引子 id */
165
+ export function headHookIds() {
166
+ return HEAD_HOOK_TYPES.map((x) => x.id);
167
+ }
168
+
169
+ /** 全部合法的微兑现 id */
170
+ export function microPayoffIds() {
171
+ return MICRO_PAYOFFS.map((x) => x.id);
172
+ }
173
+
174
+ /**
175
+ * 查连续重复的钩子。
176
+ *
177
+ * 规则(旧 fancy-write/references/hooks.md:77):
178
+ * "严禁连续 3 章用同一种" —— 同一 tail_hook 连续出现 3 章就报。
179
+ * 同理,"同一大分类连续 3 章"也算重复 —— 因为 13 式里有好几式同属悬念钩,
180
+ * 换了个名字但读者感受没变。
181
+ *
182
+ * @param {Array<{ch:number, hook_tail_type?:string}>} entries 章台账条目(按 ch 升序)
183
+ * @returns {{sameType:Array, sameCategory:Array}}
184
+ * sameType —— 连续 3 章用同一 tail_hook 的段落
185
+ * sameCategory —— 连续 3 章用同一大分类的段落
186
+ */
187
+ export function findHookRepeats(entries) { const list = (entries || [])
188
+ .filter((e) => e && e.ch !== undefined && e.ch !== null)
189
+ .slice()
190
+ .sort((a, b) => a.ch - b.ch);
191
+
192
+ const sameType = [];
193
+ const sameCategory = [];
194
+
195
+ let typeRun = [];
196
+ let catRun = [];
197
+
198
+ const flush = (run, out, keyOf, label) => {
199
+ if (run.length >= 3) {
200
+ out.push({
201
+ key: keyOf(run[0]),
202
+ label: label,
203
+ chapters: run.map((e) => e.ch),
204
+ value: keyOf(run[0])
205
+ });
206
+ }
207
+ };
208
+
209
+ for (let i = 0; i < list.length; i++) {
210
+ const cur = list[i];
211
+ const hook = resolveTailHook(cur.hook_tail_type);
212
+
213
+ // 同一种
214
+ if (hook) {
215
+ if (typeRun.length && typeRun[0]._hook.id !== hook.id) {
216
+ flush(typeRun, sameType, (e) => e._hook.id, 'hook_tail_type');
217
+ typeRun = [];
218
+ }
219
+ typeRun.push(Object.assign({}, cur, { _hook: hook }));
220
+ } else {
221
+ flush(typeRun, sameType, (e) => e._hook.id, 'hook_tail_type');
222
+ typeRun = [];
223
+ }
224
+
225
+ // 同一大分类
226
+ const cat = hook ? hook.category : null;
227
+ if (cat) {
228
+ if (catRun.length && catRun[0]._cat !== cat) {
229
+ flush(catRun, sameCategory, (e) => e._cat, 'hook_tail_category');
230
+ catRun = [];
231
+ }
232
+ catRun.push(Object.assign({}, cur, { _cat: cat }));
233
+ } else {
234
+ flush(catRun, sameCategory, (e) => e._cat, 'hook_tail_category');
235
+ catRun = [];
236
+ }
237
+ }
238
+ flush(typeRun, sameType, (e) => e._hook.id, 'hook_tail_type');
239
+ flush(catRun, sameCategory, (e) => e._cat, 'hook_tail_category');
240
+
241
+ return { sameType, sameCategory };
242
+ }
243
+
244
+ // ---------------------------------------------------------------------------
245
+ // 清单生成(唯一真源 → 提示词/文档)
246
+ // ---------------------------------------------------------------------------
247
+
248
+ /**
249
+ * 生成"合法取值清单",给写作提示词动态注入。
250
+ *
251
+ * 为什么要动态生成而不是把清单写死在 markdown 里:
252
+ * 手写两份必然漂移 —— 旧 skill 正是这么坏的:
253
+ * deslop 的"24 类"、review 声称的"19 类"(实际 20 条)、
254
+ * 代码里实现的 21 类,三份互不一致,模型根本不知道以哪套为准。
255
+ *
256
+ * 这里清单只有一份(本文档),提示词、校验器、写作指导都从它派生。
257
+ *
258
+ * @returns {string} 可直接拼进提示词的 markdown 段
259
+ */
260
+ export function readingPowerLegend() {
261
+ const lines = [];
262
+ lines.push('---');
263
+ lines.push('');
264
+ lines.push('## 附:合法取值清单(由校验器判定,填错会被拦下)');
265
+ lines.push('');
266
+ lines.push('章台账里这三个字段只能填下面清单里的值(中文名或 id 都行):');
267
+ lines.push('');
268
+
269
+ lines.push('### `hook_head_type` —— 章首引子 7 式');
270
+ lines.push('');
271
+ lines.push('| id | 名称 |');
272
+ lines.push('|---|---|');
273
+ for (const h of HEAD_HOOK_TYPES) lines.push('| ' + h.id + ' | ' + h.name + ' |');
274
+ lines.push('');
275
+
276
+ lines.push('### `hook_tail_type` —— 章末钩子 13 式');
277
+ lines.push('');
278
+ lines.push('| id | 名称 | 大分类 |');
279
+ lines.push('|---|---|---|');
280
+ for (const h of TAIL_HOOK_TYPES) lines.push('| ' + h.id + ' | ' + h.name + ' | ' + h.category + ' |');
281
+ lines.push('');
282
+ lines.push('**不得连续 3 章用同一种**(同一大分类也不行):`HOOK_REPEAT`。');
283
+ lines.push('');
284
+
285
+ lines.push('### `micro_payoffs` —— 微兑现 7 种(每章至少 1 个)');
286
+ lines.push('');
287
+ lines.push('| id | 名称 |');
288
+ lines.push('|---|---|');
289
+ for (const m of MICRO_PAYOFFS) lines.push('| ' + m.id + ' | ' + m.name + ' |');
290
+ lines.push('');
291
+ lines.push('### `hook_strength` —— 钩子强度 5 级');
292
+ lines.push('');
293
+ lines.push('| id | 名称 | 级别 | 适用位置 |');
294
+ lines.push('|---|---|---|---|');
295
+ for (const s of HOOK_STRENGTHS) lines.push('| ' + s.id + ' | ' + s.name + ' | ' + s.level + ' | ' + s.usage + ' |');
296
+ lines.push('');
297
+ return lines.join('\n');
298
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * reference-log.js —— 参考决策台账
3
+ *
4
+ * 目的:LLM 每做一个会进入产物的设计决策,都要留下“依据了哪份参考、
5
+ * 具体哪一条、为什么这么用”的可追溯记录。
6
+ *
7
+ * 文件放在 `设定/.reference-decisions.md`:
8
+ * - 不属于阶段产物,不参与 schema/ingest;
9
+ * - 人类可读的 Markdown 表格;
10
+ * - 只追加,不覆盖已有行。
11
+ *
12
+ * 这里只负责初始化、计数和生成提示词块;真正的“读取参考并做决策”
13
+ * 由 LLM 执行。插件在阶段收尾时把行数报给用户,缺失时给出警告。
14
+ */
15
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
16
+ import { join } from 'node:path';
17
+
18
+ export const REFERENCE_LOG_PATH = '设定/.reference-decisions.md';
19
+ export const REFERENCE_LOG_COLUMNS = ['阶段', '批次', '决策对象', '决策内容', '参考文件', '参考条目', '备注'];
20
+
21
+ export const REFERENCE_LOG_HEADER = [
22
+ '# 参考决策记录',
23
+ '',
24
+ '> 本文件由 LLM 在每次设计决策后追加。只追加,不覆盖。',
25
+ '> 参考文件可以是 `references` 目录下的方法论,也可以是 `对标/wiki/` 下的总结。',
26
+ '',
27
+ '| ' + REFERENCE_LOG_COLUMNS.join(' | ') + ' |',
28
+ '|' + REFERENCE_LOG_COLUMNS.map(() => '---').join('|') + '|',
29
+ '',
30
+ ].join('\n');
31
+
32
+ /** 确保 `设定/.reference-decisions.md` 存在;不覆盖已有内容。 */
33
+ export function ensureReferenceDecisionLog(projectRoot) {
34
+ if (!projectRoot) return false;
35
+ const dir = join(projectRoot, '设定');
36
+ if (!existsSync(dir)) return false;
37
+ const file = join(projectRoot, REFERENCE_LOG_PATH);
38
+ if (existsSync(file)) return true;
39
+ try {
40
+ mkdirSync(dir, { recursive: true });
41
+ writeFileSync(file, REFERENCE_LOG_HEADER, { encoding: 'utf-8', flag: 'wx' });
42
+ return true;
43
+ } catch {
44
+ // 并发创建时另一个批次可能已经写了;文件存在就算成功。
45
+ return existsSync(file);
46
+ }
47
+ }
48
+
49
+ /** 解析参考决策台账里的数据行。 */
50
+ export function parseReferenceDecisionRows(projectRoot) {
51
+ const file = join(projectRoot, REFERENCE_LOG_PATH);
52
+ if (!existsSync(file)) return [];
53
+ let text = '';
54
+ try { text = readFileSync(file, 'utf-8'); } catch { return []; }
55
+ const rows = [];
56
+ for (const line of text.split(/\r?\n/)) {
57
+ const trimmed = line.trim();
58
+ if (!trimmed.startsWith('|')) continue;
59
+ if (/^\|\s*---/.test(trimmed)) continue;
60
+ if (trimmed.startsWith('| ' + REFERENCE_LOG_COLUMNS[0] + ' |')) continue;
61
+ const cells = trimmed.replace(/^\|/, '').replace(/\|$/, '').split('|').map((x) => x.trim());
62
+ if (cells.length < REFERENCE_LOG_COLUMNS.length) continue;
63
+ rows.push(Object.fromEntries(REFERENCE_LOG_COLUMNS.map((c, i) => [c, cells[i] || ''])));
64
+ }
65
+ return rows;
66
+ }
67
+
68
+ export function countReferenceDecisionRows(projectRoot) {
69
+ return parseReferenceDecisionRows(projectRoot).length;
70
+ }
71
+
72
+ /** 阶段收尾提示:把台账行数报给用户,空台账必须显眼。 */
73
+ export function referenceLogNote(projectRoot) {
74
+ const count = countReferenceDecisionRows(projectRoot);
75
+ if (count > 0) {
76
+ return '📚 参考决策记录:' + count + ' 条(' + REFERENCE_LOG_PATH + ')';
77
+ }
78
+ return '⚠️ 参考决策记录为空:本次 LLM 没有按契约把“决策依据了哪份参考”写入 `' +
79
+ REFERENCE_LOG_PATH + '`。';
80
+ }
81
+
82
+ /**
83
+ * 批次提示词里的强制记录块。
84
+ *
85
+ * 这里不把“参考文件”限死在 对标/wiki:方法论 references 目录也是参考。
86
+ */
87
+ export function referenceLogPromptBlock(stageLabel, batch, options = {}) {
88
+ const batchName = (batch && (batch.type || batch.groupKey || batch.file)) || '本批';
89
+ const allowed = options.allowedFileText || ('除本批分片外,`' + REFERENCE_LOG_PATH + '` 是本批唯一允许写的额外文件;不要修改 wiki、模板、references 目录或其它阶段。');
90
+ return [
91
+ '## 参考决策记录(必须)',
92
+ '',
93
+ '本批开始前必须实际读取本批参考清单中的 `references` 目录指导文档和 `对标/wiki` 目录总结;',
94
+ '插件注入的摘要只用于快速定位,**不能替代原文件**。本批列出的“本批优先参考”必须逐份核对;阶段级清单用于全局判断,不要求逐篇通读。',
95
+ '',
96
+ '每完成一个会进入产物的独立设计决策,就要向 `' + REFERENCE_LOG_PATH + '` 追加一行:',
97
+ '',
98
+ '| 阶段 | 批次 | 决策对象 | 决策内容 | 参考文件 | 参考条目 | 备注 |',
99
+ '|---|---|---|---|---|---|---|',
100
+ '| ' + stageLabel + ' | ' + batchName + ' | 例如 FS004 | 例如 设计“反向验证”式伏笔 | 例如 对标/wiki 下的伏笔布局总结 | 例如 用反向验证排除“整体替换”式解释 | 例如 让读者先看到结果再反推原因 |',
101
+ '',
102
+ '记录规则:',
103
+ '1. **每个独立决策至少一行**:每个实体、每条关系、每个伏笔/爽点/冲突节点、每章规划、每章细纲、每章正文都算独立决策;同一对象的多个字段如果依据不同参考,拆成多行。',
104
+ '2. `参考文件` 必须写实际读过的路径;`参考条目` 必须写原文里的标题/原则名,不能只写文件名;原文有“来源章节 / 来源实体”时,把对应章节或实体一并写进 `参考条目`。',
105
+ '3. `备注` 写清“为什么用、怎么用、哪些地方没有采用”,不要写“参考了”。',
106
+ '4. **只追加数据行**,不要重复写表头,不要覆盖或删除已有行;备注里的 `|` 写成 `\\|`。',
107
+ '5. ' + allowed,
108
+ '',
109
+ ].join('\n');
110
+ }