@lemoncat7/dsh-partner 2.8.1 → 2.8.3

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 (833) hide show
  1. package/docs/THIRD_PARTY_LICENSES.md +10 -0
  2. package/docs/blocked-delivery-recovery.md +7 -0
  3. package/docs/concern-attention.md +19 -0
  4. package/docs/observation-contract.md +21 -0
  5. package/docs/pendant-loading.md +30 -0
  6. package/docs/releases/2.8.2.md +7 -0
  7. package/docs/releases/2.8.3.md +20 -0
  8. package/lib/agent-runtime.d.ts +7 -0
  9. package/lib/agent-runtime.d.ts.map +1 -1
  10. package/lib/agent-runtime.js +119 -35
  11. package/lib/agent-runtime.js.map +1 -1
  12. package/lib/api/features/pendant-api.d.ts.map +1 -1
  13. package/lib/api/features/pendant-api.js +11 -2
  14. package/lib/api/features/pendant-api.js.map +1 -1
  15. package/lib/api.d.ts +1 -0
  16. package/lib/api.d.ts.map +1 -1
  17. package/lib/api.js +41 -6
  18. package/lib/api.js.map +1 -1
  19. package/lib/autonomy.d.ts.map +1 -1
  20. package/lib/autonomy.js +9 -2
  21. package/lib/autonomy.js.map +1 -1
  22. package/lib/channel-message.d.ts +2 -0
  23. package/lib/channel-message.d.ts.map +1 -1
  24. package/lib/channel-message.js.map +1 -1
  25. package/lib/channels/direct/message-format.js +127 -127
  26. package/lib/channels/direct/message-format.js.map +1 -1
  27. package/lib/channels/manager.d.ts.map +1 -1
  28. package/lib/channels/manager.js +4 -3
  29. package/lib/channels/manager.js.map +1 -1
  30. package/lib/channels/notification-delivery.d.ts.map +1 -1
  31. package/lib/channels/notification-delivery.js +10 -0
  32. package/lib/channels/notification-delivery.js.map +1 -1
  33. package/lib/channels/weixin/connect.d.ts.map +1 -1
  34. package/lib/client-api.d.ts +17 -2
  35. package/lib/client-api.d.ts.map +1 -1
  36. package/lib/client-api.js +8 -1
  37. package/lib/client-api.js.map +1 -1
  38. package/lib/client.d.ts.map +1 -1
  39. package/lib/client.js +2398 -1970
  40. package/lib/client.js.map +4 -4
  41. package/lib/client.pendant.js +4157 -0
  42. package/lib/collaboration/composition.js +1 -1
  43. package/lib/collaboration/composition.js.map +1 -1
  44. package/lib/concern-attention.d.ts +19 -0
  45. package/lib/concern-attention.d.ts.map +1 -0
  46. package/lib/concern-attention.js +36 -0
  47. package/lib/concern-attention.js.map +1 -0
  48. package/lib/concern-domain.d.ts +10 -1
  49. package/lib/concern-domain.d.ts.map +1 -1
  50. package/lib/concern-domain.js +4 -0
  51. package/lib/concern-domain.js.map +1 -1
  52. package/lib/concern-execution-notes.d.ts +18 -0
  53. package/lib/concern-execution-notes.d.ts.map +1 -0
  54. package/lib/concern-execution-notes.js +41 -0
  55. package/lib/concern-execution-notes.js.map +1 -0
  56. package/lib/concern-execution-receipt.d.ts +4 -0
  57. package/lib/concern-execution-receipt.d.ts.map +1 -0
  58. package/lib/concern-execution-receipt.js +28 -0
  59. package/lib/concern-execution-receipt.js.map +1 -0
  60. package/lib/concern-execution.d.ts +32 -0
  61. package/lib/concern-execution.d.ts.map +1 -0
  62. package/lib/concern-execution.js +112 -0
  63. package/lib/concern-execution.js.map +1 -0
  64. package/lib/concern-management-tool.d.ts.map +1 -1
  65. package/lib/concern-management-tool.js +21 -19
  66. package/lib/concern-management-tool.js.map +1 -1
  67. package/lib/concern-note-source.d.ts +7 -0
  68. package/lib/concern-note-source.d.ts.map +1 -0
  69. package/lib/concern-note-source.js +25 -0
  70. package/lib/concern-note-source.js.map +1 -0
  71. package/lib/concern-resources.d.ts +27 -0
  72. package/lib/concern-resources.d.ts.map +1 -0
  73. package/lib/concern-resources.js +36 -0
  74. package/lib/concern-resources.js.map +1 -0
  75. package/lib/concern-sources.d.ts +1 -1
  76. package/lib/concern-sources.d.ts.map +1 -1
  77. package/lib/concern-store.d.ts +10 -1
  78. package/lib/concern-store.d.ts.map +1 -1
  79. package/lib/concern-store.js +135 -36
  80. package/lib/concern-store.js.map +1 -1
  81. package/lib/concern-tool.d.ts +1 -0
  82. package/lib/concern-tool.d.ts.map +1 -1
  83. package/lib/concern-tool.js +8 -4
  84. package/lib/concern-tool.js.map +1 -1
  85. package/lib/decision/conversation.d.ts +11 -0
  86. package/lib/decision/conversation.d.ts.map +1 -0
  87. package/lib/decision/conversation.js +52 -0
  88. package/lib/decision/conversation.js.map +1 -0
  89. package/lib/decision/events.d.ts +16 -0
  90. package/lib/decision/events.d.ts.map +1 -0
  91. package/lib/decision/events.js +9 -0
  92. package/lib/decision/events.js.map +1 -0
  93. package/lib/domain.d.ts +2 -0
  94. package/lib/domain.d.ts.map +1 -1
  95. package/lib/domain.js.map +1 -1
  96. package/lib/heartbeat.d.ts +12 -10
  97. package/lib/heartbeat.d.ts.map +1 -1
  98. package/lib/heartbeat.js +55 -26
  99. package/lib/heartbeat.js.map +1 -1
  100. package/lib/index.d.ts.map +1 -1
  101. package/lib/index.js +4 -1
  102. package/lib/index.js.map +1 -1
  103. package/lib/memory-prompt-context.js +1 -0
  104. package/lib/memory-prompt-context.js.map +1 -1
  105. package/lib/memory-reflection.js +1 -1
  106. package/lib/observation-contract.d.ts +9 -0
  107. package/lib/observation-contract.d.ts.map +1 -0
  108. package/lib/observation-contract.js +19 -0
  109. package/lib/observation-contract.js.map +1 -0
  110. package/lib/observation-executor.d.ts +5 -0
  111. package/lib/observation-executor.d.ts.map +1 -1
  112. package/lib/observation-executor.js +23 -1
  113. package/lib/observation-executor.js.map +1 -1
  114. package/lib/observation-loop.d.ts +7 -0
  115. package/lib/observation-loop.d.ts.map +1 -1
  116. package/lib/observation-loop.js +65 -4
  117. package/lib/observation-loop.js.map +1 -1
  118. package/lib/observation-progress.d.ts +18 -0
  119. package/lib/observation-progress.d.ts.map +1 -0
  120. package/lib/observation-progress.js +57 -0
  121. package/lib/observation-progress.js.map +1 -0
  122. package/lib/observation-sources.d.ts +14 -0
  123. package/lib/observation-sources.d.ts.map +1 -0
  124. package/lib/observation-sources.js +12 -0
  125. package/lib/observation-sources.js.map +1 -0
  126. package/lib/pendant/badge-art.d.ts +9 -0
  127. package/lib/pendant/badge-art.d.ts.map +1 -0
  128. package/lib/pendant/badge-art.js +68 -0
  129. package/lib/pendant/badge-art.js.map +1 -0
  130. package/lib/pendant/card-image.d.ts.map +1 -1
  131. package/lib/pendant/card-image.js +11 -9
  132. package/lib/pendant/card-image.js.map +1 -1
  133. package/lib/pendant/renderer-loader.d.ts +9 -0
  134. package/lib/pendant/renderer-loader.d.ts.map +1 -0
  135. package/lib/pendant/renderer-loader.js +12 -0
  136. package/lib/pendant/renderer-loader.js.map +1 -0
  137. package/lib/pendant/renderer.d.ts +2 -6
  138. package/lib/pendant/renderer.d.ts.map +1 -1
  139. package/lib/pendant/renderer.js +1 -67
  140. package/lib/pendant/renderer.js.map +1 -1
  141. package/lib/pendant/static-card.d.ts +20 -0
  142. package/lib/pendant/static-card.d.ts.map +1 -0
  143. package/lib/pendant/static-card.js +55 -0
  144. package/lib/pendant/static-card.js.map +1 -0
  145. package/lib/pendant/widget.d.ts.map +1 -1
  146. package/lib/pendant/widget.js +9 -5
  147. package/lib/pendant/widget.js.map +1 -1
  148. package/lib/pendant-renderer.js +14 -14
  149. package/lib/requirements/service.d.ts.map +1 -1
  150. package/lib/requirements/service.js +2 -0
  151. package/lib/requirements/service.js.map +1 -1
  152. package/lib/requirements/worker.d.ts.map +1 -1
  153. package/lib/requirements/worker.js +1 -0
  154. package/lib/requirements/worker.js.map +1 -1
  155. package/lib/scheduler/continuations.d.ts.map +1 -1
  156. package/lib/scheduler/continuations.js +17 -3
  157. package/lib/scheduler/continuations.js.map +1 -1
  158. package/lib/skills/builtin.d.ts.map +1 -1
  159. package/lib/skills/builtin.js +2 -0
  160. package/lib/skills/builtin.js.map +1 -1
  161. package/lib/skills/drama-builtin.d.ts.map +1 -1
  162. package/lib/skills/drama-builtin.js +13 -1
  163. package/lib/skills/drama-builtin.js.map +1 -1
  164. package/lib/skills/story-builtins.d.ts +3 -0
  165. package/lib/skills/story-builtins.d.ts.map +1 -0
  166. package/lib/skills/story-builtins.js +72 -0
  167. package/lib/skills/story-builtins.js.map +1 -0
  168. package/lib/skills/viora-canvas-builtin.d.ts.map +1 -1
  169. package/lib/skills/viora-canvas-builtin.js +12 -1
  170. package/lib/skills/viora-canvas-builtin.js.map +1 -1
  171. package/lib/snapshot-cache.d.ts +7 -0
  172. package/lib/snapshot-cache.d.ts.map +1 -0
  173. package/lib/snapshot-cache.js +26 -0
  174. package/lib/snapshot-cache.js.map +1 -0
  175. package/lib/tasks/result.js +1 -1
  176. package/lib/tasks/result.js.map +1 -1
  177. package/lib/ui/memory/concern-board.d.ts +1 -1
  178. package/lib/ui/memory/concern-board.d.ts.map +1 -1
  179. package/lib/ui/memory/concern-board.js +14 -6
  180. package/lib/ui/memory/concern-board.js.map +1 -1
  181. package/lib/ui/memory/concern-deletion.d.ts.map +1 -1
  182. package/lib/ui/memory/concern-deletion.js +2 -1
  183. package/lib/ui/memory/concern-deletion.js.map +1 -1
  184. package/lib/ui/memory/concern-editor.d.ts.map +1 -1
  185. package/lib/ui/memory/concern-editor.js +5 -3
  186. package/lib/ui/memory/concern-editor.js.map +1 -1
  187. package/lib/ui/memory/concern-execution-fields.d.ts +7 -0
  188. package/lib/ui/memory/concern-execution-fields.d.ts.map +1 -0
  189. package/lib/ui/memory/concern-execution-fields.js +8 -0
  190. package/lib/ui/memory/concern-execution-fields.js.map +1 -0
  191. package/lib/ui/memory/concern-execution-status.d.ts +8 -0
  192. package/lib/ui/memory/concern-execution-status.d.ts.map +1 -0
  193. package/lib/ui/memory/concern-execution-status.js +31 -0
  194. package/lib/ui/memory/concern-execution-status.js.map +1 -0
  195. package/lib/ui/memory/concern-feedback.d.ts +15 -0
  196. package/lib/ui/memory/concern-feedback.d.ts.map +1 -0
  197. package/lib/ui/memory/concern-feedback.js +13 -0
  198. package/lib/ui/memory/concern-feedback.js.map +1 -0
  199. package/lib/ui/memory/concern-panel.d.ts.map +1 -1
  200. package/lib/ui/memory/concern-panel.js +11 -3
  201. package/lib/ui/memory/concern-panel.js.map +1 -1
  202. package/package.json +7 -1
  203. package/resources/skills/novel-game-creation/manifest.json +393 -0
  204. package/resources/skills/novel-game-creation/upstream/LICENSE +21 -0
  205. package/resources/skills/novel-game-creation/upstream/README.md +420 -0
  206. package/resources/skills/novel-game-creation/upstream/README_ZH.md +421 -0
  207. package/resources/skills/novel-game-creation/upstream/VERSION +1 -0
  208. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/BUILD_BRIEF.md +219 -0
  209. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/.vercelignore +3 -0
  210. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/RUN.md +138 -0
  211. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/bg/compound_new_guofeng.webp +0 -0
  212. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/fate_coda.webp +0 -0
  213. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/meng.webp +0 -0
  214. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/pan.webp +0 -0
  215. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/pinger.webp +0 -0
  216. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/xuee.webp +0 -0
  217. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/finale/yue.webp +0 -0
  218. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_accord_five.webp +0 -0
  219. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_afterglow_five.webp +0 -0
  220. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/inner_court_alliance.webp +0 -0
  221. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/public_day10.webp +0 -0
  222. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/public_day15.webp +0 -0
  223. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/public_day5.webp +0 -0
  224. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/group/title_new_guofeng.webp +0 -0
  225. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/joint/pan_pinger.webp +0 -0
  226. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/joint/pan_xuee.webp +0 -0
  227. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/joint/yue_meng.webp +0 -0
  228. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pan.webp +0 -0
  229. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/joint/yue_pinger.webp +0 -0
  230. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/meng/explicit.webp +0 -0
  231. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/meng/prelude.webp +0 -0
  232. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/milestone/meng.webp +0 -0
  233. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/milestone/pan.webp +0 -0
  234. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/milestone/pinger.webp +0 -0
  235. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/milestone/xuee.webp +0 -0
  236. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/milestone/yue.webp +0 -0
  237. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/pan/explicit.webp +0 -0
  238. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/pan/prelude.webp +0 -0
  239. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/pinger/explicit.webp +0 -0
  240. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/pinger/prelude.webp +0 -0
  241. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/xuee/explicit.webp +0 -0
  242. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/xuee/prelude.webp +0 -0
  243. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/yue/explicit.webp +0 -0
  244. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/cg/yue/prelude.webp +0 -0
  245. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/heroine/meng/night.webp +0 -0
  246. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/heroine/pan/night.webp +0 -0
  247. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/heroine/pinger/night.webp +0 -0
  248. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/heroine/xuee/night.webp +0 -0
  249. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/heroine/yue/night.webp +0 -0
  250. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/household/li_jiaoer.webp +0 -0
  251. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/assets/scene/gate_collector.webp +0 -0
  252. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/css/new-guofeng.css +1494 -0
  253. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/css/style.css +2508 -0
  254. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/index.html +18 -0
  255. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/assets.js +147 -0
  256. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/audio.js +332 -0
  257. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/data.js +7470 -0
  258. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/engine.js +13066 -0
  259. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/main.js +3051 -0
  260. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/js/text.js +32 -0
  261. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/package.json +11 -0
  262. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/app/vercel.json +23 -0
  263. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/build/art/generated-art.json +161 -0
  264. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/example.json +12 -0
  265. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/qa/README.md +26 -0
  266. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/qa/playable-model.json +99 -0
  267. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/qa/verification.json +41 -0
  268. package/resources/skills/novel-game-creation/upstream/examples/jin-ping-mei/qa/verify_playable_model.mjs +152 -0
  269. package/resources/skills/novel-game-creation/upstream/manifest.json +498 -0
  270. package/resources/skills/novel-game-creation/upstream/skills/game-art-direction/SKILL.md +47 -0
  271. package/resources/skills/novel-game-creation/upstream/skills/game-art-direction/agents/openai.yaml +4 -0
  272. package/resources/skills/novel-game-creation/upstream/skills/game-art-direction/references/art-direction-method.md +40 -0
  273. package/resources/skills/novel-game-creation/upstream/skills/game-build/SKILL.md +66 -0
  274. package/resources/skills/novel-game-creation/upstream/skills/game-build/agents/openai.yaml +4 -0
  275. package/resources/skills/novel-game-creation/upstream/skills/game-build/references/blender-asset-workflow.md +24 -0
  276. package/resources/skills/novel-game-creation/upstream/skills/game-build/references/build-brief-contract.md +72 -0
  277. package/resources/skills/novel-game-creation/upstream/skills/game-build/references/generative-media-pipeline.md +25 -0
  278. package/resources/skills/novel-game-creation/upstream/skills/game-build/references/playable-model-contract.md +46 -0
  279. package/resources/skills/novel-game-creation/upstream/skills/game-build/references/tts-production-contract.md +23 -0
  280. package/resources/skills/novel-game-creation/upstream/skills/game-concept/SKILL.md +54 -0
  281. package/resources/skills/novel-game-creation/upstream/skills/game-concept/agents/openai.yaml +4 -0
  282. package/resources/skills/novel-game-creation/upstream/skills/game-concept/references/concept-method.md +47 -0
  283. package/resources/skills/novel-game-creation/upstream/skills/game-qa/SKILL.md +39 -0
  284. package/resources/skills/novel-game-creation/upstream/skills/game-qa/agents/openai.yaml +4 -0
  285. package/resources/skills/novel-game-creation/upstream/skills/game-qa/references/qa-contract.md +66 -0
  286. package/resources/skills/novel-game-creation/upstream/skills/game-qa/references/test-design-method.md +49 -0
  287. package/resources/skills/novel-game-creation/upstream/skills/game-world-design/SKILL.md +50 -0
  288. package/resources/skills/novel-game-creation/upstream/skills/game-world-design/agents/openai.yaml +4 -0
  289. package/resources/skills/novel-game-creation/upstream/skills/game-world-design/references/narrative-design-method.md +35 -0
  290. package/resources/skills/novel-game-creation/upstream/skills/game-world-design/references/numeric-design-method.md +22 -0
  291. package/resources/skills/novel-game-creation/upstream/skills/game-world-design/references/world-design-method.md +57 -0
  292. package/resources/skills/novel-game-creation/upstream/skills/novel-game-analyze/SKILL.md +68 -0
  293. package/resources/skills/novel-game-creation/upstream/skills/novel-game-analyze/agents/openai.yaml +4 -0
  294. package/resources/skills/novel-game-creation/upstream/skills/novel-game-analyze/references/gameability-protocol.md +65 -0
  295. package/resources/skills/novel-game-creation/upstream/skills/novel-to-game/SKILL.md +54 -0
  296. package/resources/skills/novel-game-creation/upstream/skills/novel-to-game/agents/openai.yaml +4 -0
  297. package/resources/skills/novel-game-creation/upstream/skills/novel-to-game/references/intake-method.md +50 -0
  298. package/resources/skills/novel-game-creation/upstream/skills/novel-to-game/references/pipeline-contract.md +60 -0
  299. package/resources/skills/story-creation/manifest.json +1477 -0
  300. package/resources/skills/story-creation/upstream/LICENSE.upstream +21 -0
  301. package/resources/skills/story-creation/upstream/manifest.json +1861 -0
  302. package/resources/skills/story-creation/upstream/roles/chapter-extractor.md +254 -0
  303. package/resources/skills/story-creation/upstream/roles/character-designer.md +168 -0
  304. package/resources/skills/story-creation/upstream/roles/consistency-checker.md +192 -0
  305. package/resources/skills/story-creation/upstream/roles/narrative-writer.md +105 -0
  306. package/resources/skills/story-creation/upstream/roles/story-architect.md +139 -0
  307. package/resources/skills/story-creation/upstream/roles/story-explorer.md +262 -0
  308. package/resources/skills/story-creation/upstream/roles/story-researcher.md +303 -0
  309. package/resources/skills/story-creation/upstream/skills/browser-cdp/SKILL.md +175 -0
  310. package/resources/skills/story-creation/upstream/skills/story/SKILL.md +129 -0
  311. package/resources/skills/story-creation/upstream/skills/story/VERSION +1 -0
  312. package/resources/skills/story-creation/upstream/skills/story/references/author-memory-maintenance.md +75 -0
  313. package/resources/skills/story-creation/upstream/skills/story/references/author-memory.md +117 -0
  314. package/resources/skills/story-creation/upstream/skills/story/scripts/author_memory_commit.py +1635 -0
  315. package/resources/skills/story-creation/upstream/skills/story-cover/SKILL.md +336 -0
  316. package/resources/skills/story-creation/upstream/skills/story-cover/references/cover-styles.md +210 -0
  317. package/resources/skills/story-creation/upstream/skills/story-deslop/SKILL.md +274 -0
  318. package/resources/skills/story-creation/upstream/skills/story-deslop/references/agent-calls.md +5 -0
  319. package/resources/skills/story-creation/upstream/skills/story-deslop/references/anti-ai-writing.md +556 -0
  320. package/resources/skills/story-creation/upstream/skills/story-deslop/references/author-memory-maintenance.md +75 -0
  321. package/resources/skills/story-creation/upstream/skills/story-deslop/references/author-memory.md +117 -0
  322. package/resources/skills/story-creation/upstream/skills/story-deslop/references/banned-words.md +151 -0
  323. package/resources/skills/story-creation/upstream/skills/story-deslop/references/deslop-gates.md +171 -0
  324. package/resources/skills/story-creation/upstream/skills/story-deslop/references/style-resolution.md +27 -0
  325. package/resources/skills/story-creation/upstream/skills/story-deslop/scripts/author_memory_commit.py +1635 -0
  326. package/resources/skills/story-creation/upstream/skills/story-deslop/scripts/check-ai-patterns.js +1586 -0
  327. package/resources/skills/story-creation/upstream/skills/story-deslop/scripts/check-degeneration.js +349 -0
  328. package/resources/skills/story-creation/upstream/skills/story-deslop/scripts/normalize-punctuation.js +415 -0
  329. package/resources/skills/story-creation/upstream/skills/story-deslop/scripts/style-whitelist.js +39 -0
  330. package/resources/skills/story-creation/upstream/skills/story-import/SKILL.md +127 -0
  331. package/resources/skills/story-creation/upstream/skills/story-import/references/character-state-reverse.md +82 -0
  332. package/resources/skills/story-creation/upstream/skills/story-import/references/deep-analysis.md +47 -0
  333. package/resources/skills/story-creation/upstream/skills/story-import/references/format-and-structure.md +168 -0
  334. package/resources/skills/story-creation/upstream/skills/story-import/references/import-report.md +64 -0
  335. package/resources/skills/story-creation/upstream/skills/story-import/references/import-special-cases.md +34 -0
  336. package/resources/skills/story-creation/upstream/skills/story-import/references/import-tracking.md +95 -0
  337. package/resources/skills/story-creation/upstream/skills/story-import/references/length-routing.md +134 -0
  338. package/resources/skills/story-creation/upstream/skills/story-import/references/outline-reverse-rules.md +79 -0
  339. package/resources/skills/story-creation/upstream/skills/story-import/references/outline-reverse.md +24 -0
  340. package/resources/skills/story-creation/upstream/skills/story-import/references/state-tracking.md +43 -0
  341. package/resources/skills/story-creation/upstream/skills/story-import/references/structure-mapping-long.md +448 -0
  342. package/resources/skills/story-creation/upstream/skills/story-import/references/structure-mapping-short.md +187 -0
  343. package/resources/skills/story-creation/upstream/skills/story-import/references/tracking-initialization.md +31 -0
  344. package/resources/skills/story-creation/upstream/skills/story-import/references/tracking-transaction.md +146 -0
  345. package/resources/skills/story-creation/upstream/skills/story-import/scripts/build_outline_brief.py +332 -0
  346. package/resources/skills/story-creation/upstream/skills/story-import/scripts/check-outline-contract.js +520 -0
  347. package/resources/skills/story-creation/upstream/skills/story-import/scripts/tracking_commit.py +1691 -0
  348. package/resources/skills/story-creation/upstream/skills/story-import/scripts/wordcount_core.py +405 -0
  349. package/resources/skills/story-creation/upstream/skills/story-long-analyze/SKILL.md +140 -0
  350. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/author-facing.md +252 -0
  351. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/deconstruction-notes.md +270 -0
  352. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/final-checks.md +19 -0
  353. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/index-rebuild.md +11 -0
  354. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/inspiration-library.md +105 -0
  355. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/pipeline-ops.md +154 -0
  356. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/semantic-acceptance-fixtures.md +84 -0
  357. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/stage1-golden-chapters.md +128 -0
  358. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/stage2-extraction.md +229 -0
  359. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/stage3-plot-rhythm.md +480 -0
  360. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/stage4-characters-settings.md +195 -0
  361. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/stage5-report.md +33 -0
  362. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/style-profile-generator.md +76 -0
  363. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/style-profile-protocol.md +133 -0
  364. package/resources/skills/story-creation/upstream/skills/story-long-analyze/references/synthesis-inputs.md +39 -0
  365. package/resources/skills/story-creation/upstream/skills/story-long-analyze/scripts/build_chapter_index.py +466 -0
  366. package/resources/skills/story-creation/upstream/skills/story-long-analyze/scripts/inspect_existing_assets.py +702 -0
  367. package/resources/skills/story-creation/upstream/skills/story-long-analyze/scripts/inspiration_index.py +864 -0
  368. package/resources/skills/story-creation/upstream/skills/story-long-analyze/scripts/manage_analysis_run.py +1145 -0
  369. package/resources/skills/story-creation/upstream/skills/story-long-analyze/scripts/render_relation_chart.py +425 -0
  370. package/resources/skills/story-creation/upstream/skills/story-long-scan/SKILL.md +193 -0
  371. package/resources/skills/story-creation/upstream/skills/story-long-scan/references/genre-trends.md +197 -0
  372. package/resources/skills/story-creation/upstream/skills/story-long-scan/references/platform-ciweimao.md +19 -0
  373. package/resources/skills/story-creation/upstream/skills/story-long-scan/references/platform-fanqie.md +45 -0
  374. package/resources/skills/story-creation/upstream/skills/story-long-scan/references/platform-jjwxc.md +28 -0
  375. package/resources/skills/story-creation/upstream/skills/story-long-scan/references/platform-qidian.md +35 -0
  376. package/resources/skills/story-creation/upstream/skills/story-long-scan/references/platform-qimao.md +27 -0
  377. package/resources/skills/story-creation/upstream/skills/story-long-scan/references/publishing-guide.md +114 -0
  378. package/resources/skills/story-creation/upstream/skills/story-long-scan/references/reader-profiling.md +105 -0
  379. package/resources/skills/story-creation/upstream/skills/story-long-scan/references/topic-decision.md +82 -0
  380. package/resources/skills/story-creation/upstream/skills/story-long-scan/scripts/aggregate-rank.js +682 -0
  381. package/resources/skills/story-creation/upstream/skills/story-long-scan/scripts/cdp-utils.js +242 -0
  382. package/resources/skills/story-creation/upstream/skills/story-long-scan/scripts/ciweimao-rank-scraper.js +240 -0
  383. package/resources/skills/story-creation/upstream/skills/story-long-scan/scripts/fanqie-rank-scraper.js +658 -0
  384. package/resources/skills/story-creation/upstream/skills/story-long-scan/scripts/jjwxc-rank-scraper.js +400 -0
  385. package/resources/skills/story-creation/upstream/skills/story-long-scan/scripts/qidian-rank-scraper.js +592 -0
  386. package/resources/skills/story-creation/upstream/skills/story-long-scan/scripts/qimao-rank-scraper.js +435 -0
  387. package/resources/skills/story-creation/upstream/skills/story-long-write/SKILL.md +136 -0
  388. package/resources/skills/story-creation/upstream/skills/story-long-write/references/agent-calls.md +47 -0
  389. package/resources/skills/story-creation/upstream/skills/story-long-write/references/anti-ai-writing.md +556 -0
  390. package/resources/skills/story-creation/upstream/skills/story-long-write/references/artifact-protocols.md +183 -0
  391. package/resources/skills/story-creation/upstream/skills/story-long-write/references/author-memory-maintenance.md +75 -0
  392. package/resources/skills/story-creation/upstream/skills/story-long-write/references/author-memory.md +117 -0
  393. package/resources/skills/story-creation/upstream/skills/story-long-write/references/banned-words.md +151 -0
  394. package/resources/skills/story-creation/upstream/skills/story-long-write/references/benchmark-recall.md +16 -0
  395. package/resources/skills/story-creation/upstream/skills/story-long-write/references/character-basics.md +445 -0
  396. package/resources/skills/story-creation/upstream/skills/story-long-write/references/character-design-methods.md +428 -0
  397. package/resources/skills/story-creation/upstream/skills/story-long-write/references/character-relations.md +433 -0
  398. package/resources/skills/story-creation/upstream/skills/story-long-write/references/commercial-core-methods.md +502 -0
  399. package/resources/skills/story-creation/upstream/skills/story-long-write/references/cross-book-recall.md +88 -0
  400. package/resources/skills/story-creation/upstream/skills/story-long-write/references/deslop-gates.md +171 -0
  401. package/resources/skills/story-creation/upstream/skills/story-long-write/references/dialogue-mastery.md +288 -0
  402. package/resources/skills/story-creation/upstream/skills/story-long-write/references/emotion-on-page.md +116 -0
  403. package/resources/skills/story-creation/upstream/skills/story-long-write/references/emotional-arc-design.md +313 -0
  404. package/resources/skills/story-creation/upstream/skills/story-long-write/references/emotional-methods.md +209 -0
  405. package/resources/skills/story-creation/upstream/skills/story-long-write/references/female-audience-writing.md +188 -0
  406. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//344/270/234/346/226/271/344/273/231/344/276/240.md +51 -0
  407. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//344/274/240/347/273/237/347/216/204/345/271/273.md +51 -0
  408. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//345/216/206/345/217/262/345/217/244/344/273/243.md +51 -0
  409. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//345/216/206/345/217/262/350/204/221/346/264/236.md +51 -0
  410. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//345/217/214/347/224/267/344/270/273.md +53 -0
  411. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//345/217/244/350/250/200/350/204/221/346/264/236.md +51 -0
  412. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//345/217/244/351/243/216/344/270/226/346/203/205.md +51 -0
  413. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//345/245/263/351/242/221/346/202/254/347/226/221.md +51 -0
  414. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//345/245/263/351/242/221/347/247/215/347/224/260.md +51 -0
  415. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//345/256/253/346/226/227/345/256/205/346/226/227.md +51 -0
  416. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//345/271/264/344/273/243.md +53 -0
  417. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//345/277/253/347/251/277.md +51 -0
  418. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//346/202/254/347/226/221/347/201/265/345/274/202.md +51 -0
  419. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//346/202/254/347/226/221/350/204/221/346/264/236.md +51 -0
  420. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//346/210/230/347/245/236/350/265/230/345/251/277.md +51 -0
  421. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//346/212/227/346/210/230/350/260/215/346/210/230.md +51 -0
  422. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//346/230/237/345/205/211/347/222/200/347/222/250.md +51 -0
  423. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//346/260/221/345/233/275/350/250/200/346/203/205.md +51 -0
  424. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//346/270/270/346/210/217/344/275/223/350/202/262.md +51 -0
  425. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//347/216/204/345/271/273/350/204/221/346/264/236.md +53 -0
  426. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//347/216/204/345/271/273/350/250/200/346/203/205.md +51 -0
  427. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//347/216/260/350/250/200/350/204/221/346/264/236.md +51 -0
  428. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//347/247/221/345/271/273/346/234/253/344/270/226.md +51 -0
  429. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//350/201/214/345/234/272/345/251/232/346/201/213.md +51 -0
  430. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//350/245/277/346/226/271/345/245/207/345/271/273.md +51 -0
  431. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//350/261/252/351/227/250/346/200/273/350/243/201.md +53 -0
  432. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//351/203/275/345/270/202/344/277/256/347/234/237.md +51 -0
  433. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//351/203/275/345/270/202/346/227/245/345/270/270.md +53 -0
  434. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//351/203/275/345/270/202/347/247/215/347/224/260.md +51 -0
  435. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//351/203/275/345/270/202/350/204/221/346/264/236.md +53 -0
  436. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//351/203/275/345/270/202/351/253/230/346/255/246.md +53 -0
  437. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards//351/235/222/346/230/245/347/224/234/345/256/240.md +51 -0
  438. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-prose-cards.md +91 -0
  439. package/resources/skills/story-creation/upstream/skills/story-long-write/references/genre-readers.md +235 -0
  440. package/resources/skills/story-creation/upstream/skills/story-long-write/references/long-chapter-hooks.md +225 -0
  441. package/resources/skills/story-creation/upstream/skills/story-long-write/references/long-chapter-quality.md +177 -0
  442. package/resources/skills/story-creation/upstream/skills/story-long-write/references/long-format.md +36 -0
  443. package/resources/skills/story-creation/upstream/skills/story-long-write/references/long-genre-catalog.md +394 -0
  444. package/resources/skills/story-creation/upstream/skills/story-long-write/references/long-genre-mechanics.md +415 -0
  445. package/resources/skills/story-creation/upstream/skills/story-long-write/references/long-reversal.md +335 -0
  446. package/resources/skills/story-creation/upstream/skills/story-long-write/references/long-suspense.md +352 -0
  447. package/resources/skills/story-creation/upstream/skills/story-long-write/references/opening-design.md +349 -0
  448. package/resources/skills/story-creation/upstream/skills/story-long-write/references/outline-conflict.md +457 -0
  449. package/resources/skills/story-creation/upstream/skills/story-long-write/references/outline-methods.md +287 -0
  450. package/resources/skills/story-creation/upstream/skills/story-long-write/references/outline-rhythm.md +389 -0
  451. package/resources/skills/story-creation/upstream/skills/story-long-write/references/outline-structure-theory.md +273 -0
  452. package/resources/skills/story-creation/upstream/skills/story-long-write/references/plot-core-methods.md +522 -0
  453. package/resources/skills/story-creation/upstream/skills/story-long-write/references/plot-emotion-system.md +354 -0
  454. package/resources/skills/story-creation/upstream/skills/story-long-write/references/plot-frameworks.md +711 -0
  455. package/resources/skills/story-creation/upstream/skills/story-long-write/references/plot-special-topics.md +622 -0
  456. package/resources/skills/story-creation/upstream/skills/story-long-write/references/project-files.md +114 -0
  457. package/resources/skills/story-creation/upstream/skills/story-long-write/references/reader-contract-and-progression.md +117 -0
  458. package/resources/skills/story-creation/upstream/skills/story-long-write/references/reference-index.md +86 -0
  459. package/resources/skills/story-creation/upstream/skills/story-long-write/references/stop-and-fix.md +9 -0
  460. package/resources/skills/story-creation/upstream/skills/story-long-write/references/style-combat-face.md +352 -0
  461. package/resources/skills/story-creation/upstream/skills/story-long-write/references/style-craft.md +327 -0
  462. package/resources/skills/story-creation/upstream/skills/story-long-write/references/style-genre-modules.md +510 -0
  463. package/resources/skills/story-creation/upstream/skills/story-long-write/references/style-resolution.md +27 -0
  464. package/resources/skills/story-creation/upstream/skills/story-long-write/references/tracking-initialization.md +31 -0
  465. package/resources/skills/story-creation/upstream/skills/story-long-write/references/tracking-transaction.md +146 -0
  466. package/resources/skills/story-creation/upstream/skills/story-long-write/references/workflow-chapter.md +104 -0
  467. package/resources/skills/story-creation/upstream/skills/story-long-write/references/workflow-daily.md +75 -0
  468. package/resources/skills/story-creation/upstream/skills/story-long-write/references/workflow-outline.md +157 -0
  469. package/resources/skills/story-creation/upstream/skills/story-long-write/references/workflow-revision.md +89 -0
  470. package/resources/skills/story-creation/upstream/skills/story-long-write/references/workflow-setup.md +170 -0
  471. package/resources/skills/story-creation/upstream/skills/story-long-write/references/workflow-volume.md +142 -0
  472. package/resources/skills/story-creation/upstream/skills/story-long-write/references/writer-prompt-fallback.md +24 -0
  473. package/resources/skills/story-creation/upstream/skills/story-long-write/references/writing-craft.md +442 -0
  474. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/author_memory_commit.py +1635 -0
  475. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/build_architect_brief.py +260 -0
  476. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/build_writer_prompt.py +611 -0
  477. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/check-ai-patterns.js +1586 -0
  478. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/check-degeneration.js +349 -0
  479. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/check-outline-contract.js +520 -0
  480. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/check-outline-copy.js +313 -0
  481. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/normalize-punctuation.js +415 -0
  482. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/outline_view.py +286 -0
  483. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/storyctl.py +561 -0
  484. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/style-whitelist.js +39 -0
  485. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/tracking_commit.py +1691 -0
  486. package/resources/skills/story-creation/upstream/skills/story-long-write/scripts/wordcount_core.py +405 -0
  487. package/resources/skills/story-creation/upstream/skills/story-review/SKILL.md +231 -0
  488. package/resources/skills/story-creation/upstream/skills/story-review/references/agent-prompts.md +184 -0
  489. package/resources/skills/story-creation/upstream/skills/story-review/references/anti-ai-writing.md +556 -0
  490. package/resources/skills/story-creation/upstream/skills/story-review/references/author-memory-maintenance.md +75 -0
  491. package/resources/skills/story-creation/upstream/skills/story-review/references/author-memory.md +117 -0
  492. package/resources/skills/story-creation/upstream/skills/story-review/references/banned-words.md +151 -0
  493. package/resources/skills/story-creation/upstream/skills/story-review/references/batch-review.md +19 -0
  494. package/resources/skills/story-creation/upstream/skills/story-review/references/character-relations.md +433 -0
  495. package/resources/skills/story-creation/upstream/skills/story-review/references/dialogue-mastery.md +288 -0
  496. package/resources/skills/story-creation/upstream/skills/story-review/references/plot-core-methods.md +522 -0
  497. package/resources/skills/story-creation/upstream/skills/story-review/references/quality-rubric.md +55 -0
  498. package/resources/skills/story-creation/upstream/skills/story-review/references/review-quality.md +246 -0
  499. package/resources/skills/story-creation/upstream/skills/story-review/references/review-tracking.md +10 -0
  500. package/resources/skills/story-creation/upstream/skills/story-review/references/rubrics/fanqie.md +18 -0
  501. package/resources/skills/story-creation/upstream/skills/story-review/references/rubrics/qidian.md +19 -0
  502. package/resources/skills/story-creation/upstream/skills/story-review/references/rubrics/zhihu.md +20 -0
  503. package/resources/skills/story-creation/upstream/skills/story-review/references/solo.md +52 -0
  504. package/resources/skills/story-creation/upstream/skills/story-review/references/style-resolution.md +27 -0
  505. package/resources/skills/story-creation/upstream/skills/story-review/references/tracking-initialization.md +31 -0
  506. package/resources/skills/story-creation/upstream/skills/story-review/references/tracking-transaction.md +146 -0
  507. package/resources/skills/story-creation/upstream/skills/story-review/scripts/author_memory_commit.py +1635 -0
  508. package/resources/skills/story-creation/upstream/skills/story-review/scripts/check-ai-patterns.js +1586 -0
  509. package/resources/skills/story-creation/upstream/skills/story-review/scripts/check-degeneration.js +349 -0
  510. package/resources/skills/story-creation/upstream/skills/story-review/scripts/normalize-punctuation.js +415 -0
  511. package/resources/skills/story-creation/upstream/skills/story-review/scripts/style-whitelist.js +39 -0
  512. package/resources/skills/story-creation/upstream/skills/story-review/scripts/tracking_commit.py +1691 -0
  513. package/resources/skills/story-creation/upstream/skills/story-review/scripts/wordcount_core.py +405 -0
  514. package/resources/skills/story-creation/upstream/skills/story-setup/SKILL.md +165 -0
  515. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/agent-quality.md +72 -0
  516. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/agent-reference-profiles.md +55 -0
  517. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/anti-ai-writing.md +556 -0
  518. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/banned-words.md +151 -0
  519. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/benchmark-style-load.md +107 -0
  520. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/character-basics.md +445 -0
  521. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/character-design-methods.md +428 -0
  522. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/character-relations.md +433 -0
  523. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/deslop-gates.md +171 -0
  524. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/dialogue-mastery.md +288 -0
  525. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/emotion-on-page.md +116 -0
  526. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/emotional-arc-design.md +313 -0
  527. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/format-and-structure.md +168 -0
  528. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//344/270/234/346/226/271/344/273/231/344/276/240.md +51 -0
  529. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//344/274/240/347/273/237/347/216/204/345/271/273.md +51 -0
  530. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//345/216/206/345/217/262/345/217/244/344/273/243.md +51 -0
  531. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//345/216/206/345/217/262/350/204/221/346/264/236.md +51 -0
  532. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//345/217/214/347/224/267/344/270/273.md +53 -0
  533. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//345/217/244/350/250/200/350/204/221/346/264/236.md +51 -0
  534. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//345/217/244/351/243/216/344/270/226/346/203/205.md +51 -0
  535. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//345/245/263/351/242/221/346/202/254/347/226/221.md +51 -0
  536. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//345/245/263/351/242/221/347/247/215/347/224/260.md +51 -0
  537. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//345/256/253/346/226/227/345/256/205/346/226/227.md +51 -0
  538. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//345/271/264/344/273/243.md +53 -0
  539. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//345/277/253/347/251/277.md +51 -0
  540. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//346/202/254/347/226/221/347/201/265/345/274/202.md +51 -0
  541. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//346/202/254/347/226/221/350/204/221/346/264/236.md +51 -0
  542. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//346/210/230/347/245/236/350/265/230/345/251/277.md +51 -0
  543. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//346/212/227/346/210/230/350/260/215/346/210/230.md +51 -0
  544. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//346/230/237/345/205/211/347/222/200/347/222/250.md +51 -0
  545. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//346/260/221/345/233/275/350/250/200/346/203/205.md +51 -0
  546. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//346/270/270/346/210/217/344/275/223/350/202/262.md +51 -0
  547. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//347/216/204/345/271/273/350/204/221/346/264/236.md +53 -0
  548. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//347/216/204/345/271/273/350/250/200/346/203/205.md +51 -0
  549. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//347/216/260/350/250/200/350/204/221/346/264/236.md +51 -0
  550. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//347/247/221/345/271/273/346/234/253/344/270/226.md +51 -0
  551. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//350/201/214/345/234/272/345/251/232/346/201/213.md +51 -0
  552. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//350/245/277/346/226/271/345/245/207/345/271/273.md +51 -0
  553. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//350/261/252/351/227/250/346/200/273/350/243/201.md +53 -0
  554. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//351/203/275/345/270/202/344/277/256/347/234/237.md +51 -0
  555. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//351/203/275/345/270/202/346/227/245/345/270/270.md +53 -0
  556. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//351/203/275/345/270/202/347/247/215/347/224/260.md +51 -0
  557. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//351/203/275/345/270/202/350/204/221/346/264/236.md +53 -0
  558. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//351/203/275/345/270/202/351/253/230/346/255/246.md +53 -0
  559. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards//351/235/222/346/230/245/347/224/234/345/256/240.md +51 -0
  560. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-prose-cards.md +91 -0
  561. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/genre-readers.md +235 -0
  562. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/long-chapter-hooks.md +225 -0
  563. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/long-emotional-methods.md +209 -0
  564. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/long-genre-catalog.md +394 -0
  565. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/long-genre-mechanics.md +415 -0
  566. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/long-quality.md +42 -0
  567. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/long-reversal.md +335 -0
  568. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/long-suspense.md +352 -0
  569. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/opening-design.md +349 -0
  570. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/outline-conflict.md +457 -0
  571. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/outline-methods.md +287 -0
  572. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/outline-rhythm.md +389 -0
  573. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/plot-core-methods.md +522 -0
  574. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/short-chapter-hooks.md +225 -0
  575. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/short-emotional-methods.md +179 -0
  576. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/short-genre-formulas.md +417 -0
  577. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/short-paragraph-hooks.md +146 -0
  578. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/short-quality.md +39 -0
  579. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/short-reversal.md +355 -0
  580. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/short-suspense.md +352 -0
  581. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/style-combat-face.md +352 -0
  582. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/style-genre-modules.md +510 -0
  583. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/style-resolution.md +27 -0
  584. package/resources/skills/story-creation/upstream/skills/story-setup/references/agent-references/writing-craft.md +442 -0
  585. package/resources/skills/story-creation/upstream/skills/story-setup/references/diagnostics.md +38 -0
  586. package/resources/skills/story-creation/upstream/skills/story-short-analyze/SKILL.md +224 -0
  587. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-character-basics.md +453 -0
  588. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-character-design.md +438 -0
  589. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-character-relations.md +395 -0
  590. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-method.md +42 -0
  591. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-paragraph-hooks.md +146 -0
  592. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-reader-profile.md +245 -0
  593. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-report-style.md +45 -0
  594. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-short-genres.md +65 -0
  595. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-short-hooks.md +77 -0
  596. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-short-mechanics.md +82 -0
  597. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-short-patterns.md +50 -0
  598. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-short-suspense.md +98 -0
  599. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/analysis-writing-techniques.md +477 -0
  600. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/deconstruction-examples.md +79 -0
  601. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/optional-modules.md +97 -0
  602. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/output-contract.md +127 -0
  603. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/quality-checklist.md +80 -0
  604. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/real-market-data.md +90 -0
  605. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/source-story-quality.md +221 -0
  606. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/stage2-3-structure-emotion.md +308 -0
  607. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/stage4-6-reversal-summary.md +611 -0
  608. package/resources/skills/story-creation/upstream/skills/story-short-analyze/references/zhihu-style.md +54 -0
  609. package/resources/skills/story-creation/upstream/skills/story-short-scan/SKILL.md +182 -0
  610. package/resources/skills/story-creation/upstream/skills/story-short-scan/references/manual-rank-input.md +36 -0
  611. package/resources/skills/story-creation/upstream/skills/story-short-scan/references/platform-dianzhong.md +24 -0
  612. package/resources/skills/story-creation/upstream/skills/story-short-scan/references/platform-heiyan.md +21 -0
  613. package/resources/skills/story-creation/upstream/skills/story-short-scan/references/real-market-data.md +90 -0
  614. package/resources/skills/story-creation/upstream/skills/story-short-scan/references/topic-match.md +38 -0
  615. package/resources/skills/story-creation/upstream/skills/story-short-scan/scripts/aggregate-rank.js +682 -0
  616. package/resources/skills/story-creation/upstream/skills/story-short-scan/scripts/cdp-utils.js +242 -0
  617. package/resources/skills/story-creation/upstream/skills/story-short-scan/scripts/dz-browse-scraper.js +232 -0
  618. package/resources/skills/story-creation/upstream/skills/story-short-scan/scripts/heiyan-booklist-scraper.js +345 -0
  619. package/resources/skills/story-creation/upstream/skills/story-short-write/SKILL.md +136 -0
  620. package/resources/skills/story-creation/upstream/skills/story-short-write/references/agent-calls.md +13 -0
  621. package/resources/skills/story-creation/upstream/skills/story-short-write/references/author-memory-maintenance.md +75 -0
  622. package/resources/skills/story-creation/upstream/skills/story-short-write/references/author-memory.md +117 -0
  623. package/resources/skills/story-creation/upstream/skills/story-short-write/references/banned-words.md +151 -0
  624. package/resources/skills/story-creation/upstream/skills/story-short-write/references/benchmark-recall.md +40 -0
  625. package/resources/skills/story-creation/upstream/skills/story-short-write/references/cross-book-recall.md +62 -0
  626. package/resources/skills/story-creation/upstream/skills/story-short-write/references/dialogue-mastery.md +288 -0
  627. package/resources/skills/story-creation/upstream/skills/story-short-write/references/emotional-methods.md +179 -0
  628. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-styles//344/270/226/346/203/205/346/211/223/350/204/270.md +159 -0
  629. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-styles//345/217/214/347/224/267/344/270/273.md +156 -0
  630. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-styles//345/244/215/344/273/207/346/211/223/350/204/270.md +163 -0
  631. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-styles//345/256/205/346/226/227/345/256/253/346/226/227.md +167 -0
  632. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-styles//346/200/273/350/243/201/350/261/252/351/227/250.md +178 -0
  633. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-styles//346/202/254/347/226/221.md +166 -0
  634. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-styles//346/260/221/344/277/227/346/200/252/350/260/210.md +159 -0
  635. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-styles//346/262/231/351/233/225/350/204/221/346/264/236.md +151 -0
  636. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-styles//347/224/234/345/256/240.md +153 -0
  637. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-styles//350/277/275/345/246/273/347/201/253/350/221/254/345/234/272.md +210 -0
  638. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-writing-formulas.md +417 -0
  639. package/resources/skills/story-creation/upstream/skills/story-short-write/references/genre-writing-techniques.md +490 -0
  640. package/resources/skills/story-creation/upstream/skills/story-short-write/references/hooks-chapter.md +225 -0
  641. package/resources/skills/story-creation/upstream/skills/story-short-write/references/hooks-paragraph.md +146 -0
  642. package/resources/skills/story-creation/upstream/skills/story-short-write/references/output-contract.md +127 -0
  643. package/resources/skills/story-creation/upstream/skills/story-short-write/references/reference-index.md +23 -0
  644. package/resources/skills/story-creation/upstream/skills/story-short-write/references/scene-craft.md +40 -0
  645. package/resources/skills/story-creation/upstream/skills/story-short-write/references/short-craft.md +147 -0
  646. package/resources/skills/story-creation/upstream/skills/story-short-write/references/short-deslop.md +133 -0
  647. package/resources/skills/story-creation/upstream/skills/story-short-write/references/short-format.md +199 -0
  648. package/resources/skills/story-creation/upstream/skills/story-short-write/references/short-prose-quality.md +224 -0
  649. package/resources/skills/story-creation/upstream/skills/story-short-write/references/short-reversal.md +355 -0
  650. package/resources/skills/story-creation/upstream/skills/story-short-write/references/short-suspense.md +352 -0
  651. package/resources/skills/story-creation/upstream/skills/story-short-write/references/style-resolution.md +27 -0
  652. package/resources/skills/story-creation/upstream/skills/story-short-write/references/submission-craft.md +35 -0
  653. package/resources/skills/story-creation/upstream/skills/story-short-write/references/villain-and-reveal.md +153 -0
  654. package/resources/skills/story-creation/upstream/skills/story-short-write/references/workflow-design.md +85 -0
  655. package/resources/skills/story-creation/upstream/skills/story-short-write/references/workflow-draft.md +116 -0
  656. package/resources/skills/story-creation/upstream/skills/story-short-write/references/workflow-revision.md +99 -0
  657. package/resources/skills/story-creation/upstream/skills/story-short-write/references/writing-workflow.md +50 -0
  658. package/resources/skills/story-creation/upstream/skills/story-short-write/scripts/author_memory_commit.py +1635 -0
  659. package/resources/skills/story-creation/upstream/skills/story-short-write/scripts/check-ai-patterns.js +1586 -0
  660. package/resources/skills/story-creation/upstream/skills/story-short-write/scripts/check-degeneration.js +349 -0
  661. package/resources/skills/story-creation/upstream/skills/story-short-write/scripts/check-delivery-contract.js +241 -0
  662. package/resources/skills/story-creation/upstream/skills/story-short-write/scripts/check-outline-copy.js +313 -0
  663. package/resources/skills/story-creation/upstream/skills/story-short-write/scripts/check-phase2-contract.js +363 -0
  664. package/resources/skills/story-creation/upstream/skills/story-short-write/scripts/normalize-punctuation.js +415 -0
  665. package/resources/skills/story-creation/upstream/skills/story-short-write/scripts/style-whitelist.js +39 -0
  666. package/resources/skills/video-recap-production/manifest.json +673 -0
  667. package/resources/skills/video-recap-production/upstream/CHANGELOG.md +371 -0
  668. package/resources/skills/video-recap-production/upstream/LICENSE +21 -0
  669. package/resources/skills/video-recap-production/upstream/README.en.md +468 -0
  670. package/resources/skills/video-recap-production/upstream/README.md +459 -0
  671. package/resources/skills/video-recap-production/upstream/manifest.json +839 -0
  672. package/resources/skills/video-recap-production/upstream/skills/video-assemble/SKILL.md +112 -0
  673. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/audio-modes.md +63 -0
  674. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/explicit-audio-mix.md +75 -0
  675. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/foreground-compose.md +118 -0
  676. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/LICENSE.duo-video +21 -0
  677. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/SOURCE.md +15 -0
  678. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/empty_draft_meta_info.json +69 -0
  679. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/empty_jy_combination_segment.json +84 -0
  680. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/empty_jy_combination_video_material.json +98 -0
  681. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/empty_jy_draft.json +204 -0
  682. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/empty_jy_material_video.json +83 -0
  683. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/empty_jy_meta_material_value.json +23 -0
  684. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/empty_jy_project_info.json +133 -0
  685. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/empty_jy_segment.json +70 -0
  686. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/empty_jy_text_styles.json +30 -0
  687. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/empty_yj_material_audio.json +41 -0
  688. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/jianying/empty_yj_material_text.json +109 -0
  689. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/narration-adoption.md +103 -0
  690. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/packaging.md +45 -0
  691. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/pair-media.md +74 -0
  692. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/source-score.md +147 -0
  693. package/resources/skills/video-recap-production/upstream/skills/video-assemble/references/subtitle-track.md +202 -0
  694. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/adoption/__init__.py +1 -0
  695. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/adoption/audio_mix_binding.py +279 -0
  696. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/adoption/frozen_audio.py +187 -0
  697. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/adoption/narration_binding.py +323 -0
  698. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/adoption/strict_inputs.py +109 -0
  699. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/adoption/strict_publish.py +130 -0
  700. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/artifacts.py +47 -0
  701. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/assemble.py +591 -0
  702. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/assemble_constants.py +51 -0
  703. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/assembly_contract.py +318 -0
  704. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/assembly_settings.py +138 -0
  705. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/audio_automation.py +195 -0
  706. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/audio_mix.py +465 -0
  707. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/compose_foreground.py +293 -0
  708. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/export_jianying.py +254 -0
  709. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/jianying/__init__.py +1 -0
  710. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/jianying/builders.py +718 -0
  711. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/jianying/model.py +105 -0
  712. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/jianying/optional.py +23 -0
  713. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/jianying/schema.py +53 -0
  714. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/jianying/templates.py +37 -0
  715. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/jianying/timeline_contract.py +223 -0
  716. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/jianying/tracks.py +72 -0
  717. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/jianying/writer.py +383 -0
  718. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/lib.py +218 -0
  719. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/media.py +190 -0
  720. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/narration_audio.py +413 -0
  721. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/packaging.py +122 -0
  722. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/pair_media.py +216 -0
  723. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/render_preflight.py +33 -0
  724. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/source_score.py +469 -0
  725. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/source_subtitles.py +423 -0
  726. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/subtitles/__init__.py +1 -0
  727. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/subtitles/core.py +330 -0
  728. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/subtitles/render.py +82 -0
  729. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/subtitles/track.py +324 -0
  730. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/subtitles/track_binding.py +200 -0
  731. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/timeline.py +217 -0
  732. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/timeline_emit.py +167 -0
  733. package/resources/skills/video-recap-production/upstream/skills/video-assemble/scripts/visual_render.py +448 -0
  734. package/resources/skills/video-recap-production/upstream/skills/video-cut/SKILL.md +108 -0
  735. package/resources/skills/video-recap-production/upstream/skills/video-cut/references/shot-review.md +50 -0
  736. package/resources/skills/video-recap-production/upstream/skills/video-cut/scripts/cut.py +41 -0
  737. package/resources/skills/video-recap-production/upstream/skills/video-cut/scripts/cut_cli.py +308 -0
  738. package/resources/skills/video-recap-production/upstream/skills/video-cut/scripts/cut_contract.py +424 -0
  739. package/resources/skills/video-recap-production/upstream/skills/video-cut/scripts/cut_render.py +304 -0
  740. package/resources/skills/video-recap-production/upstream/skills/video-cut/scripts/lib.py +114 -0
  741. package/resources/skills/video-recap-production/upstream/skills/video-cut/scripts/media_geometry.py +238 -0
  742. package/resources/skills/video-recap-production/upstream/skills/video-cut/scripts/narration_mapping.py +71 -0
  743. package/resources/skills/video-recap-production/upstream/skills/video-cut/scripts/narrative_selection.py +244 -0
  744. package/resources/skills/video-recap-production/upstream/skills/video-cut/scripts/sentence_boundaries.py +585 -0
  745. package/resources/skills/video-recap-production/upstream/skills/video-cut/scripts/shot_review.py +357 -0
  746. package/resources/skills/video-recap-production/upstream/skills/video-recap/SKILL.md +260 -0
  747. package/resources/skills/video-recap-production/upstream/skills/video-recap/references/audio-routing.md +126 -0
  748. package/resources/skills/video-recap-production/upstream/skills/video-recap/references/config-playbook.md +65 -0
  749. package/resources/skills/video-recap-production/upstream/skills/video-recap/references/data-schema.md +545 -0
  750. package/resources/skills/video-recap-production/upstream/skills/video-recap/references/resource-library.md +151 -0
  751. package/resources/skills/video-recap-production/upstream/skills/video-recap/references/shift-left-qc-schema.md +23 -0
  752. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/doctor.py +556 -0
  753. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/final_qc.py +527 -0
  754. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/lib.py +220 -0
  755. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/library.py +509 -0
  756. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/materials.py +406 -0
  757. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/mimo_qc.py +33 -0
  758. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/project_binding.py +286 -0
  759. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/qc/__init__.py +1 -0
  760. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/qc/mimo_client.py +13 -0
  761. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/qc/mimo_contract.py +9 -0
  762. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/qc/mimo_evidence.py +287 -0
  763. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/qc/mimo_observations.py +175 -0
  764. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/qc/mimo_payload.py +206 -0
  765. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/qc/mimo_report.py +318 -0
  766. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/qc/mimo_runner.py +130 -0
  767. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/qc_contract.py +320 -0
  768. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/recap.py +9 -0
  769. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/recap_cli.py +209 -0
  770. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/recap_inspect.py +452 -0
  771. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/recap_review.py +70 -0
  772. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/recap_runner.py +696 -0
  773. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/recap_runtime.py +260 -0
  774. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/recap_source.py +278 -0
  775. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/recap_stage_qc.py +160 -0
  776. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/recap_timeline.py +621 -0
  777. package/resources/skills/video-recap-production/upstream/skills/video-recap/scripts/resource_lock.py +162 -0
  778. package/resources/skills/video-recap-production/upstream/skills/video-script/SKILL.md +251 -0
  779. package/resources/skills/video-recap-production/upstream/skills/video-script/references/creative-editing-playbook.md +229 -0
  780. package/resources/skills/video-recap-production/upstream/skills/video-script/references/promotional-copy.md +68 -0
  781. package/resources/skills/video-recap-production/upstream/skills/video-script/references/research-guide.md +75 -0
  782. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/agent_text.py +301 -0
  783. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/deslop_qc.py +211 -0
  784. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/evidence_bundle.py +440 -0
  785. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/lib.py +254 -0
  786. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/narration_lint.py +707 -0
  787. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/review.py +40 -0
  788. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/review_grounding.py +238 -0
  789. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/review_response.py +669 -0
  790. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/review_runner.py +186 -0
  791. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/speech_ownership.py +143 -0
  792. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/timeline_fusion.py +108 -0
  793. package/resources/skills/video-recap-production/upstream/skills/video-script/scripts/validate.py +175 -0
  794. package/resources/skills/video-recap-production/upstream/skills/video-understanding/SKILL.md +77 -0
  795. package/resources/skills/video-recap-production/upstream/skills/video-understanding/references/data-schema.md +193 -0
  796. package/resources/skills/video-recap-production/upstream/skills/video-understanding/references/prompt-templates.md +37 -0
  797. package/resources/skills/video-recap-production/upstream/skills/video-understanding/references/research-guide.md +74 -0
  798. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/agent_text.py +301 -0
  799. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/asr.py +279 -0
  800. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/asr_timing_evidence.py +270 -0
  801. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/brief.py +9 -0
  802. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/briefing/__init__.py +1 -0
  803. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/briefing/builder.py +402 -0
  804. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/briefing/context.py +317 -0
  805. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/briefing/inputs.py +268 -0
  806. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/briefing/timeline.py +326 -0
  807. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/consolidate.py +596 -0
  808. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/deslop_qc.py +211 -0
  809. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/detect.py +504 -0
  810. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/extract.py +62 -0
  811. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/lib.py +419 -0
  812. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/narration_lint.py +707 -0
  813. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/speech_ownership.py +143 -0
  814. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/storyboard.py +445 -0
  815. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/timeline_fusion.py +108 -0
  816. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/understand.py +9 -0
  817. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/understanding_brief.py +190 -0
  818. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/understanding_cache.py +345 -0
  819. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/understanding_runner.py +387 -0
  820. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/understanding_storyboard.py +167 -0
  821. package/resources/skills/video-recap-production/upstream/skills/video-understanding/scripts/vlm.py +674 -0
  822. package/resources/skills/video-recap-production/upstream/skills/video-voiceover/SKILL.md +91 -0
  823. package/resources/skills/video-recap-production/upstream/skills/video-voiceover/references/index-tts.md +31 -0
  824. package/resources/skills/video-recap-production/upstream/skills/video-voiceover/scripts/approved_text_policy.py +123 -0
  825. package/resources/skills/video-recap-production/upstream/skills/video-voiceover/scripts/dub.py +623 -0
  826. package/resources/skills/video-recap-production/upstream/skills/video-voiceover/scripts/lib.py +352 -0
  827. package/resources/skills/video-recap-production/upstream/skills/video-voiceover/scripts/providers/__init__.py +1 -0
  828. package/resources/skills/video-recap-production/upstream/skills/video-voiceover/scripts/providers/fish_audio.py +91 -0
  829. package/resources/skills/video-recap-production/upstream/skills/video-voiceover/scripts/providers/index_tts.py +184 -0
  830. package/resources/skills/video-recap-production/upstream/skills/video-voiceover/scripts/tts_audio.py +102 -0
  831. package/resources/skills/video-recap-production/upstream/skills/video-voiceover/scripts/voiceover.py +771 -0
  832. package/resources/skills/viora-canvas-production/manifest.json +5 -1
  833. package/resources/skills/viora-canvas-production/references/story-handoff.md +49 -0
@@ -0,0 +1,1586 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const { loadStyleWhitelist, maskStyleText } = require('./style-whitelist.js');
6
+ const path = require('path');
7
+
8
+ const USAGE = `Usage: node check-ai-patterns.js [--check] [--json] [--fail-on=blocking|all] <file...>
9
+ node check-ai-patterns.js --list-review-classes
10
+
11
+ Detect high-risk AI-flavor prose patterns that need human rewrite:
12
+ - negative setup followed by positive flip in the same sentence
13
+ - comma/semicolon/colon + positive flip
14
+ - sentence break + positive flip
15
+ - repeated negative setup followed by positive flip
16
+ - em-dash (按功能改写), 碎句号 (连续短叙述句), 长段落 (按镜头断段)
17
+ - 微动作复读 (「了下/了一下」式轻量补语高密度,电报体指纹)
18
+ - 套式反应细节 (指尖/指节/目光等无功能微动作与「平静得像在念」式语气比喻成片)
19
+ - 抽象总结复读 (命运/棋局/这一刻终于明白/才刚刚开始,AI 结尾腔)
20
+ - 套词密度过高 (仿佛/一丝/深吸一口气/平静无波等禁用词聚集)
21
+ - 比喻密度过高 (像/好像/仿佛/如同等比喻标记成片复现)
22
+ - 解释链密度过高 (知道/明白/这意味着/必须/需要等判断链聚集)
23
+ - 系统公告公文腔过密 (方括号系统/规则行里硬规则词聚集)
24
+ - 过度精炼短段 (长文本里短叙述段过密且自然连接偏少)
25
+ - 低连接密度 (引号外叙述功能词/白话连接偏少且中长句不足,像提纲/电报体)
26
+ - 监控摄像头式动作清单 (同段连续摆放动作动词,缺少视角温度/情绪缓冲)
27
+ - 音量反差腔 (声音不高/不大…却…, 实战漏网句式)
28
+ - 否定排比 (没有X,没有Y…连排 / 没X…只是Y 先否定后肯定, 实战漏网句式)
29
+ - 工整并列 (至于X不X,怎么X / 同动词「不V A,不V B」,含台词,advisory)
30
+ - 反序对比 (是A,不是B — not-is 的反序变种, 实战漏网句式)
31
+ - 预告式总结收尾 (文末窗口 没人知道/才刚刚开始/正朝着…压了过去, 实战漏网句式)
32
+ - 章尾状态总结体 (文末窗口 这一夜注定/这一切都结束了/新的人生才刚刚开始/命运的齿轮)
33
+ - 引号强调滥用 (叙述里 1-4 字短词加引号强调,密度型)
34
+
35
+ Book-local .deslop-whitelist literal spans are excluded from style scanning (no regex or ancestor inheritance).
36
+ Each finding carries severity: blocking by default for generation/deslop cleanup (not-is-comparison / em-dash / voice-contrast / negation-parade / reverse-not-is / trailer-ending / trailer-summary). This is a local style/readability gate, not an AIGC detector score; functional human text can be marked for review instead of hard-edited for a detector.
37
+ 或 advisory (period-stutter / long-paragraph / micro-action-tic / negation-pair-tic / stock-reaction-tic / action-list-tic / abstract-summary-tic / cliche-density-tic / metaphor-density-tic / reasoning-chain-tic / system-notice-formality-tic / overcompressed-prose-tic / low-connective-density-tic / quote-emphasis-tic / formulaic-parallelism,是提示,justified 的长推理/氛围段可保留)。
38
+ --fail-on=blocking 只在出现 blocking finding 时退出 1;默认 --fail-on=all 有任何 finding 即退出 1。
39
+ Each finding also carries review: mechanical(表面形态确定、就地改写即可)或 semantic(可能承担叙事功能,需读上下文判断是否保留);blocking 一律 mechanical。--list-review-classes 输出完整分类表(JSON)。
40
+ HTML comments (<!-- ... -->, e.g. the 去味:跳过 exemption marker) are metadata, not prose, and are never scanned.
41
+
42
+ The script reports findings only. It never rewrites text, because the safe fix is
43
+ contextual: usually delete the negative setup, write the positive term directly,
44
+ or show it via action/detail.`;
45
+
46
+ const STOP_CHARS = new Set(['。', '!', '?', '!', '?', '\n']);
47
+ const SOFT_SEPARATORS = new Set([',', ',', '、', ';', ';', ':', ':']);
48
+ const HARD_SEPARATORS = new Set(['。', '.', '!', '!', '?', '?']);
49
+ const MAX_NEGATIVE_SPAN = 80;
50
+ const MAX_POSITIVE_SPAN = 80;
51
+
52
+ // 碎句号:连续 STUTTER_MIN_RUN 个「叙述」短句(每句可见字数 ≤ STUTTER_MAX_SENTENCE)无呼吸。
53
+ // 只数叙述句,跳过对话/弹幕/系统播报(成片短句是这些体裁的正常形态,不算碎句号)。
54
+ const STUTTER_MIN_RUN = 6;
55
+ const STUTTER_MAX_SENTENCE = 5;
56
+ // 长段落:单段原始字符数超过阈值即提示按镜头断段(手机阅读保守阈值,正常单段远低于此)。
57
+ const LONG_PARAGRAPH_CHARS = 200;
58
+
59
+ // 微动作复读:「V了下 / V了一下 / 拍了两下 / 松了半圈」式轻量补语在叙述里高密度复现,
60
+ // 容易形成删减过头的电报体指纹。只扫引号外叙述;密度与次数双门槛同时达标才报,
61
+ // 单次出现是正常中文。
62
+ const MICRO_TIC_PATTERN = /了(?:[一两三几半])?[下阵圈道声眼口气会]/g;
63
+ const MICRO_TIC_MIN_HITS = 5;
64
+ // 「没A,也没B」「不A,也不B」否定对偶:七猫人类长篇约 0.07 处/千字,模型稿 0.4-1.5 处/千字;
65
+ // 写人物没做什么是英文式节奏,一章 3 处以上报一条提示。
66
+ const NEGATION_PAIR_PATTERN = /(?:没有|没|不)[^,。!?;,!?\n]{1,10}[,,;]\s*[^,。!?;,!?\n]{0,6}?也(?:没有|没|不)/g;
67
+ const NEGATION_PAIR_MIN_HITS = 3;
68
+ const MICRO_TIC_PER_KILO = 6;
69
+
70
+ // 套式反应细节:不是禁写身体,而是提示成片出现的“部位 + 轻微动作/状态”、
71
+ // “胸口像被撞了一下”、喉结/眼圈/声音放轻等通用情绪尾巴,以及“平静语气 +
72
+ // 像在念/宣判”模板。此类句子词面变化大,不能逐词 blocking;按章聚集到 4 处才
73
+ // advisory,要求逐处做删除测试。正常受伤、打斗、生理反应若承担物理后果可保留。
74
+ const STOCK_REACTION_PATTERNS = [
75
+ /(?:指尖|手指|指节|手背|掌心|拳头|袖口|衣角|裙角|下唇|嘴唇|唇角|嘴角|眉头|眼底|眸光|目光|视线|肩膀|呼吸)[^。!?!?\n]{0,16}(?:轻轻|微微|缓缓|悄然|不自觉|无意识|下意识|攥紧|握紧|收紧|绞紧|泛白|发白|叩|敲|摩挲|抿紧|抿成|移开|垂下|躲开|一颤|颤了?一下|停了?一下|顿了?一下)/g,
76
+ /(?:语气|声音)[^。!?!?\n]{0,12}(?:平静|冷静|平淡|冷淡|淡漠|平直)[^。!?!?\n]{0,12}(?:像|仿佛|如同|好像)[^。!?!?\n]{0,16}(?:念|读|报|说|陈述|宣判|背诵)/g,
77
+ /(?:胸口|心口)[^。!?!?\n]{0,16}(?:像|仿佛|如同|好像)[^。!?!?\n]{0,16}(?:撞|锤|压|攥|堵)[^。!?!?\n]{0,8}(?:一下|一记|一拳)?/g,
78
+ /(?:声音|嗓音|语气)[^。!?!?\n]{0,12}(?:放轻|压低|发紧|发颤|很轻|轻了些)/g,
79
+ /(?:喉结|喉头|喉咙)[^。!?!?\n]{0,10}(?:滚|动|紧|堵|发涩|发干)/g,
80
+ /(?:眼眶|眼圈|鼻子)[^。!?!?\n]{0,8}(?:发红|红了|发热|发酸|一酸)/g,
81
+ /(?:抿了?下唇|抿了?抿唇|抿了?下嘴|抿着笑)/g,
82
+ ];
83
+ const STOCK_REACTION_MIN_HITS = 4;
84
+ // 校准(真人语料,<br> 已还原为换行):qimao 长篇 5584 章 + heiyan 短篇整篇 3983 篇。
85
+ // 长篇章尺度(中位约 2100 字)per-kilo 1.0→1.5 误报 0.43%→0.39%,几乎不动;
86
+ // 短篇整篇 8000-20000 字下 MIN_HITS 形同虚设、只剩密度门,1.0 时误报 5.57%,
87
+ // 1.5 降到 1.46%。故取 1.5,把两个总体拉到同一量级(四份副本共用一组阈值)。
88
+ const STOCK_REACTION_PER_KILO = 1.5;
89
+
90
+ // 监控摄像头式动作清单:同一段连续堆叠通用动作动词(伸手/拿起/取过/挑开/放下/转身等),
91
+ // 且用逗号/顿号串联成步骤表时,读感像无视角温度的监控记录。只做 advisory;
92
+ // 打斗/追逐等功能性动作编排可保留或人工复核。
93
+ const ACTION_LIST_VERB_PATTERN = /伸手|抬手|探手|拿起|拿过|取出|取过|掏出|摸出|抓起|攥住|握住|捏住|按住|推开|拉开|打开|关上|放下|递给|挑开|掀开|扯开|拧开|倒出|端起|转身|回头|抬头|低头|弯腰|俯身|走到|走向|坐下|站起|看向|看着|盯着|扫过/g;
94
+ const ACTION_LIST_MIN_HITS = 5;
95
+ const ACTION_LIST_MIN_SEPARATORS = 4;
96
+
97
+ // 抽象总结复读:模板化段落常把角色当下经历拔成「命运/棋局/
98
+ // 这一刻终于明白/才刚刚开始」的作者总结。单个词可能服务题材;高密度聚集才报。
99
+ const ABSTRACT_SUMMARY_PATTERNS = [
100
+ /这一刻[,,]?[^\n。!?!?]{0,24}(?:终于|才)(?:明白|意识到)/g,
101
+ /从这一刻开始/g,
102
+ /(?:命运|宿命)[^\n。!?!?]{0,28}(?:齿轮|棋局|獠牙|改写|推向|安排)/g,
103
+ /早已[^\n。!?!?]{0,8}(?:布好|安排好)[^\n。!?!?]{0,8}(?:棋局|局)/g,
104
+ /前所未有的(?:决意|清醒|勇气|力量|恐惧|平静|信念)/g,
105
+ /(?:反击|复仇|战争|较量|故事|命运)[^\n。!?!?]{0,12}才刚刚开始/g,
106
+ /(?:新的开始|全新的开始)/g,
107
+ ];
108
+ const ABSTRACT_SUMMARY_MIN_HITS = 3;
109
+ const ABSTRACT_SUMMARY_PER_KILO = 4;
110
+
111
+ // 套词密度:单个「仿佛/一丝」可能是正常中文,高密度聚集才会形成模板腔。
112
+ // 词表只收本 repo banned-words 中已明确标为高危的形态,避免把普通功能词一网打尽。
113
+ const CLICHE_PATTERNS = [
114
+ /仿佛|犹如|宛若|如同/g,
115
+ /一丝|一抹|些许|几分|隐约/g,
116
+ /深吸一口气|缓缓|微微|轻轻|淡淡/g,
117
+ /眼中闪过|嘴角勾起|眸光微微一闪|指节泛白|目光锐利|眼神锐利/g,
118
+ /心中涌起一股|心头一震|心中一动|心下了然|心中暗道|心中一凛/g,
119
+ /不容置疑|不容置喙|不易察觉|显而易见|毫无疑问|不可否认/g,
120
+ /声音不大[,,]?却带着|语气平静无波|平静无波|声音平直|听不出情绪/g,
121
+ /不知何时|唾手可得|无声翻涌|沉默(?:在[^。!?!?\n]{0,16})?蔓延|难以言说/g,
122
+ /散发着一股|冰冷的光|格外刺眼|深邃而冰冷/g,
123
+ ];
124
+ const CLICHE_DENSITY_MIN_HITS = 8;
125
+ const CLICHE_DENSITY_PER_KILO = 12;
126
+
127
+ // 比喻密度:单个生活化比喻可服务画面;“像/好像/仿佛/如同”成片复现时,
128
+ // 容易变成 AI 式修辞堆叠。只做 advisory,修法是删到必要数量并回到具体画面,
129
+ // 不是把“像”换成另一组比喻词。
130
+ const METAPHOR_MARKER_PATTERN = /好像|像是|仿佛|宛如|如同|犹如|(?<![不头图画影录摄肖])像(?![头像素])/g;
131
+ const METAPHOR_LIKE_PHRASE_PATTERN = /(?:死|水|冰|火|潮水|石头|木头|机器|纸|铁|鬼|死人|刀|针|网|墙)一样/g;
132
+ const METAPHOR_DENSITY_MIN_HITS = 7;
133
+ const METAPHOR_DENSITY_PER_KILO = 3;
134
+
135
+ // 解释链密度:常见“他知道/他明白/这意味着/必须需要”
136
+ // 连续替读者推理,读感像报告。单个判断词可服务推理;高密度聚集才提示回到角色当下证据。
137
+ const REASONING_CHAIN_PATTERNS = [
138
+ { key: 'mental', core: true, pattern: /(?<![不没未无])(?:他|她|我)?(?:知道|明白|意识到|清楚|判断|确认|分析)/g },
139
+ { key: 'connector', core: true, pattern: /这意味着|也就是说|换句话说|真正的问题(?:在于)?|问题在于|关键在于|在这种情况下|按照这个逻辑|只有这样|想到这里/g },
140
+ { key: 'modal', core: true, pattern: /(?:(?<!不)(?:必须|需要|应该|只要|就会|可能|可以|能够|无法)|不能)[^。!?!?\n]{0,16}(?:判断|确认|承担|维持|稳住|控制|扩大|失控|带来|造成|理解|默认|回家|进门|核对|筛选|减少|建立|风险|结果|秩序|责任)/g },
141
+ { key: 'abstract', core: false, pattern: /(?:任务|条件|风险|来源|逻辑|局面|结果|责任|秩序|规则|信息不足|决策能力)/g },
142
+ ];
143
+ const REASONING_CHAIN_MIN_HITS = 8;
144
+ const REASONING_CHAIN_CORE_MIN_HITS = 4;
145
+ const REASONING_CHAIN_MIN_BUCKETS = 2;
146
+ const REASONING_CHAIN_PER_KILO = 18;
147
+
148
+ // 系统公告公文腔:只看成片方括号规则/面板行里的硬规则词。
149
+ // 这不是特定题材词表;单条严肃规则、日常叙述或普通对话不触发。
150
+ const NOTICE_FORMAL_PATTERNS = [
151
+ /不得|必须|不可|禁止|严禁|应当|须|需|务必/g,
152
+ /当前|本公告|本规则|本系统|提示|任务失败|临时权限|权限|状态|等级/g,
153
+ /维持|公共区域|秩序|优先|惩罚|处罚|违规|指令|执行/g,
154
+ /被视为|同样计入|计入|承担|责任|单位|撤回|转发|截图/g,
155
+ ];
156
+ const NOTICE_FORMAL_CORE_PATTERN = /不得|必须|不可|禁止|严禁|应当|须|需|务必|被视为|同样计入|计入/g;
157
+ const NOTICE_FORMAL_MIN_LINES = 4;
158
+ const NOTICE_FORMAL_MIN_HITS = 12;
159
+ const NOTICE_FORMAL_CORE_MIN_HITS = 5;
160
+ const NOTICE_FORMAL_PER_KILO = 60;
161
+
162
+ // 过度精炼短段:过度处理样本里常见大量 15 字以内叙述段,且“的/了/就/着/过/呢/吧/啊”等
163
+ // 自然连接偏少;对照文本通常保留更多自然连接。此项只做 advisory,禁止机械注水。
164
+ const OVERCOMPRESSED_PROSE_PARTICLE_PATTERN = /[的了就着过呢吧啊呀嘛]/g;
165
+ const OVERCOMPRESSED_PROSE_MIN_CHARS = 1200;
166
+ const OVERCOMPRESSED_PROSE_MIN_PARAS = 45;
167
+ const OVERCOMPRESSED_PROSE_SHORT_MAX_CHARS = 15;
168
+ const OVERCOMPRESSED_PROSE_SHORT_RATIO = 0.58;
169
+ const OVERCOMPRESSED_PROSE_PARTICLE_PER_KILO = 85;
170
+
171
+ // 低连接密度:单纯低功能词会误抓有大量中长句的文本;
172
+ // 因此必须叠加“中长句不足”,并只看引号外叙述。这是 overcompressed 的短窗口补充,只做 advisory。
173
+ const LOW_CONNECTIVE_FUNCTION_TERMS = ['的', '了', '就', '在', '是', '也', '都', '还', '又', '把', '被', '给', '这个', '那个', '里面', '以后', '时候', '现在', '因为', '所以', '但是', '不过', '然后', '已经', '还是', '起来', '出来', '下去'];
174
+ const LOW_CONNECTIVE_PLAIN_TERMS = ['的', '了', '就', '也', '还', '又', '这个', '那个', '东西', '事情', '时候', '里面', '以后', '一下', '一点', '有点', '还是'];
175
+ const LOW_CONNECTIVE_MIN_CHARS = 800;
176
+ const LOW_CONNECTIVE_FUNCTION_PER_KILO = 100;
177
+ const LOW_CONNECTIVE_PLAIN_PER_KILO = 65;
178
+ const LOW_CONNECTIVE_LONG_SENTENCE_CHARS = 30;
179
+ const LOW_CONNECTIVE_LONG_SENTENCE_RATIO = 0.08;
180
+
181
+ // either-or「不是A就是B / 不是A也是B」里紧贴的「是」是连词的一部分,不是肯定项系动词。
182
+ // 含「不」以沿用「不是A,也不是B」第二个否定段不算翻转的旧排除。
183
+ const COMPACT_EITHER_OR_PREV = new Set(['不', '就', '也']);
184
+ // 句尾语气/反问助词;「…,是吗 / 是吧 / 是嘛」是反问尾巴,不是否定后的肯定翻转。
185
+ const TAG_PARTICLES = new Set(['吗', '吧', '嘛']);
186
+ // 段首确认语;「不是第一次来。是的,他还记得……」里的「是的/是啊」
187
+ // 是承接确认,不是「不是 A,是 B」的肯定翻转。
188
+ const AFFIRMATION_TAG_PARTICLES = new Set(['的', '啊', '呀', '呢']);
189
+ const AFFIRMATION_TAG_BOUNDARY = new Set(['', ',', ',', '。', '.', '!', '!', '?', '?', '、', ';', ';', ':', ':', '\n', '\r', '\t', ' ']);
190
+
191
+ // 成对引号(台词/系统播报/弹幕)的字符对,stripQuoted 与 quotedRanges 共用一份来源。
192
+ // 引号片段一律不跨行(字符类里排掉 \n):正文漏一个收引号很常见(多段台词只在末段收尾、
193
+ // 全半角引号混用都会漏),若允许跨行配对,一个未闭合的开引号会把后面成百上千字全算成
194
+ // 「引号内」,让 quotedRanges 的消费方(not-is 跨行扫描)把整段叙述静默豁免掉。
195
+ const QUOTE_PAIRS = [['「', '」'], ['『', '』'], ['【', '】'], ['“', '”'], ['‘', '’'], ['"', '"'], ["'", "'"]];
196
+ const QUOTE_SOURCES = QUOTE_PAIRS.map(([open, close]) => `${escapeRegExp(open)}[^${escapeRegExpCharClass(close)}\\n]*${escapeRegExp(close)}`);
197
+
198
+ // ---- 实战测试漏网句式(来源:实战写作抓到的真实漏网例句;2026-07 校准)----
199
+ // 校准基线:《万疆》真人正文 20 章(第1/10/20/…/190章)+ demo 前 20 章。
200
+ // blocking 规则要求真人语料命中 ≈0(每 20 章 ≤1 处且人工判定确属该句式);数据见各规则注释。
201
+
202
+ // 音量反差腔(实战漏网 A):「声音不高,第一句却稳稳压住了整个大厅。」
203
+ // 旧网只有套词密度桶里的「声音不大,却带着」,音量词/转折词一换就漏。
204
+ // 引号外叙述逐处 blocking;修法是删掉音量铺垫,直接写声音落进场子的具体效果。
205
+ // 校准:《万疆》20 章 0 命中,demo 前 20 章 0 命中。
206
+ const VOICE_CONTRAST_PATTERN = /声音(?:并)?不[大高响亮][^。!?!?\n]{0,16}[却但偏]/g;
207
+
208
+ // 否定排比(实战漏网 B):「没有伴奏,没有和声,没有提词器。」同句 ≥2 个「没有X,」连排;
209
+ // 变体「他没炫技,没有那种…架势。他只是唱」先否定铺垫、再用「只是/只会/只有」收肯定。
210
+ // 只收「没/没有」段,不收「不X」段——真人叙述里「不哭不闹」类太常见,收进来误报换不来收益。
211
+ // 光杆「没」还得挡两类非否定用法,否则正常叙述会被判成排比:
212
+ // 1) 黏着语素(沉没/淹没/埋没/出没/隐没…)——前字排除,「船沉没在雾里,没人回头,…只有…」不算;
213
+ // 2) 时间惯用语(没多久/没过多久/没等X)——后字排除,「没多久,没等她撑伞,…只有…」不算。
214
+ // 「没有X」段不带这两种歧义(黏着语素后接不出「有」,时间惯用语已被后字排除覆盖),
215
+ // 第一条连排式照旧不加护栏。
216
+ // 校准:《万疆》20 章 0 命中,demo 前 20 章 0 命中。
217
+ const NEGATION_PARADE_PATTERNS = [
218
+ /(?:没有[^。!?!?\n,,]{1,12}[,,]){2}/g,
219
+ /(?<![沉淹埋出隐湮吞覆漫泯])没(?!有?过?多久)(?:有)?[^。!?!?\n,,]{1,12}[,,]\s*没(?!有?过?多久)(?:有)?[^。!?!?\n,,]{1,16}[,,。.][^。!?!?\n,,]{0,6}只(?:是|会|有)/g,
220
+ ];
221
+ const CROSS_NEGATION_START = /^不是[^。!?!?\n]{1,24}[。!?!?]?$/;
222
+ const CROSS_NEGATION_MIDDLE = /^(?:也|还)不是[^。!?!?\n]{1,24}[。!?!?]?$/;
223
+ const CROSS_NEGATION_END = /^只是[^。!?!?\n]{1,32}[。!?!?]?$/;
224
+
225
+ // 两类常见但不能直接判错的工整框架,只做 advisory。与 blocking 规则不同,这里故意扫描
226
+ // 台词:自然点单「不放辣,不放葱」靠对象最短长度排除;更长的同动词清单交语义审查判断功能。
227
+ const DECISION_FRAME_PATTERN = /至于([\u3400-\u9fff]{1,3})不\1[,,]\s*怎么\1/g;
228
+ const REPEATED_NEGATIVE_VERB_PATTERN = /不([\u3400-\u9fff]{1,2})([\u3400-\u9fff]{2,8})[,,]\s*不\1([\u3400-\u9fff]{2,8})/g;
229
+
230
+ // 反序对比腔(实战漏网 C):「是真嗓子,不是修音修出来的」——not-is-comparison 的反序变种。
231
+ // 复用 not-is 的排除基建:引号内剥离(maskQuoted)、「是的/是啊」确认语(isAffirmationTagAt);
232
+ // 前字排除从 either-or 的 不/就/也 扩展到全部「X是」连词/副词合成词(还是/只是/可是/但是/
233
+ // 于是/倒是/像是/若是/要是/正是/便是/总是/老是/更是/最是/算是/怕是/凡是/或是/即是/自是/
234
+ // 竟是/原是/本是/仍是/许是/净是/光是/单是/尽是);「是不是」问句起头与「不是吗/不是么/
235
+ // 不是吧」反问尾巴单独排除。
236
+ // 校准:《万疆》20 章 0 命中,demo 前 20 章 0 命中,按 blocking 实现。
237
+ const REVERSE_NOT_IS_PATTERN = /是([^。!?!?\n,,]{1,12})[,,]\s*(?:而)?不是([^。!?!?\n]{1,20})/g;
238
+ const REVERSE_NOT_IS_PREV_EXCLUDE = new Set([...COMPACT_EITHER_OR_PREV, '还', '只', '可', '但', '于', '倒', '像', '若', '要', '正', '便', '总', '老', '更', '最', '算', '怕', '凡', '或', '即', '自', '竟', '原', '本', '仍', '许', '净', '光', '单', '尽']);
239
+
240
+ // 预告式总结收尾(实战漏网 D):「没人知道,这才刚刚开头。」「一场…震惊接力,正朝着…缓缓压了过去。」
241
+ // 章尾替读者预告下一章走向是 AI 收尾腔。只扫文末窗口(剥引号后可见字数,按行取整),
242
+ // 正文中段的「没人知道」多为普通叙述,不误伤;引号内台词(「没人知道…」)不计。
243
+ // 「正式拉开序幕/帷幕」是场内事件的报幕式陈述(真人语料「钟声再度响起,比赛正式拉开序幕」),
244
+ // 不是叙述者预告,前置 lookbehind 排除。
245
+ // 校准:《万疆》20 章排除「正式拉开序幕」2 处报幕句后 0 命中,demo 前 20 章 0 命中。
246
+ const TRAILER_ENDING_PATTERN = /没人知道|谁也不知道|谁也没想到|殊不知|(?:这)?才刚刚开(?:始|头)|正(?:朝着|向着)[^。!?!?\n]{0,24}(?:压|涌|袭|逼)(?:了?过去|了?过来|来)|(?<!正式)拉开(?:序幕|帷幕)|即将(?:开始|来临|降临)/g;
247
+ const TRAILER_ENDING_WINDOW_CHARS = 600;
248
+
249
+ // 章尾状态总结体:把细纲「结尾设定」原样写成总结句收章(「这一夜注定无人入眠」
250
+ // 「这一切都结束了」「新的人生才刚刚开始」「命运的齿轮」)。与 trailer-ending 共用文末窗口,
251
+ // 区别是它盖章过去、trailer-ending 预告将来;收的都是 banned-words 已按名禁掉的形态。
252
+ // 不收「(这|那)一刻…终于明白」:真人语料里那是正常的认知节拍,短篇第一人称审判句还是卖点
253
+ // (short-craft「审判金句 / 心死余韵」),密度型由 advisory 的 abstract-summary-tic 兜。
254
+ // 各分支都要求落在句末断言位,否则会吃进条件从句(等这一切结束了,我们就…)、动补
255
+ // (这一切都说明得非常清楚)、成语跨匹配(这一刻…命中注定)、系表(这一战的结果是注定的)、
256
+ // 及物用法(就这样…才结束了这个话题)、场内报幕(就这样…宣布…圆满落幕)和否定认知
257
+ // (他不知道这一切意味着什么)——最后一类靠 (?!什么) 排掉间接疑问,那是盖章的反面。
258
+ // 校准(文末 600 字窗口,命中逐条人工复核):qimao 章中段 20000 章命中 1 处(0.005%)、
259
+ // heiyan 整篇 3999 篇命中 22 处(0.550%,全部是上列禁用形态);同批既有 trailer-ending
260
+ // 分别命中 1.345% / 6.602%——本规则误报面显著小于已上线的同窗口规则。短篇整篇即收口,
261
+ // 基线天然高于长篇章中段,故两个总体分别报数。
262
+ const TRAILER_SUMMARY_PATTERN = /这一(?:夜|天|刻|战|年|局|役)[,,]?[^。!?!?,,\n]{0,6}(?<!命中)(?<!是)注定[^。!?!?\n]{0,8}[。!]|就这样[,,][^。!?!?,,\n]{0,8}(?:一切|全部)[^。!?!?,,\n]{0,4}(?:结束了|落幕|收场)[。!]|这一切[,,]?[^。!?!?,,\n]{0,6}(?:都)?(?:说明|意味着|结束了)(?!的)(?:(?!什么)[^。!?!?\n]){0,6}[。!]|(?:新的篇章|新的旅程|崭新的篇章|新的人生)[^。!?!?\n]{0,6}(?:开始|拉开|展开)|命运[^。!?!?\n]{0,6}齿轮/g;
263
+
264
+ // 引号强调滥用(实战漏网 E,advisory 密度型,风格照 metaphor-density-tic):
265
+ // 叙述里短词加引号强调(他是被请来"把关"的)。只数叙述层 1-4 字成对引号片段;
266
+ // 排除项:【】系统面板载体、引语动词(说|道|问|喊|答|念|叫|回|吼|嘀咕,加细 骂|写|读|唱)
267
+ // 前 6 字/后 3 字邻接的极短台词、引号内含句读的台词、引号外无叙述的行(独立台词/
268
+ // 弹幕流/拟声词连发)、引号套引号(台词内强调)。全文 ≥3 处报一条——单处强调是
269
+ // 正常修辞,密度高才是模板腔。
270
+ // 校准:demo 前 20 章 0 章过阈值;《万疆》20 章 2 章过阈值(海报标语“我在番城”系列、
271
+ // “邀战书”等转述载体,真人也这么写),所以该规则只做 advisory,不升 blocking。
272
+ const QUOTE_EMPHASIS_MIN_HITS = 3;
273
+ const QUOTE_EMPHASIS_MAX_VISIBLE = 4;
274
+ const QUOTE_EMPHASIS_SPEECH_VERB_PATTERN = /[说道问喊答念叫回吼骂写读唱嘀咕]/;
275
+
276
+ // review 分类:下游按它分流。mechanical = 命中的表面形态本身就是问题,写作端就地改写即可;
277
+ // semantic = 同一形态可能承担叙事功能,要读上下文判断保留还是改(交语义审查)。blocking 只能是
278
+ // mechanical(blocking 的含义就是不需判断、必须清零)。新增 finding type 必须在此登记,否则运行期
279
+ // 直接报错退出(scripts/test-ai-patterns.sh 另从源码抽 type 与本表双向核对)。
280
+ // type review 理由
281
+ // not-is-comparison mechanical 固定句式模板「不是A,而是B」,blocking
282
+ // voice-contrast mechanical 固定反差模板「声音不大…却」,blocking
283
+ // negation-parade mechanical 固定否定排比模板,blocking
284
+ // reverse-not-is mechanical 固定反序对比模板「是A,不是B」,blocking
285
+ // trailer-ending mechanical 文末窗口固定预告短语,blocking
286
+ // trailer-summary mechanical 文末窗口固定总结短语,blocking
287
+ // em-dash mechanical 标点,blocking
288
+ // long-paragraph mechanical 段长阈值,断段即可
289
+ // period-stutter mechanical 连续短句计数,合句即可
290
+ // micro-action-tic mechanical 固定补语「了下/了一下」密度
291
+ // negation-pair-tic semantic 「没A,也没B」否定对偶,留真正要强调的
292
+ // cliche-density-tic mechanical 固定禁用词表密度
293
+ // stock-reaction-tic semantic 身体反应可能承担物理后果或情绪,要逐处做删除测试
294
+ // action-list-tic semantic 打斗/追逐等功能性动作编排可保留
295
+ // abstract-summary-tic semantic 作者总结/上帝视角,是否越位要看语境
296
+ // metaphor-density-tic semantic 比喻是否服务画面、删哪几个要读上下文
297
+ // reasoning-chain-tic semantic 替读者推理的解释链,长推理段可能是正当的
298
+ // system-notice-formality-tic semantic 系统/规则文本本就可以公文腔,看体裁功能
299
+ // overcompressed-prose-tic semantic 短段过密可能是节奏选择,禁止机械注水
300
+ // low-connective-density-tic semantic 同上,连接词补不补要看读感
301
+ // quote-emphasis-tic semantic 标语/转述载体的引号是正常写法
302
+ // formulaic-parallelism semantic 工整并列可能承担辩解或悬念排除
303
+ const REVIEW_CLASSES = Object.freeze({
304
+ 'not-is-comparison': 'mechanical',
305
+ 'voice-contrast': 'mechanical',
306
+ 'negation-parade': 'mechanical',
307
+ 'reverse-not-is': 'mechanical',
308
+ 'trailer-ending': 'mechanical',
309
+ 'trailer-summary': 'mechanical',
310
+ 'em-dash': 'mechanical',
311
+ 'long-paragraph': 'mechanical',
312
+ 'period-stutter': 'mechanical',
313
+ 'micro-action-tic': 'mechanical',
314
+ 'negation-pair-tic': 'semantic',
315
+ 'cliche-density-tic': 'mechanical',
316
+ 'stock-reaction-tic': 'semantic',
317
+ 'action-list-tic': 'semantic',
318
+ 'abstract-summary-tic': 'semantic',
319
+ 'metaphor-density-tic': 'semantic',
320
+ 'reasoning-chain-tic': 'semantic',
321
+ 'system-notice-formality-tic': 'semantic',
322
+ 'overcompressed-prose-tic': 'semantic',
323
+ 'low-connective-density-tic': 'semantic',
324
+ 'quote-emphasis-tic': 'semantic',
325
+ 'formulaic-parallelism': 'semantic',
326
+ });
327
+
328
+ const options = {
329
+ json: false,
330
+ files: [],
331
+ failOn: 'all',
332
+ };
333
+
334
+ for (let i = 2; i < process.argv.length; i += 1) {
335
+ const arg = process.argv[i];
336
+ if (arg === '--check') {
337
+ // Accepted for symmetry with normalize-punctuation.js; detection is always check-only.
338
+ } else if (arg === '--list-review-classes') {
339
+ process.stdout.write(`${JSON.stringify(REVIEW_CLASSES, null, 2)}\n`);
340
+ process.exit(0);
341
+ } else if (arg === '--json') {
342
+ options.json = true;
343
+ } else if (arg.startsWith('--fail-on=')) {
344
+ const v = arg.slice('--fail-on='.length);
345
+ if (v !== 'blocking' && v !== 'all') die(`--fail-on must be 'blocking' or 'all'`);
346
+ options.failOn = v;
347
+ } else if (arg === '-h' || arg === '--help') {
348
+ process.stdout.write(`${USAGE}\n`);
349
+ process.exit(0);
350
+ } else if (arg.startsWith('-')) {
351
+ die(`Unknown option: ${arg}`);
352
+ } else {
353
+ options.files.push(arg);
354
+ }
355
+ }
356
+
357
+ if (options.files.length === 0) {
358
+ die('No files provided');
359
+ }
360
+
361
+ let failed = false;
362
+ const allFindings = [];
363
+
364
+ for (const file of options.files) {
365
+ const fullPath = path.resolve(file);
366
+ let input;
367
+ try {
368
+ input = fs.readFileSync(fullPath, 'utf8');
369
+ } catch (error) {
370
+ failed = true;
371
+ if (!options.json) console.error(`${file}: unable to read (${error.message})`);
372
+ continue;
373
+ }
374
+
375
+ let whitelist;
376
+ try { whitelist = loadStyleWhitelist(fullPath); }
377
+ catch (error) { die(`${file}: unable to read .deslop-whitelist (${error.message})`); }
378
+ const findings = scanDocument(maskStyleText(input, whitelist)).map((finding) => ({ file, ...finding, review: reviewClass(finding.type) }));
379
+ allFindings.push(...findings);
380
+ }
381
+
382
+ if (options.json) {
383
+ process.stdout.write(`${JSON.stringify({ findings: allFindings }, null, 2)}\n`);
384
+ } else {
385
+ for (const finding of allFindings) {
386
+ console.log(`${finding.file}:${finding.line}:${finding.column}: [${finding.severity}] ${finding.type}: ${finding.message} (${finding.excerpt})`);
387
+ }
388
+ }
389
+
390
+ if (failed) process.exit(2);
391
+ // --fail-on=blocking 只在出现 blocking finding 时退出 1(advisory 仅报告);默认 all 沿用「有任何 finding 即 1」。
392
+ const hasBlocking = allFindings.some((f) => f.severity === 'blocking');
393
+ if (options.failOn === 'blocking' ? hasBlocking : allFindings.length > 0) process.exit(1);
394
+
395
+ function reviewClass(type) {
396
+ const review = REVIEW_CLASSES[type];
397
+ if (!review) {
398
+ console.error(`internal error: finding type '${type}' has no review class (add it to REVIEW_CLASSES)`);
399
+ process.exit(2);
400
+ }
401
+ return review;
402
+ }
403
+
404
+ function escapeRegExp(text) {
405
+ return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
406
+ }
407
+
408
+ function escapeRegExpCharClass(text) {
409
+ return text.replace(/[\\\]^-]/g, '\\$&');
410
+ }
411
+
412
+ function die(message) {
413
+ console.error(message);
414
+ console.error(USAGE.trimEnd());
415
+ process.exit(2);
416
+ }
417
+
418
+ // unclosedOpenings:到文末都没闭合的 `<!--` 位置("行号:列号")。未闭合的注释不是注释,
419
+ // 否则一个漏写的 `-->` 会把后面整章藏起来;这些位置按正文扫描,只把 `<!--` 本身抹掉。
420
+ function scanDocument(input, unclosedOpenings = new Set()) {
421
+ const lines = input.split(/\r?\n/);
422
+ const findings = [];
423
+ let fence = null;
424
+ let inFrontMatter = hasYamlFrontMatter(lines);
425
+ let commentOpen = false;
426
+ let commentStart = null;
427
+ let block = [];
428
+ const proseLines = [];
429
+
430
+ const flushBlock = () => {
431
+ if (block.length === 0) return;
432
+ findings.push(...scanBlock(block));
433
+ block = [];
434
+ };
435
+
436
+ for (let index = 0; index < lines.length; index += 1) {
437
+ const line = lines[index];
438
+ const trimmed = line.trim();
439
+
440
+ if (inFrontMatter) {
441
+ if (index > 0 && trimmed === '---') inFrontMatter = false;
442
+ continue;
443
+ }
444
+
445
+ const fenceMarker = parseFenceMarker(trimmed);
446
+ if (fence) {
447
+ if (fenceMarker && fenceMarker.char === fence.char && fenceMarker.length >= fence.length) {
448
+ fence = null;
449
+ }
450
+ continue;
451
+ }
452
+
453
+ if (fenceMarker && !commentOpen) {
454
+ flushBlock();
455
+ fence = fenceMarker;
456
+ continue;
457
+ }
458
+
459
+ // HTML 注释(如 `<!-- 去味:跳过 -->` 豁免标记)是元信息不是正文:注释段等长换成空格
460
+ // 保列号,整行都是注释的直接跳过;注释可跨行。
461
+ const masked = maskHtmlComments(line, commentOpen, index, unclosedOpenings);
462
+ commentOpen = masked.open;
463
+ if (masked.openedAt !== null) commentStart = masked.openedAt;
464
+ if (masked.text !== line && !masked.text.trim()) continue;
465
+
466
+ block.push({ text: masked.text, lineNo: index + 1 });
467
+ proseLines.push({ text: masked.text, lineNo: index + 1 });
468
+ }
469
+
470
+ if (commentOpen && commentStart !== null) {
471
+ return scanDocument(input, new Set([...unclosedOpenings, commentStart]));
472
+ }
473
+
474
+ flushBlock();
475
+ findings.push(...scanProsePatterns(proseLines));
476
+ findings.sort((a, b) => a.line - b.line || a.column - b.column);
477
+ return findings;
478
+ }
479
+
480
+ // 段落级检测:碎句号(连续短叙述句)、长段落、破折号(按功能改写,非机械替换)。
481
+ function scanProsePatterns(proseLines) {
482
+ const findings = [];
483
+
484
+ for (const { text, lineNo } of proseLines) {
485
+ const trimmed = text.trim();
486
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
487
+
488
+ const dashPattern = /——|—|--+/g;
489
+ let dash;
490
+ while ((dash = dashPattern.exec(text)) !== null) {
491
+ findings.push({
492
+ line: lineNo,
493
+ column: dash.index + 1,
494
+ type: 'em-dash',
495
+ severity: 'blocking',
496
+ message: '破折号按功能改写:打断→动作 beat/短句,拖长音→省略或动作,插入说明→逗号/冒号;勿一律改句号。',
497
+ excerpt: compact(text.slice(Math.max(0, dash.index - 8), dash.index + dash[0].length + 8)),
498
+ });
499
+ }
500
+
501
+ if (trimmed.length > LONG_PARAGRAPH_CHARS) {
502
+ findings.push({
503
+ line: lineNo,
504
+ column: 1,
505
+ type: 'long-paragraph',
506
+ severity: 'advisory',
507
+ message: `段落过长(${trimmed.length} 字):按镜头/新动作/新线索/视线切换断段,别一段到底。`,
508
+ excerpt: compact(trimmed.slice(0, 40)),
509
+ });
510
+ }
511
+ }
512
+
513
+ findings.push(...findVoiceContrast(proseLines));
514
+ findings.push(...findNegationParade(proseLines));
515
+ findings.push(...findFormulaicParallelism(proseLines));
516
+ findings.push(...findReverseNotIs(proseLines));
517
+ findings.push(...findTrailerEnding(proseLines));
518
+ findings.push(...findQuoteEmphasisTic(proseLines));
519
+ findings.push(...findPeriodStutter(proseLines));
520
+ findings.push(...findMicroActionTic(proseLines));
521
+ findings.push(...findNegationPairTic(proseLines));
522
+ findings.push(...findStockReactionTic(proseLines));
523
+ findings.push(...findActionListTic(proseLines));
524
+ findings.push(...findAbstractSummaryTic(proseLines));
525
+ findings.push(...findClicheDensityTic(proseLines));
526
+ findings.push(...findMetaphorDensityTic(proseLines));
527
+ findings.push(...findReasoningChainTic(proseLines));
528
+ findings.push(...findNoticeFormalityTic(proseLines));
529
+ findings.push(...findOvercompressedProseTic(proseLines));
530
+ findings.push(...findLowConnectiveDensityTic(proseLines));
531
+ return findings;
532
+ }
533
+
534
+ // 音量反差腔(实战漏网 A):引号外叙述逐处 blocking,位置与摘录取自原文
535
+ // (maskQuoted 等长占位保偏移;命中片段不含问号占位符,故不会落进占位区)。
536
+ function findVoiceContrast(proseLines) {
537
+ const findings = [];
538
+
539
+ for (const { text, lineNo } of proseLines) {
540
+ const trimmed = text.trim();
541
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
542
+ const masked = maskQuoted(text);
543
+ VOICE_CONTRAST_PATTERN.lastIndex = 0;
544
+ let match;
545
+ while ((match = VOICE_CONTRAST_PATTERN.exec(masked)) !== null) {
546
+ findings.push({
547
+ line: lineNo,
548
+ column: match.index + 1,
549
+ type: 'voice-contrast',
550
+ severity: 'blocking',
551
+ message: '音量反差腔:「声音不大/不高…却/但…」是 AI 高频反差模板;删掉音量铺垫,直接写声音落进场子的具体效果(谁停了手、哪排安静了)。',
552
+ excerpt: compact(text.slice(match.index, match.index + match[0].length)),
553
+ });
554
+ }
555
+ }
556
+
557
+ return findings;
558
+ }
559
+
560
+ // 否定排比(实战漏网 B):同句「没有X,」连排 / 先否定后「只是」收肯定。
561
+ // 可能在同一片文字上重叠命中,按区间去重只报一次。
562
+ function findNegationParade(proseLines) {
563
+ const findings = [];
564
+
565
+ for (const { text, lineNo } of proseLines) {
566
+ const trimmed = text.trim();
567
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
568
+ const masked = maskQuoted(text);
569
+
570
+ const spans = [];
571
+ for (const pattern of NEGATION_PARADE_PATTERNS) {
572
+ pattern.lastIndex = 0;
573
+ let match;
574
+ while ((match = pattern.exec(masked)) !== null) {
575
+ spans.push([match.index, match.index + match[0].length]);
576
+ }
577
+ }
578
+ spans.sort((a, b) => a[0] - b[0]);
579
+
580
+ let lastEnd = -1;
581
+ for (const [start, end] of spans) {
582
+ if (start < lastEnd) {
583
+ lastEnd = Math.max(lastEnd, end);
584
+ continue;
585
+ }
586
+ lastEnd = end;
587
+ findings.push({
588
+ line: lineNo,
589
+ column: start + 1,
590
+ type: 'negation-parade',
591
+ severity: 'blocking',
592
+ message: '否定排比:「没有X,没有Y…」/「没X,没有Y,只是Z」是 AI 高频排比模板;删掉否定清单,直接写现场实际有什么,最多留一个最有信息量的否定。',
593
+ excerpt: compact(text.slice(start, end)),
594
+ });
595
+ }
596
+ }
597
+
598
+ return findings;
599
+ }
600
+
601
+ function findFormulaicParallelism(proseLines) {
602
+ const findings = [];
603
+
604
+ for (const { text, lineNo } of proseLines) {
605
+ const trimmed = text.trim();
606
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
607
+ for (const [pattern, message] of [
608
+ [DECISION_FRAME_PATTERN, '「至于X不X,怎么X」把同一决定拆成工整栏目;若只是复述细纲,压成角色当下的一次判断或直接动作。'],
609
+ [REPEATED_NEGATIVE_VERB_PATTERN, '同动词「不V A,不V B」容易写成否定清单;含台词也要按语境复核,保留真正有功能的一项即可。'],
610
+ ]) {
611
+ pattern.lastIndex = 0;
612
+ let match;
613
+ while ((match = pattern.exec(text)) !== null) {
614
+ findings.push({
615
+ line: lineNo,
616
+ column: match.index + 1,
617
+ type: 'formulaic-parallelism',
618
+ severity: 'advisory',
619
+ message,
620
+ excerpt: compact(match[0]),
621
+ });
622
+ }
623
+ }
624
+ }
625
+
626
+ // 跨段「不是A / 也不是B / 只是C」既可能是细纲复述,也可能是正常的
627
+ // 辩解、悬念排除或情绪递进。纯句法无法稳定区分,因此只给 advisory,交给语义复核。
628
+ const window = [];
629
+ for (const { text, lineNo } of proseLines) {
630
+ const trimmed = text.trim();
631
+ if (!trimmed) continue;
632
+ if (isDivider(trimmed) || isStructural(trimmed)) {
633
+ window.length = 0;
634
+ continue;
635
+ }
636
+ if (window.length && lineNo - window[window.length - 1].lineNo > 2) window.length = 0;
637
+ window.push({ text: maskQuoted(trimmed), original: trimmed, lineNo });
638
+ if (window.length > 3) window.shift();
639
+ if (window.length !== 3) continue;
640
+ if (!CROSS_NEGATION_START.test(window[0].text)
641
+ || !CROSS_NEGATION_MIDDLE.test(window[1].text)
642
+ || !CROSS_NEGATION_END.test(window[2].text)) continue;
643
+ findings.push({
644
+ line: window[0].lineNo,
645
+ column: 1,
646
+ type: 'formulaic-parallelism',
647
+ severity: 'advisory',
648
+ message: '跨段「不是… / 也不是… / 只是…」可能是工整否定铺排,也可能承担辩解或悬念排除;通读语境,只在重复细纲或拖慢画面时改写。',
649
+ excerpt: compact(window.map((entry) => entry.original).join(' / ')),
650
+ });
651
+ }
652
+
653
+ return findings;
654
+ }
655
+
656
+ // 反序对比腔(实战漏网 C):「是A,不是B」。排除基建复用 not-is-comparison:
657
+ // 引号内剥离、「是的/是啊」确认语;前字合成词与反问尾巴见 REVERSE_NOT_IS_PREV_EXCLUDE 注释。
658
+ function findReverseNotIs(proseLines) {
659
+ const findings = [];
660
+
661
+ for (const { text, lineNo } of proseLines) {
662
+ const trimmed = text.trim();
663
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
664
+ const masked = maskQuoted(text);
665
+ REVERSE_NOT_IS_PATTERN.lastIndex = 0;
666
+ let match;
667
+ while ((match = REVERSE_NOT_IS_PATTERN.exec(masked)) !== null) {
668
+ const start = match.index;
669
+ // 「就是/也是/还是/只是/可是…」里的「是」是合成词一部分,不是肯定项系动词。
670
+ if (REVERSE_NOT_IS_PREV_EXCLUDE.has(masked[start - 1])) continue;
671
+ // 「是不是…」问句起头。
672
+ if (masked[start + 1] === '不') continue;
673
+ // 「是的,…不是…」承接确认语(复用 not-is 的判定)。
674
+ if (isAffirmationTagAt(masked, start)) continue;
675
+ // 「…,不是吗/不是么/不是吧」反问尾巴。
676
+ if (/^[吗么吧]/.test(match[2])) continue;
677
+ findings.push({
678
+ line: lineNo,
679
+ column: start + 1,
680
+ type: 'reverse-not-is',
681
+ severity: 'blocking',
682
+ message: '反序对比腔:「是A,不是B」与「不是A,是B」同族;删掉后置否定,直接写 A 的具体表现,或用细节让读者自己对比。',
683
+ excerpt: compact(text.slice(start, start + match[0].length)),
684
+ });
685
+ }
686
+ }
687
+
688
+ return findings;
689
+ }
690
+
691
+ // 预告式总结收尾(实战漏网 D):只扫文末窗口。从文末往回收集叙述行,
692
+ // 直到剥引号后的可见字数达到窗口大小(按行取整,边界行整行计入)。
693
+ function findTrailerEnding(proseLines) {
694
+ const windowLines = [];
695
+ let accumulated = 0;
696
+
697
+ for (let i = proseLines.length - 1; i >= 0 && accumulated < TRAILER_ENDING_WINDOW_CHARS; i -= 1) {
698
+ const { text } = proseLines[i];
699
+ const trimmed = text.trim();
700
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
701
+ windowLines.unshift(proseLines[i]);
702
+ accumulated += visibleLength(stripQuoted(trimmed));
703
+ }
704
+
705
+ const findings = [];
706
+ for (const { text, lineNo } of windowLines) {
707
+ const masked = maskQuoted(text);
708
+ TRAILER_ENDING_PATTERN.lastIndex = 0;
709
+ let match;
710
+ while ((match = TRAILER_ENDING_PATTERN.exec(masked)) !== null) {
711
+ findings.push({
712
+ line: lineNo,
713
+ column: match.index + 1,
714
+ type: 'trailer-ending',
715
+ severity: 'blocking',
716
+ message: '预告式总结收尾:「没人知道/才刚刚开始/正朝着…压了过去」是 AI 章尾预告腔;结尾停在具体动作、画面或一句台词上,悬念让事件自己挂住,别替读者预告下一章。',
717
+ excerpt: compact(text.slice(match.index, match.index + match[0].length)),
718
+ });
719
+ }
720
+ TRAILER_SUMMARY_PATTERN.lastIndex = 0;
721
+ let summaryMatch;
722
+ while ((summaryMatch = TRAILER_SUMMARY_PATTERN.exec(masked)) !== null) {
723
+ findings.push({
724
+ line: lineNo,
725
+ column: summaryMatch.index + 1,
726
+ type: 'trailer-summary',
727
+ severity: 'blocking',
728
+ message: '章尾状态总结体:「这一夜注定…/这一切都结束了/新的人生才刚刚开始/命运的齿轮」是把细纲的结尾设定原样写成了总结句;结尾设定要落成最后一个具体动作、画面或台词,别替读者盖章。',
729
+ excerpt: compact(text.slice(summaryMatch.index, summaryMatch.index + summaryMatch[0].length)),
730
+ });
731
+ }
732
+ }
733
+
734
+ return findings;
735
+ }
736
+
737
+ // 引号强调滥用(实战漏网 E):统计叙述层 1-4 字成对引号强调片段,全文只报一条
738
+ // (密度型分布指纹)。台词类排除见 QUOTE_EMPHASIS_* 常量注释。
739
+ function findQuoteEmphasisTic(proseLines) {
740
+ let hits = 0;
741
+ let firstLine = null;
742
+ const samples = [];
743
+
744
+ for (const { text, lineNo } of proseLines) {
745
+ const trimmed = text.trim();
746
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
747
+ // 引号外没有叙述的行(独立台词/弹幕流/拟声词连发「“叮咚~”“叮咚~”」)整行跳过:
748
+ // 强调滥用是叙述层指纹,没有叙述就无所谓强调。
749
+ if (visibleLength(stripQuoted(trimmed)) === 0) continue;
750
+ const ranges = quotedRanges(text);
751
+
752
+ for (const [start, end] of ranges) {
753
+ if (text[start] === '【') continue; // 系统面板/公告载体,不是强调引号
754
+ // 引号套引号:台词内部的强调属于角色语言,不算叙述层强调滥用。
755
+ if (ranges.some(([s2, e2]) => s2 <= start && end <= e2 && (s2 !== start || e2 !== end))) continue;
756
+ const inner = text.slice(start + 1, end - 1);
757
+ const visible = visibleLength(inner);
758
+ if (visible < 1 || visible > QUOTE_EMPHASIS_MAX_VISIBLE) continue;
759
+ if (/[。!?!?…,,;;::]/.test(inner)) continue; // 含句读的是台词/播报,不是强调
760
+ const before = text.slice(Math.max(0, start - 6), start);
761
+ const after = text.slice(end, end + 3);
762
+ if (QUOTE_EMPHASIS_SPEECH_VERB_PATTERN.test(before) || QUOTE_EMPHASIS_SPEECH_VERB_PATTERN.test(after)) continue; // 引语动词邻接=极短台词
763
+ hits += 1;
764
+ if (firstLine === null) firstLine = lineNo;
765
+ if (samples.length < 6 && !samples.includes(inner)) samples.push(inner);
766
+ }
767
+ }
768
+
769
+ if (hits < QUOTE_EMPHASIS_MIN_HITS) return [];
770
+
771
+ return [{
772
+ line: firstLine,
773
+ column: 1,
774
+ type: 'quote-emphasis-tic',
775
+ severity: 'advisory',
776
+ message: `引号强调滥用:叙述里 1-4 字短词加引号强调 ${hits} 处;只留真正反讽/转述必要的一两处,其余去掉引号直接写,或换成具体动作让读者自己品。`,
777
+ excerpt: compact(samples.join(' ')),
778
+ }];
779
+ }
780
+
781
+ // 否定对偶:统计引号外叙述里「没A,也没B」式句子,全文只报一条。
782
+ function findNegationPairTic(proseLines) {
783
+ let hits = 0;
784
+ let firstLine = null;
785
+ const samples = [];
786
+ for (const { text, lineNo } of proseLines) {
787
+ const trimmed = text.trim();
788
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
789
+ const narrative = stripQuoted(trimmed);
790
+ NEGATION_PAIR_PATTERN.lastIndex = 0;
791
+ let match;
792
+ while ((match = NEGATION_PAIR_PATTERN.exec(narrative)) !== null) {
793
+ hits += 1;
794
+ if (firstLine === null) firstLine = lineNo;
795
+ if (samples.length < 4) samples.push(match[0]);
796
+ }
797
+ }
798
+ if (hits < NEGATION_PAIR_MIN_HITS) return [];
799
+ return [{
800
+ line: firstLine,
801
+ column: 1,
802
+ type: 'negation-pair-tic',
803
+ severity: 'advisory',
804
+ message: `否定对偶偏多:「没A,也没B」式叙述 ${hits} 处;写人物没做什么是英文式节奏,改成他做了什么(留一两处真正要强调的)。`,
805
+ excerpt: compact(samples.join(' | ')),
806
+ }];
807
+ }
808
+
809
+ // 微动作复读:统计引号外叙述里「了X量词」轻量补语的密度。次数与每千字密度双门槛,
810
+ // 全文只报一条(这是分布级指纹,不是逐处问题)。
811
+ function findMicroActionTic(proseLines) {
812
+ let hits = 0;
813
+ let narrativeChars = 0;
814
+ let firstLine = null;
815
+ const samples = [];
816
+
817
+ for (const { text, lineNo } of proseLines) {
818
+ const trimmed = text.trim();
819
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
820
+ const narrative = stripQuoted(trimmed);
821
+ narrativeChars += visibleLength(narrative);
822
+ MICRO_TIC_PATTERN.lastIndex = 0;
823
+ let match;
824
+ while ((match = MICRO_TIC_PATTERN.exec(narrative)) !== null) {
825
+ hits += 1;
826
+ if (firstLine === null) firstLine = lineNo;
827
+ if (samples.length < 6 && !samples.includes(match[0])) samples.push(match[0]);
828
+ }
829
+ }
830
+
831
+ if (narrativeChars === 0 || hits < MICRO_TIC_MIN_HITS) return [];
832
+ const perKilo = (hits / narrativeChars) * 1000;
833
+ if (perKilo < MICRO_TIC_PER_KILO) return [];
834
+
835
+ return [{
836
+ line: firstLine,
837
+ column: 1,
838
+ type: 'micro-action-tic',
839
+ severity: 'advisory',
840
+ message: `微动作复读:「了下/了一下」式轻量补语 ${hits} 处(${perKilo.toFixed(1)}/千字);同一反应模板高密度复现是机械指纹,合并动作 beat、换具体细节,别每个动作都补一个轻反应尾巴。`,
841
+ excerpt: compact(samples.join(' ')),
842
+ }];
843
+ }
844
+
845
+ // 套式反应细节:统计引号外叙述中通用的部位/声线反应与固定语气比喻。
846
+ // 这是删除测试的候选集,不是身体描写黑名单;全篇只报一条,保留有动作后果、
847
+ // 伤势、人物习惯或情节功能的细节。
848
+ function findStockReactionTic(proseLines) {
849
+ let hits = 0;
850
+ let narrativeChars = 0;
851
+ let firstLine = null;
852
+ const samples = [];
853
+
854
+ for (const { text, lineNo } of proseLines) {
855
+ const trimmed = text.trim();
856
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
857
+ const narrative = stripQuoted(trimmed);
858
+ narrativeChars += visibleLength(narrative);
859
+
860
+ for (const pattern of STOCK_REACTION_PATTERNS) {
861
+ pattern.lastIndex = 0;
862
+ let match;
863
+ while ((match = pattern.exec(narrative)) !== null) {
864
+ hits += 1;
865
+ if (firstLine === null) firstLine = lineNo;
866
+ const sample = sentenceAround(narrative, match.index);
867
+ if (samples.length < 6 && sample && !samples.includes(sample)) samples.push(sample);
868
+ }
869
+ }
870
+ }
871
+
872
+ if (narrativeChars === 0 || hits < STOCK_REACTION_MIN_HITS) return [];
873
+ const perKilo = (hits / narrativeChars) * 1000;
874
+ if (perKilo < STOCK_REACTION_PER_KILO) return [];
875
+
876
+ return [{
877
+ line: firstLine,
878
+ column: 1,
879
+ type: 'stock-reaction-tic',
880
+ severity: 'advisory',
881
+ message: `套式反应细节:指尖/指节/喉结/眼圈/声音放轻等通用反应或“平静得像在念”式语气比喻 ${hits} 处(${perKilo.toFixed(1)}/千字);逐处做删除测试,只标注情绪、不改变选择、关系、物件或动作结果的删掉,不要换部位或同义动作。`,
882
+ excerpt: compact(samples.join(' | ')),
883
+ }];
884
+ }
885
+
886
+ function findActionListTic(proseLines) {
887
+ const findings = [];
888
+
889
+ for (const { text, lineNo } of proseLines) {
890
+ const trimmed = text.trim();
891
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
892
+ const narrative = stripQuoted(trimmed).trim();
893
+ if (!narrative) continue;
894
+
895
+ ACTION_LIST_VERB_PATTERN.lastIndex = 0;
896
+ const verbs = [];
897
+ let match;
898
+ while ((match = ACTION_LIST_VERB_PATTERN.exec(narrative)) !== null) {
899
+ verbs.push(match[0]);
900
+ }
901
+
902
+ if (verbs.length < ACTION_LIST_MIN_HITS) continue;
903
+ const separators = (narrative.match(/[,、;;]/g) || []).length;
904
+ if (separators < ACTION_LIST_MIN_SEPARATORS) continue;
905
+
906
+ findings.push({
907
+ line: lineNo,
908
+ column: 1,
909
+ type: 'action-list-tic',
910
+ severity: 'advisory',
911
+ message: `监控摄像头式动作清单:同段连续动作动词 ${verbs.length} 个、分隔符 ${separators} 个;合并琐碎步骤,只保留有情绪/情节功能的动作,必要时用角色犹豫、误判或环境反馈做缓冲。`,
912
+ excerpt: compact(verbs.slice(0, 8).join(' ')),
913
+ });
914
+ }
915
+
916
+ return findings;
917
+ }
918
+
919
+ // 套词密度:统计引号外叙述中的高危禁用词聚集。不是逐词替换器;只在密度高到
920
+ // 形成模板腔时提示,修法是删总结、换具体动作/物件/对话,不是同义词轮换。
921
+ function findClicheDensityTic(proseLines) {
922
+ let hits = 0;
923
+ let narrativeChars = 0;
924
+ let firstLine = null;
925
+ const samples = [];
926
+
927
+ for (const { text, lineNo } of proseLines) {
928
+ const trimmed = text.trim();
929
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
930
+ const narrative = stripQuoted(trimmed);
931
+ narrativeChars += visibleLength(narrative);
932
+
933
+ for (const pattern of CLICHE_PATTERNS) {
934
+ pattern.lastIndex = 0;
935
+ let match;
936
+ while ((match = pattern.exec(narrative)) !== null) {
937
+ hits += 1;
938
+ if (firstLine === null) firstLine = lineNo;
939
+ if (samples.length < 8 && !samples.includes(match[0])) samples.push(match[0]);
940
+ }
941
+ }
942
+ }
943
+
944
+ if (narrativeChars === 0 || hits < CLICHE_DENSITY_MIN_HITS) return [];
945
+ const perKilo = (hits / narrativeChars) * 1000;
946
+ if (perKilo < CLICHE_DENSITY_PER_KILO) return [];
947
+
948
+ return [{
949
+ line: firstLine,
950
+ column: 1,
951
+ type: 'cliche-density-tic',
952
+ severity: 'advisory',
953
+ message: `套词密度过高:高危 AI 套词 ${hits} 处(${perKilo.toFixed(1)}/千字);不要同义词轮换,改成角色当下可见的动作、物件、对话和具体后果。`,
954
+ excerpt: compact(samples.join(' ')),
955
+ }];
956
+ }
957
+
958
+ // 比喻密度:统计引号外叙述中“像/好像/仿佛/如同”等比喻标记。
959
+ // 单个比喻不是问题;高密度成片时才提示,避免把文本改成另一种修辞模板。
960
+ function findMetaphorDensityTic(proseLines) {
961
+ let hits = 0;
962
+ let narrativeChars = 0;
963
+ let firstLine = null;
964
+ const samples = [];
965
+
966
+ for (const { text, lineNo } of proseLines) {
967
+ const trimmed = text.trim();
968
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
969
+ const narrative = stripQuoted(trimmed);
970
+ narrativeChars += visibleLength(narrative);
971
+
972
+ METAPHOR_MARKER_PATTERN.lastIndex = 0;
973
+ let match;
974
+ while ((match = METAPHOR_MARKER_PATTERN.exec(narrative)) !== null) {
975
+ hits += 1;
976
+ if (firstLine === null) firstLine = lineNo;
977
+ const sample = sentenceAround(narrative, match.index);
978
+ if (samples.length < 6 && sample && !samples.includes(sample)) samples.push(sample);
979
+ }
980
+
981
+ METAPHOR_LIKE_PHRASE_PATTERN.lastIndex = 0;
982
+ while ((match = METAPHOR_LIKE_PHRASE_PATTERN.exec(narrative)) !== null) {
983
+ const prefix = narrative.slice(Math.max(0, match.index - 8), match.index);
984
+ if (/好像|像是|像|仿佛|宛如|如同|犹如/.test(prefix)) continue;
985
+ hits += 1;
986
+ if (firstLine === null) firstLine = lineNo;
987
+ const sample = sentenceAround(narrative, match.index);
988
+ if (samples.length < 6 && sample && !samples.includes(sample)) samples.push(sample);
989
+ }
990
+ }
991
+
992
+ if (narrativeChars === 0 || hits < METAPHOR_DENSITY_MIN_HITS) return [];
993
+ const perKilo = (hits / narrativeChars) * 1000;
994
+ if (perKilo < METAPHOR_DENSITY_PER_KILO) return [];
995
+
996
+ return [{
997
+ line: firstLine,
998
+ column: 1,
999
+ type: 'metaphor-density-tic',
1000
+ severity: 'advisory',
1001
+ message: `比喻密度过高:像/好像/仿佛/如同等比喻标记 ${hits} 处(${perKilo.toFixed(1)}/千字);保留最有叙事功能的少数比喻,其余回到具体动作、物件、声音或后果,不要换成新比喻。`,
1002
+ excerpt: compact(samples.join(' | ')),
1003
+ }];
1004
+ }
1005
+
1006
+ // 解释链密度:统计引号外叙述中“知道/明白/这意味着/必须需要”等判断链。
1007
+ // 全篇只报一条;修法不是补结构虚词,而是把判断落到动作、物件、对话和现场反馈。
1008
+ function findReasoningChainTic(proseLines) {
1009
+ let hits = 0;
1010
+ let coreHits = 0;
1011
+ let narrativeChars = 0;
1012
+ let firstLine = null;
1013
+ const samples = [];
1014
+ const buckets = new Set();
1015
+
1016
+ for (const { text, lineNo } of proseLines) {
1017
+ const trimmed = text.trim();
1018
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
1019
+ const narrative = stripQuoted(trimmed);
1020
+ narrativeChars += visibleLength(narrative);
1021
+
1022
+ for (const { pattern, key, core } of REASONING_CHAIN_PATTERNS) {
1023
+ pattern.lastIndex = 0;
1024
+ let match;
1025
+ while ((match = pattern.exec(narrative)) !== null) {
1026
+ hits += 1;
1027
+ if (core) coreHits += 1;
1028
+ buckets.add(key);
1029
+ if (firstLine === null) firstLine = lineNo;
1030
+ const sample = compact(match[0]);
1031
+ if (samples.length < 8 && !samples.includes(sample)) samples.push(sample);
1032
+ }
1033
+ }
1034
+ }
1035
+
1036
+ if (narrativeChars === 0 || hits < REASONING_CHAIN_MIN_HITS) return [];
1037
+ if (coreHits < REASONING_CHAIN_CORE_MIN_HITS || buckets.size < REASONING_CHAIN_MIN_BUCKETS) return [];
1038
+ const perKilo = (hits / narrativeChars) * 1000;
1039
+ if (perKilo < REASONING_CHAIN_PER_KILO) return [];
1040
+
1041
+ return [{
1042
+ line: firstLine,
1043
+ column: 1,
1044
+ type: 'reasoning-chain-tic',
1045
+ severity: 'advisory',
1046
+ message: `解释链密度过高:知道/明白/这意味着/必须/需要等判断链 ${hits} 处(${perKilo.toFixed(1)}/千字);像逻辑报告时,把判断落到角色当下可见的动作、物件、对话和现场反馈。`,
1047
+ excerpt: compact(samples.join(' | ')),
1048
+ }];
1049
+ }
1050
+
1051
+ // 系统/规则行如果连续像 API 文档或政府公文,读者容易闻到机器味。
1052
+ // 修法不是删除规则,而是保留功能后把一部分硬词改成白话或具体后果。
1053
+ function findNoticeFormalityTic(proseLines) {
1054
+ let hits = 0;
1055
+ let noticeChars = 0;
1056
+ let noticeLines = 0;
1057
+ let coreHits = 0;
1058
+ let firstLine = null;
1059
+ const samples = [];
1060
+
1061
+ for (const { text, lineNo } of proseLines) {
1062
+ const trimmed = text.trim();
1063
+ if (!/^【[^】]+】$/.test(trimmed)) continue;
1064
+ noticeLines += 1;
1065
+ noticeChars += visibleLength(trimmed);
1066
+
1067
+ NOTICE_FORMAL_CORE_PATTERN.lastIndex = 0;
1068
+ while (NOTICE_FORMAL_CORE_PATTERN.exec(trimmed) !== null) coreHits += 1;
1069
+
1070
+ for (const pattern of NOTICE_FORMAL_PATTERNS) {
1071
+ pattern.lastIndex = 0;
1072
+ let match;
1073
+ while ((match = pattern.exec(trimmed)) !== null) {
1074
+ hits += 1;
1075
+ if (firstLine === null) firstLine = lineNo;
1076
+ const sample = compact(match[0]);
1077
+ if (samples.length < 8 && !samples.includes(sample)) samples.push(sample);
1078
+ }
1079
+ }
1080
+ }
1081
+
1082
+ if (noticeLines < NOTICE_FORMAL_MIN_LINES || noticeChars === 0 || hits < NOTICE_FORMAL_MIN_HITS || coreHits < NOTICE_FORMAL_CORE_MIN_HITS) return [];
1083
+ const perKilo = (hits / noticeChars) * 1000;
1084
+ if (perKilo < NOTICE_FORMAL_PER_KILO) return [];
1085
+
1086
+ return [{
1087
+ line: firstLine,
1088
+ column: 1,
1089
+ type: 'system-notice-formality-tic',
1090
+ severity: 'advisory',
1091
+ message: `系统公告公文腔过密:方括号规则行中硬规则词 ${hits} 处(${perKilo.toFixed(1)}/千字);保留为角色看见的屏幕/公告/规则载体,只在载体内部白话化部分硬词,或补角色当场看懂的具体后果,不改成叙述者解释。`,
1092
+ excerpt: compact(samples.join(' | ')),
1093
+ }];
1094
+ }
1095
+
1096
+ // 长文本整体过于“精炼”:短段很多、自然连接偏少,读起来像处理过的梗概/分镜表。
1097
+ // 修法是通读后补断裂处,不是为凑阈值全局加“的/了/就”。
1098
+ function findOvercompressedProseTic(proseLines) {
1099
+ let narrativeChars = 0;
1100
+ let narrativeParas = 0;
1101
+ let shortParas = 0;
1102
+ let particles = 0;
1103
+ let firstLine = null;
1104
+ const samples = [];
1105
+
1106
+ for (const { text, lineNo } of proseLines) {
1107
+ const trimmed = text.trim();
1108
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed) || /^【[^】]+】$/.test(trimmed)) continue;
1109
+ const narrative = stripQuoted(trimmed).trim();
1110
+ const len = visibleLength(narrative);
1111
+ if (len === 0) continue;
1112
+
1113
+ if (firstLine === null) firstLine = lineNo;
1114
+ narrativeParas += 1;
1115
+ narrativeChars += len;
1116
+ if (len <= OVERCOMPRESSED_PROSE_SHORT_MAX_CHARS) {
1117
+ shortParas += 1;
1118
+ if (samples.length < 6) samples.push(narrative);
1119
+ }
1120
+
1121
+ OVERCOMPRESSED_PROSE_PARTICLE_PATTERN.lastIndex = 0;
1122
+ while (OVERCOMPRESSED_PROSE_PARTICLE_PATTERN.exec(narrative) !== null) particles += 1;
1123
+ }
1124
+
1125
+ if (narrativeChars < OVERCOMPRESSED_PROSE_MIN_CHARS || narrativeParas < OVERCOMPRESSED_PROSE_MIN_PARAS) return [];
1126
+ const shortRatio = shortParas / narrativeParas;
1127
+ if (shortRatio < OVERCOMPRESSED_PROSE_SHORT_RATIO) return [];
1128
+ const particlePerKilo = (particles / narrativeChars) * 1000;
1129
+ if (particlePerKilo >= OVERCOMPRESSED_PROSE_PARTICLE_PER_KILO) return [];
1130
+
1131
+ return [{
1132
+ line: firstLine,
1133
+ column: 1,
1134
+ type: 'overcompressed-prose-tic',
1135
+ severity: 'advisory',
1136
+ message: `过度精炼短段:叙述段 ${narrativeParas} 个,其中 ${shortParas} 个≤${OVERCOMPRESSED_PROSE_SHORT_MAX_CHARS}字(${(shortRatio * 100).toFixed(0)}%),自然连接 ${particlePerKilo.toFixed(1)}/千字偏少;先通读判断,确有提纲感再补断裂处和必要结构虚词,有意短镜头可留,别机械注水。`,
1137
+ excerpt: compact(samples.join(' | ')),
1138
+ }];
1139
+
1140
+ }
1141
+
1142
+ // 低连接密度:长文本/中短窗口里,引号外叙述的功能词和白话连接同时偏低,且缺少中长承接句,
1143
+ // 会呈现“提纲/电报体”分布。修法是恢复必要连接和句群,不是全局补词。
1144
+ function findLowConnectiveDensityTic(proseLines) {
1145
+ let bodyChars = 0;
1146
+ let functionHits = 0;
1147
+ let plainHits = 0;
1148
+ let firstLine = null;
1149
+ const sentences = [];
1150
+ const samples = [];
1151
+
1152
+ for (const { text, lineNo } of proseLines) {
1153
+ const trimmed = text.trim();
1154
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
1155
+
1156
+ // 只看引号外叙述。台词/弹幕/系统播报可以天然短促,混入统计会把体裁特征误当电报体。
1157
+ const narrative = stripQuoted(trimmed).trim();
1158
+ const narrativeLen = visibleLength(narrative);
1159
+ if (narrativeLen === 0) continue;
1160
+
1161
+ if (firstLine === null) firstLine = lineNo;
1162
+ bodyChars += narrativeLen;
1163
+ functionHits += countTerms(narrative, LOW_CONNECTIVE_FUNCTION_TERMS);
1164
+ plainHits += countTerms(narrative, LOW_CONNECTIVE_PLAIN_TERMS);
1165
+
1166
+ for (const sentence of splitSentences(narrative)) {
1167
+ const len = visibleLength(sentence);
1168
+ if (len === 0) continue;
1169
+ sentences.push(len);
1170
+ if (len <= 12 && samples.length < 6) samples.push(sentence);
1171
+ }
1172
+ }
1173
+
1174
+ if (bodyChars < LOW_CONNECTIVE_MIN_CHARS || sentences.length === 0) return [];
1175
+ const functionPerKilo = (functionHits / bodyChars) * 1000;
1176
+ if (functionPerKilo >= LOW_CONNECTIVE_FUNCTION_PER_KILO) return [];
1177
+ const plainPerKilo = (plainHits / bodyChars) * 1000;
1178
+ if (plainPerKilo >= LOW_CONNECTIVE_PLAIN_PER_KILO) return [];
1179
+ const longSentenceRatio = sentences.filter((len) => len >= LOW_CONNECTIVE_LONG_SENTENCE_CHARS).length / sentences.length;
1180
+ if (longSentenceRatio >= LOW_CONNECTIVE_LONG_SENTENCE_RATIO) return [];
1181
+
1182
+ return [{
1183
+ line: firstLine,
1184
+ column: 1,
1185
+ type: 'low-connective-density-tic',
1186
+ severity: 'advisory',
1187
+ message: `低连接密度:引号外叙述功能词 ${functionPerKilo.toFixed(1)}/千字、白话连接 ${plainPerKilo.toFixed(1)}/千字,且≥${LOW_CONNECTIVE_LONG_SENTENCE_CHARS}字承接句仅 ${(longSentenceRatio * 100).toFixed(0)}%;容易像提纲/电报体。通读后补必要连接和中长句群,别机械注水。`,
1188
+ excerpt: compact(samples.join(' | ')),
1189
+ }];
1190
+ }
1191
+
1192
+ // 抽象总结复读:统计引号外叙述中的高抽象收束模板。全篇只报一条,提醒回到角色
1193
+ // 当下可见的文件、动作、对话或物理后果;不要用命运大词替读者总结。
1194
+ function findAbstractSummaryTic(proseLines) {
1195
+ let hits = 0;
1196
+ let narrativeChars = 0;
1197
+ let firstLine = null;
1198
+ const samples = [];
1199
+
1200
+ for (const { text, lineNo } of proseLines) {
1201
+ const trimmed = text.trim();
1202
+ if (!trimmed || isDivider(trimmed) || isStructural(trimmed)) continue;
1203
+ const narrative = stripQuoted(trimmed);
1204
+ narrativeChars += visibleLength(narrative);
1205
+
1206
+ for (const pattern of ABSTRACT_SUMMARY_PATTERNS) {
1207
+ pattern.lastIndex = 0;
1208
+ let match;
1209
+ while ((match = pattern.exec(narrative)) !== null) {
1210
+ hits += 1;
1211
+ if (firstLine === null) firstLine = lineNo;
1212
+ const sample = compact(match[0]);
1213
+ if (samples.length < 6 && !samples.includes(sample)) samples.push(sample);
1214
+ }
1215
+ }
1216
+ }
1217
+
1218
+ if (narrativeChars === 0 || hits < ABSTRACT_SUMMARY_MIN_HITS) return [];
1219
+ const perKilo = (hits / narrativeChars) * 1000;
1220
+ if (perKilo < ABSTRACT_SUMMARY_PER_KILO) return [];
1221
+
1222
+ return [{
1223
+ line: firstLine,
1224
+ column: 1,
1225
+ type: 'abstract-summary-tic',
1226
+ severity: 'advisory',
1227
+ message: `抽象总结复读:命运/棋局/这一刻终于明白/才刚刚开始等作者总结 ${hits} 处(${perKilo.toFixed(1)}/千字);回到角色当下可见的文件、动作、对话或物理后果,别替读者盖章。`,
1228
+ excerpt: compact(samples.join(' | ')),
1229
+ }];
1230
+ }
1231
+
1232
+ function findPeriodStutter(proseLines) {
1233
+ const findings = [];
1234
+ let runLen = 0;
1235
+ let runStartLine = null;
1236
+ let runSample = [];
1237
+
1238
+ const flush = () => {
1239
+ if (runLen >= STUTTER_MIN_RUN) {
1240
+ findings.push({
1241
+ line: runStartLine,
1242
+ column: 1,
1243
+ type: 'period-stutter',
1244
+ severity: 'advisory',
1245
+ message: `碎句号:连续 ${runLen} 个短句无呼吸;按目标句长把碎句合并成中长句、补回画面与连接(见本 skill 句长/疏密节奏规则)。`,
1246
+ excerpt: compact(runSample.join(' ')),
1247
+ });
1248
+ }
1249
+ runLen = 0;
1250
+ runStartLine = null;
1251
+ runSample = [];
1252
+ };
1253
+
1254
+ for (const { text, lineNo } of proseLines) {
1255
+ const trimmed = text.trim();
1256
+ if (!trimmed) continue; // 空行是一句一段排版,不打断叙述连贯
1257
+ if (isDivider(trimmed) || isStructural(trimmed)) {
1258
+ flush(); // 分隔线/markdown 结构行:重置碎句计数
1259
+ continue;
1260
+ }
1261
+ const narrative = stripQuoted(trimmed);
1262
+ if (visibleLength(narrative) === 0) {
1263
+ flush(); // 纯对话/弹幕/系统播报:成片短句是正常形态,重置碎句计数
1264
+ continue;
1265
+ }
1266
+ // 只数引号外叙述句:混合行(叙述+引号内物件/短台词)的引号外片段仍参与碎句计数。
1267
+ for (const sentence of splitSentences(narrative)) {
1268
+ if (visibleLength(sentence) <= STUTTER_MAX_SENTENCE) {
1269
+ if (runLen === 0) runStartLine = lineNo;
1270
+ runLen += 1;
1271
+ if (runSample.length < 6) runSample.push(sentence);
1272
+ } else {
1273
+ flush();
1274
+ }
1275
+ }
1276
+ }
1277
+ flush();
1278
+ return findings;
1279
+ }
1280
+
1281
+ function isDivider(trimmed) {
1282
+ return /^-{3,}$/.test(trimmed) || /^[*_]{3,}$/.test(trimmed);
1283
+ }
1284
+
1285
+ // markdown 结构行(标题/列表/引用/表格)不是叙述正文,长段落/碎句号/破折号检测都跳过。
1286
+ function isStructural(trimmed) {
1287
+ return /^(#{1,6}\s|>\s?|[-*+]\s|\d+[.)]\s|\|)/.test(trimmed)
1288
+ || /^第[零一二三四五六七八九十百千万\d]+章(?:\s|_|$)/.test(trimmed);
1289
+ }
1290
+
1291
+ // 去掉成对引号内的片段(台词/系统播报),只留引号外叙述。碎句号判定用:纯对话/弹幕成片短句
1292
+ // 是体裁正常形态(豁免),但「叙述 + 引号内物件/短台词」混合行的引号外叙述仍要参与短句计数。
1293
+ function stripQuoted(text) {
1294
+ let out = text;
1295
+ for (const src of QUOTE_SOURCES) out = out.replace(new RegExp(src, 'g'), '');
1296
+ return out;
1297
+ }
1298
+
1299
+ // 把成对引号片段(含引号)替换为等长问号占位:既豁免引号内台词/播报,又保住原文
1300
+ // 偏移量,供逐处 blocking 规则定位与截取原文摘录(stripQuoted 会移位,不适合定位)。
1301
+ // 占位字符用「?」而不是「。」:占位既要截断各规则的 [^。!?!?…] 否定类(?与句号在每条
1302
+ // 规则的否定类里等效),又不能落在任何规则的接受位。句号占位会替 trailer-summary 的句末
1303
+ // [。!] 伪造出终止符,让「这一战注定是「血屠」的开端,…」这类引号里放代号/绰号的叙述行
1304
+ // 被误报,且报出的『这一战注定是。』在原文里 grep 不到。占位长度不变,故偏移与摘录窗口不漂移。
1305
+ function maskQuoted(text) {
1306
+ let out = text;
1307
+ for (const src of QUOTE_SOURCES) {
1308
+ out = out.replace(new RegExp(src, 'g'), (m) => '?'.repeat(m.length));
1309
+ }
1310
+ return out;
1311
+ }
1312
+
1313
+ // 返回引号内片段(含引号本身)的 [start, end) 区间,供 not-is 对比句豁免台词用。
1314
+ function quotedRanges(text) {
1315
+ const ranges = [];
1316
+ for (const src of QUOTE_SOURCES) {
1317
+ const re = new RegExp(src, 'g');
1318
+ let match;
1319
+ while ((match = re.exec(text)) !== null) ranges.push([match.index, match.index + match[0].length]);
1320
+ }
1321
+ return ranges;
1322
+ }
1323
+
1324
+ function insideRanges(pos, ranges) {
1325
+ return ranges.some(([start, end]) => pos >= start && pos < end);
1326
+ }
1327
+
1328
+ function splitSentences(trimmed) {
1329
+ return trimmed
1330
+ .split(/[。!?!?]/)
1331
+ .map((s) => s.trim())
1332
+ .filter(Boolean);
1333
+ }
1334
+
1335
+ function sentenceAround(text, index) {
1336
+ let start = index;
1337
+ while (start > 0 && !STOP_CHARS.has(text[start - 1])) start -= 1;
1338
+ let end = index;
1339
+ while (end < text.length && !STOP_CHARS.has(text[end])) end += 1;
1340
+ return compact(text.slice(start, end).trim());
1341
+ }
1342
+
1343
+ function visibleLength(sentence) {
1344
+ const matched = sentence.match(/[一-鿿A-zA-Za-z0-9]/g);
1345
+ return matched ? matched.length : 0;
1346
+ }
1347
+
1348
+ function countTerms(text, terms) {
1349
+ let count = 0;
1350
+ for (const term of terms) {
1351
+ let index = text.indexOf(term);
1352
+ while (index !== -1) {
1353
+ count += 1;
1354
+ index = text.indexOf(term, index + term.length);
1355
+ }
1356
+ }
1357
+ return count;
1358
+ }
1359
+
1360
+ // 把行内 HTML 注释(含 `<!--`、`-->` 本身)换成等长空格;openBefore 表示上一行留下未闭合注释。
1361
+ // unclosedOpenings 里的 `<!--` 到文末都没闭合:只抹掉这四个字符,后面照常当正文。
1362
+ // openedAt 是本行最后一个开始注释的 `<!--` 位置("行号:列号"),没有则为 null。
1363
+ function maskHtmlComments(line, openBefore, lineIndex = 0, unclosedOpenings = new Set()) {
1364
+ let out = '';
1365
+ let open = openBefore;
1366
+ let cursor = 0;
1367
+ let openedAt = null;
1368
+ while (cursor < line.length) {
1369
+ if (open) {
1370
+ const close = line.indexOf('-->', cursor);
1371
+ const end = close === -1 ? line.length : close + 3;
1372
+ out += ' '.repeat(end - cursor);
1373
+ cursor = end;
1374
+ if (close !== -1) open = false;
1375
+ continue;
1376
+ }
1377
+ const start = line.indexOf('<!--', cursor);
1378
+ if (start === -1) {
1379
+ out += line.slice(cursor);
1380
+ break;
1381
+ }
1382
+ out += line.slice(cursor, start) + ' ';
1383
+ cursor = start + 4;
1384
+ const key = `${lineIndex}:${start}`;
1385
+ if (unclosedOpenings.has(key)) continue;
1386
+ open = true;
1387
+ openedAt = key;
1388
+ }
1389
+ return { text: out, open, openedAt };
1390
+ }
1391
+
1392
+ function parseFenceMarker(trimmedLine) {
1393
+ const match = /^(?:`{3,}|~{3,})/.exec(trimmedLine);
1394
+ if (!match) return null;
1395
+ return { char: match[0][0], length: match[0].length };
1396
+ }
1397
+
1398
+ function hasYamlFrontMatter(lines) {
1399
+ if (!lines[0] || lines[0].trim() !== '---') return false;
1400
+ let sawYamlField = false;
1401
+ for (let i = 1; i < Math.min(lines.length, 40); i += 1) {
1402
+ const trimmed = lines[i].trim();
1403
+ if (trimmed === '---') return sawYamlField;
1404
+ if (/^[A-Za-z0-9_-]+:\s*/.test(trimmed)) sawYamlField = true;
1405
+ }
1406
+ return false;
1407
+ }
1408
+
1409
+ function scanBlock(block) {
1410
+ const text = block.map((entry) => entry.text).join('\n');
1411
+ const lineStarts = [];
1412
+ let cursor = 0;
1413
+
1414
+ for (const entry of block) {
1415
+ lineStarts.push({ offset: cursor, lineNo: entry.lineNo });
1416
+ cursor += entry.text.length + 1;
1417
+ }
1418
+
1419
+ return findNotIsComparisons(text, (offset) => positionForOffset(lineStarts, offset));
1420
+ }
1421
+
1422
+ function positionForOffset(lineStarts, offset) {
1423
+ let low = 0;
1424
+ let high = lineStarts.length - 1;
1425
+
1426
+ while (low <= high) {
1427
+ const mid = Math.floor((low + high) / 2);
1428
+ const current = lineStarts[mid];
1429
+ const next = lineStarts[mid + 1];
1430
+
1431
+ if (offset < current.offset) {
1432
+ high = mid - 1;
1433
+ } else if (next && offset >= next.offset) {
1434
+ low = mid + 1;
1435
+ } else {
1436
+ return {
1437
+ line: current.lineNo,
1438
+ column: offset - current.offset + 1,
1439
+ };
1440
+ }
1441
+ }
1442
+
1443
+ return { line: lineStarts[0].lineNo, column: 1 };
1444
+ }
1445
+
1446
+ function findNotIsComparisons(text, getPosition) {
1447
+ const findings = [];
1448
+ const quoted = quotedRanges(text);
1449
+ let offset = 0;
1450
+
1451
+ while (offset < text.length) {
1452
+ const start = text.indexOf('不是', offset);
1453
+ if (start === -1) break;
1454
+
1455
+ // 引号内是台词/系统播报:口语里「不是A,是B」是自然辩解/反问,不算叙述层 AI 对比句式
1456
+ // (与碎句号一致豁免引号内容)。
1457
+ if (insideRanges(start, quoted)) {
1458
+ offset = start + 2;
1459
+ continue;
1460
+ }
1461
+
1462
+ // Avoid the common yes/no question fragment “是不是”.
1463
+ if (start > 0 && text[start - 1] === '是') {
1464
+ offset = start + 2;
1465
+ continue;
1466
+ }
1467
+
1468
+ const candidate = text.slice(start);
1469
+ const markerEnd = findPositiveFlipEnd(candidate);
1470
+
1471
+ if (markerEnd === -1) {
1472
+ offset = start + 2;
1473
+ continue;
1474
+ }
1475
+
1476
+ const raw = trimTrailingNoise(extractFinding(candidate, markerEnd));
1477
+ if (raw.length >= 4) {
1478
+ const position = getPosition(start);
1479
+ findings.push({
1480
+ line: position.line,
1481
+ column: position.column,
1482
+ type: 'not-is-comparison',
1483
+ severity: 'blocking',
1484
+ message: '高频 AI 对比句式;删掉否定铺垫,直接写后项,或改成动作/细节呈现。',
1485
+ excerpt: compact(raw),
1486
+ });
1487
+ }
1488
+
1489
+ offset = start + Math.max(raw.length, 2);
1490
+ }
1491
+
1492
+ return findings;
1493
+ }
1494
+
1495
+ function findPositiveFlipEnd(candidate) {
1496
+ let index = 2; // after “不是”
1497
+ let scanned = 0;
1498
+ let crossedSeparator = false;
1499
+
1500
+ while (index < candidate.length && scanned <= MAX_NEGATIVE_SPAN) {
1501
+ const char = candidate[index];
1502
+
1503
+ if (startsWithAt(candidate, index, '而是')) return index + 2;
1504
+
1505
+ if (SOFT_SEPARATORS.has(char)) {
1506
+ const next = skipGap(candidate, index + 1);
1507
+ if (startsWithAt(candidate, next, '而是')) return next + 2;
1508
+ if (candidate[next] === '是' && !TAG_PARTICLES.has(candidate[next + 1]) && !isAffirmationTagAt(candidate, next)) return next + 1;
1509
+ crossedSeparator = true;
1510
+ }
1511
+
1512
+ if (HARD_SEPARATORS.has(char)) {
1513
+ const next = skipGap(candidate, index + 1);
1514
+ if (candidate[next] === '是' && !TAG_PARTICLES.has(candidate[next + 1]) && !isAffirmationTagAt(candidate, next)) return next + 1;
1515
+ if (char !== '.') break;
1516
+ crossedSeparator = true;
1517
+ }
1518
+
1519
+ if (STOP_CHARS.has(char)) break;
1520
+
1521
+ // Catch compact forms such as “不是A是B”, but only within the first clause —
1522
+ // before any separator. After a separator the trailing “是” of a conjunction
1523
+ // (只是/可是/但是/还是/于是/倒是/总是…) is part of that word, not a positive
1524
+ // copula (issue #166 false-positive class). Post-separator flips are still
1525
+ // caught when separator-adjacent (“,是”/“,而是”) by the separator branches
1526
+ // above; subject-present flips like “,他是”/“,那是” are intentionally NOT
1527
+ // caught here — there is no separator-local way to tell them from a
1528
+ // conjunction without a word list, and on a hard rescan-to-0 gate a false
1529
+ // positive (forcing a rewrite of good prose) costs more than missing this
1530
+ // rarer form. The “是” in the either-or idiom “不是A就是B / 也是B” is part of
1531
+ // the 就是/也是 conjunction, not a copula, so 就/也 are excluded too. Also never
1532
+ // treat the “是” inside a second negative fragment (“不是A,也不是B”) as the flip.
1533
+ if (char === '是' && !COMPACT_EITHER_OR_PREV.has(candidate[index - 1]) && !crossedSeparator) {
1534
+ return index + 1;
1535
+ }
1536
+
1537
+ index += 1;
1538
+ scanned += 1;
1539
+ }
1540
+
1541
+ return -1;
1542
+ }
1543
+
1544
+ function extractFinding(candidate, markerEnd) {
1545
+ let end = markerEnd;
1546
+ const limit = Math.min(candidate.length, markerEnd + MAX_POSITIVE_SPAN);
1547
+
1548
+ while (end < limit) {
1549
+ if (STOP_CHARS.has(candidate[end])) break;
1550
+ end += 1;
1551
+ }
1552
+
1553
+ return candidate.slice(0, end);
1554
+ }
1555
+
1556
+ function startsWithAt(text, index, needle) {
1557
+ return text.slice(index, index + needle.length) === needle;
1558
+ }
1559
+
1560
+ function isAffirmationTagAt(text, index) {
1561
+ if (text[index] !== '是') return false;
1562
+ const particle = text[index + 1];
1563
+ if (!AFFIRMATION_TAG_PARTICLES.has(particle)) return false;
1564
+ const boundary = text[index + 2] || '';
1565
+ return AFFIRMATION_TAG_BOUNDARY.has(boundary);
1566
+ }
1567
+
1568
+ // 跳过行内空白与换行(含空行/段落间距),停在下一个实义字符。原实现只吞一个换行,
1569
+ // 会漏掉跨空行的「不是A。(空行)是B」这类分段揭示句。
1570
+ function skipGap(text, index) {
1571
+ while (index < text.length && (isInlineSpace(text[index]) || text[index] === '\n')) index += 1;
1572
+ return index;
1573
+ }
1574
+
1575
+ function isInlineSpace(char) {
1576
+ return char === ' ' || char === '\t' || char === '\r';
1577
+ }
1578
+
1579
+ function trimTrailingNoise(text) {
1580
+ return text.replace(/[\s|))】\]]+$/u, '');
1581
+ }
1582
+
1583
+ function compact(text) {
1584
+ const normalized = text.replace(/\s+/g, ' ').trim();
1585
+ return normalized.length > 80 ? `${normalized.slice(0, 77)}...` : normalized;
1586
+ }