novelws 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (320) hide show
  1. package/CHANGELOG.md +161 -0
  2. package/LICENSE +22 -0
  3. package/README.md +372 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +50 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/check.d.ts +6 -0
  9. package/dist/commands/check.d.ts.map +1 -0
  10. package/dist/commands/check.js +32 -0
  11. package/dist/commands/check.js.map +1 -0
  12. package/dist/commands/init.d.ts +6 -0
  13. package/dist/commands/init.d.ts.map +1 -0
  14. package/dist/commands/init.js +160 -0
  15. package/dist/commands/init.js.map +1 -0
  16. package/dist/commands/plugin.d.ts +6 -0
  17. package/dist/commands/plugin.d.ts.map +1 -0
  18. package/dist/commands/plugin.js +135 -0
  19. package/dist/commands/plugin.js.map +1 -0
  20. package/dist/commands/upgrade.d.ts +6 -0
  21. package/dist/commands/upgrade.d.ts.map +1 -0
  22. package/dist/commands/upgrade.js +92 -0
  23. package/dist/commands/upgrade.js.map +1 -0
  24. package/dist/core/config.d.ts +72 -0
  25. package/dist/core/config.d.ts.map +1 -0
  26. package/dist/core/config.js +136 -0
  27. package/dist/core/config.js.map +1 -0
  28. package/dist/core/errors.d.ts +59 -0
  29. package/dist/core/errors.d.ts.map +1 -0
  30. package/dist/core/errors.js +125 -0
  31. package/dist/core/errors.js.map +1 -0
  32. package/dist/core/platform.d.ts +27 -0
  33. package/dist/core/platform.d.ts.map +1 -0
  34. package/dist/core/platform.js +75 -0
  35. package/dist/core/platform.js.map +1 -0
  36. package/dist/core/template.d.ts +35 -0
  37. package/dist/core/template.d.ts.map +1 -0
  38. package/dist/core/template.js +94 -0
  39. package/dist/core/template.js.map +1 -0
  40. package/dist/plugins/identifier.d.ts +13 -0
  41. package/dist/plugins/identifier.d.ts.map +1 -0
  42. package/dist/plugins/identifier.js +72 -0
  43. package/dist/plugins/identifier.js.map +1 -0
  44. package/dist/plugins/installers/base.d.ts +27 -0
  45. package/dist/plugins/installers/base.d.ts.map +1 -0
  46. package/dist/plugins/installers/base.js +30 -0
  47. package/dist/plugins/installers/base.js.map +1 -0
  48. package/dist/plugins/installers/github.d.ts +22 -0
  49. package/dist/plugins/installers/github.d.ts.map +1 -0
  50. package/dist/plugins/installers/github.js +133 -0
  51. package/dist/plugins/installers/github.js.map +1 -0
  52. package/dist/plugins/installers/local.d.ts +16 -0
  53. package/dist/plugins/installers/local.d.ts.map +1 -0
  54. package/dist/plugins/installers/local.js +69 -0
  55. package/dist/plugins/installers/local.js.map +1 -0
  56. package/dist/plugins/installers/npm.d.ts +20 -0
  57. package/dist/plugins/installers/npm.d.ts.map +1 -0
  58. package/dist/plugins/installers/npm.js +99 -0
  59. package/dist/plugins/installers/npm.js.map +1 -0
  60. package/dist/plugins/manager.d.ts +77 -0
  61. package/dist/plugins/manager.d.ts.map +1 -0
  62. package/dist/plugins/manager.js +349 -0
  63. package/dist/plugins/manager.js.map +1 -0
  64. package/dist/plugins/registry.d.ts +48 -0
  65. package/dist/plugins/registry.d.ts.map +1 -0
  66. package/dist/plugins/registry.js +111 -0
  67. package/dist/plugins/registry.js.map +1 -0
  68. package/dist/plugins/types.d.ts +66 -0
  69. package/dist/plugins/types.d.ts.map +1 -0
  70. package/dist/plugins/types.js +2 -0
  71. package/dist/plugins/types.js.map +1 -0
  72. package/dist/plugins/validator.d.ts +19 -0
  73. package/dist/plugins/validator.d.ts.map +1 -0
  74. package/dist/plugins/validator.js +164 -0
  75. package/dist/plugins/validator.js.map +1 -0
  76. package/dist/utils/logger.d.ts +13 -0
  77. package/dist/utils/logger.d.ts.map +1 -0
  78. package/dist/utils/logger.js +48 -0
  79. package/dist/utils/logger.js.map +1 -0
  80. package/dist/utils/project.d.ts +24 -0
  81. package/dist/utils/project.d.ts.map +1 -0
  82. package/dist/utils/project.js +61 -0
  83. package/dist/utils/project.js.map +1 -0
  84. package/dist/version.d.ts +3 -0
  85. package/dist/version.d.ts.map +1 -0
  86. package/dist/version.js +21 -0
  87. package/dist/version.js.map +1 -0
  88. package/package.json +76 -0
  89. package/plugins/authentic-voice/README.md +31 -0
  90. package/plugins/authentic-voice/commands/authentic-voice.md +73 -0
  91. package/plugins/authentic-voice/commands/authenticity-audit.md +37 -0
  92. package/plugins/authentic-voice/config.yaml +30 -0
  93. package/plugins/authentic-voice/experts/authentic-editor.md +27 -0
  94. package/plugins/export/README.md +319 -0
  95. package/plugins/export/commands/export.md +460 -0
  96. package/plugins/export/commands/generate-cover.md +256 -0
  97. package/plugins/export/commands/metadata.md +309 -0
  98. package/plugins/export/config.yaml +47 -0
  99. package/plugins/export/experts/publishing-expert.md +171 -0
  100. package/plugins/export/templates/epub/chapter-template.html +13 -0
  101. package/plugins/export/templates/epub/cover-template.html +14 -0
  102. package/plugins/export/templates/epub/stylesheet.css +200 -0
  103. package/plugins/export/templates/pdf/ebook-style.css +137 -0
  104. package/plugins/export/templates/pdf/print-style.css +179 -0
  105. package/plugins/export/templates/platforms/jinjiang-format.md +101 -0
  106. package/plugins/export/templates/platforms/qidian-format.md +108 -0
  107. package/plugins/export/templates/platforms/tomato-format.md +95 -0
  108. package/plugins/translate/README.md +265 -0
  109. package/plugins/translate/commands/glossary.md +731 -0
  110. package/plugins/translate/commands/translate-preview.md +543 -0
  111. package/plugins/translate/commands/translate.md +649 -0
  112. package/plugins/translate/config.yaml +43 -0
  113. package/plugins/translate/experts/literary-translator.md +605 -0
  114. package/templates/commands/analyze.md +1485 -0
  115. package/templates/commands/checklist.md +434 -0
  116. package/templates/commands/clarify.md +257 -0
  117. package/templates/commands/constitution.md +257 -0
  118. package/templates/commands/expert.md +136 -0
  119. package/templates/commands/plan.md +749 -0
  120. package/templates/commands/recap.md +613 -0
  121. package/templates/commands/relations.md +96 -0
  122. package/templates/commands/revise.md +341 -0
  123. package/templates/commands/specify.md +682 -0
  124. package/templates/commands/tasks.md +142 -0
  125. package/templates/commands/timeline.md +73 -0
  126. package/templates/commands/track-init.md +137 -0
  127. package/templates/commands/track.md +463 -0
  128. package/templates/commands/write.md +1264 -0
  129. package/templates/config/keyword-mappings.json +106 -0
  130. package/templates/knowledge/audit-config.json +26 -0
  131. package/templates/knowledge/character-profiles.md +152 -0
  132. package/templates/knowledge/character-voices.md +137 -0
  133. package/templates/knowledge/locations.md +184 -0
  134. package/templates/knowledge/world-setting.md +121 -0
  135. package/templates/knowledge-base/README.md +285 -0
  136. package/templates/knowledge-base/character-archetypes/01-hero.md +233 -0
  137. package/templates/knowledge-base/character-archetypes/02-mentor.md +177 -0
  138. package/templates/knowledge-base/character-archetypes/03-shadow.md +221 -0
  139. package/templates/knowledge-base/character-archetypes/04-ally.md +178 -0
  140. package/templates/knowledge-base/character-archetypes/05-shapeshifter.md +177 -0
  141. package/templates/knowledge-base/character-archetypes/06-trickster.md +181 -0
  142. package/templates/knowledge-base/character-archetypes/07-threshold-guardian.md +177 -0
  143. package/templates/knowledge-base/character-archetypes/08-herald.md +180 -0
  144. package/templates/knowledge-base/character-archetypes/09-father.md +249 -0
  145. package/templates/knowledge-base/character-archetypes/10-mother.md +202 -0
  146. package/templates/knowledge-base/character-archetypes/11-child.md +183 -0
  147. package/templates/knowledge-base/character-archetypes/12-sage.md +202 -0
  148. package/templates/knowledge-base/character-archetypes/README.md +60 -0
  149. package/templates/knowledge-base/character-archetypes/application-guide.md +222 -0
  150. package/templates/knowledge-base/character-archetypes/archetype-combinations.md +242 -0
  151. package/templates/knowledge-base/character-archetypes/config.yaml +28 -0
  152. package/templates/knowledge-base/character-archetypes/examples-analysis.md +223 -0
  153. package/templates/knowledge-base/craft/character-arc.md +1153 -0
  154. package/templates/knowledge-base/craft/dialogue.md +1170 -0
  155. package/templates/knowledge-base/craft/pacing.md +1200 -0
  156. package/templates/knowledge-base/craft/scene-structure.md +1136 -0
  157. package/templates/knowledge-base/craft/show-not-tell.md +1012 -0
  158. package/templates/knowledge-base/emotional-beats/01-first-meeting.md +145 -0
  159. package/templates/knowledge-base/emotional-beats/02-bonding-moment.md +226 -0
  160. package/templates/knowledge-base/emotional-beats/03-declaration.md +284 -0
  161. package/templates/knowledge-base/emotional-beats/04-triumph.md +240 -0
  162. package/templates/knowledge-base/emotional-beats/05-reunion.md +396 -0
  163. package/templates/knowledge-base/emotional-beats/06-forgiveness.md +204 -0
  164. package/templates/knowledge-base/emotional-beats/07-betrayal.md +204 -0
  165. package/templates/knowledge-base/emotional-beats/08-loss.md +214 -0
  166. package/templates/knowledge-base/emotional-beats/09-rejection.md +254 -0
  167. package/templates/knowledge-base/emotional-beats/10-failure.md +244 -0
  168. package/templates/knowledge-base/emotional-beats/11-misunderstanding.md +205 -0
  169. package/templates/knowledge-base/emotional-beats/12-farewell.md +283 -0
  170. package/templates/knowledge-base/emotional-beats/13-revelation.md +242 -0
  171. package/templates/knowledge-base/emotional-beats/14-point-of-no-return.md +215 -0
  172. package/templates/knowledge-base/emotional-beats/15-dark-night.md +244 -0
  173. package/templates/knowledge-base/emotional-beats/16-sacrifice.md +246 -0
  174. package/templates/knowledge-base/emotional-beats/17-awakening.md +246 -0
  175. package/templates/knowledge-base/emotional-beats/18-confrontation.md +217 -0
  176. package/templates/knowledge-base/emotional-beats/19-bittersweet-goodbye.md +368 -0
  177. package/templates/knowledge-base/emotional-beats/20-moral-dilemma.md +248 -0
  178. package/templates/knowledge-base/emotional-beats/21-temptation.md +240 -0
  179. package/templates/knowledge-base/emotional-beats/22-redemption.md +210 -0
  180. package/templates/knowledge-base/emotional-beats/README.md +104 -0
  181. package/templates/knowledge-base/emotional-beats/beat-sequences.md +276 -0
  182. package/templates/knowledge-base/emotional-beats/config.yaml +30 -0
  183. package/templates/knowledge-base/emotional-beats/pacing-guide.md +390 -0
  184. package/templates/knowledge-base/genres/historical.md +1127 -0
  185. package/templates/knowledge-base/genres/mystery.md +1123 -0
  186. package/templates/knowledge-base/genres/revenge.md +846 -0
  187. package/templates/knowledge-base/genres/romance.md +948 -0
  188. package/templates/knowledge-base/genres/sci-fi.md +156 -0
  189. package/templates/knowledge-base/genres/thriller.md +166 -0
  190. package/templates/knowledge-base/genres/wuxia.md +143 -0
  191. package/templates/knowledge-base/references/README.md +96 -0
  192. package/templates/knowledge-base/references/china-1920s/culture.md +423 -0
  193. package/templates/knowledge-base/references/china-1920s/daily-life.md +616 -0
  194. package/templates/knowledge-base/references/china-1920s/overview.md +298 -0
  195. package/templates/knowledge-base/references/china-1920s/society.md +703 -0
  196. package/templates/knowledge-base/references/china-1920s/warlords.md +427 -0
  197. package/templates/knowledge-base/references/cultivation-world/daily-life.md +108 -0
  198. package/templates/knowledge-base/references/cultivation-world/overview.md +64 -0
  199. package/templates/knowledge-base/references/cultivation-world/power-system.md +108 -0
  200. package/templates/knowledge-base/references/cultivation-world/sects.md +104 -0
  201. package/templates/knowledge-base/references/cultivation-world/world-setting.md +108 -0
  202. package/templates/knowledge-base/references/modern-workplace/corporate.md +115 -0
  203. package/templates/knowledge-base/references/modern-workplace/daily-life.md +129 -0
  204. package/templates/knowledge-base/references/modern-workplace/overview.md +73 -0
  205. package/templates/knowledge-base/references/modern-workplace/relationships.md +107 -0
  206. package/templates/knowledge-base/references/modern-workplace/tech-industry.md +131 -0
  207. package/templates/knowledge-base/references/tang-dynasty/culture.md +135 -0
  208. package/templates/knowledge-base/references/tang-dynasty/daily-life.md +139 -0
  209. package/templates/knowledge-base/references/tang-dynasty/overview.md +76 -0
  210. package/templates/knowledge-base/references/tang-dynasty/politics.md +121 -0
  211. package/templates/knowledge-base/references/tang-dynasty/society.md +126 -0
  212. package/templates/knowledge-base/requirements/README.md +240 -0
  213. package/templates/knowledge-base/requirements/anti-ai-v3.md +46 -0
  214. package/templates/knowledge-base/requirements/anti-ai-v4.md +430 -0
  215. package/templates/knowledge-base/requirements/fast-paced.md +552 -0
  216. package/templates/knowledge-base/requirements/no-poison.md +60 -0
  217. package/templates/knowledge-base/requirements/romance-angst.md +102 -0
  218. package/templates/knowledge-base/requirements/romance-sweet.md +63 -0
  219. package/templates/knowledge-base/requirements/serious-literature.md +45 -0
  220. package/templates/knowledge-base/requirements/strong-emotion.md +60 -0
  221. package/templates/knowledge-base/styles/README.md +302 -0
  222. package/templates/knowledge-base/styles/ancient-style.md +579 -0
  223. package/templates/knowledge-base/styles/literary.md +439 -0
  224. package/templates/knowledge-base/styles/minimal.md +472 -0
  225. package/templates/knowledge-base/styles/natural-voice.md +930 -0
  226. package/templates/knowledge-base/styles/web-novel.md +525 -0
  227. package/templates/memory/constitution.md +140 -0
  228. package/templates/memory/personal-voice.md +113 -0
  229. package/templates/scripts/README.md +187 -0
  230. package/templates/scripts/bash/analyze-story.sh +170 -0
  231. package/templates/scripts/bash/check-consistency.sh +463 -0
  232. package/templates/scripts/bash/check-plot.sh +374 -0
  233. package/templates/scripts/bash/check-timeline.sh +346 -0
  234. package/templates/scripts/bash/check-world.sh +395 -0
  235. package/templates/scripts/bash/check-writing-state.sh +854 -0
  236. package/templates/scripts/bash/clarify-story.sh +117 -0
  237. package/templates/scripts/bash/common.sh +151 -0
  238. package/templates/scripts/bash/constitution.sh +114 -0
  239. package/templates/scripts/bash/generate-tasks.sh +65 -0
  240. package/templates/scripts/bash/init-tracking.sh +183 -0
  241. package/templates/scripts/bash/manage-relations.sh +174 -0
  242. package/templates/scripts/bash/plan-story.sh +100 -0
  243. package/templates/scripts/bash/specify-story.sh +93 -0
  244. package/templates/scripts/bash/tasks-story.sh +96 -0
  245. package/templates/scripts/bash/test-word-count.sh +182 -0
  246. package/templates/scripts/bash/tests/bench-preload-cache.sh +100 -0
  247. package/templates/scripts/bash/tests/run-all-benchmarks.sh +16 -0
  248. package/templates/scripts/bash/tests/test-cache-semantics.sh +199 -0
  249. package/templates/scripts/bash/tests/test-cross-platform.sh +35 -0
  250. package/templates/scripts/bash/tests/test-edge-cases-bash.sh +60 -0
  251. package/templates/scripts/bash/tests/test-phase1-bash.sh +28 -0
  252. package/templates/scripts/bash/tests/test-preload-cache.sh +123 -0
  253. package/templates/scripts/bash/tests/test-regex-precompile.sh +89 -0
  254. package/templates/scripts/bash/tests/test-regression-bash.sh +42 -0
  255. package/templates/scripts/bash/tests/test-task6-verification.sh +200 -0
  256. package/templates/scripts/bash/text-audit.sh +144 -0
  257. package/templates/scripts/bash/track-progress.sh +194 -0
  258. package/templates/scripts/powershell/analyze-story.ps1 +171 -0
  259. package/templates/scripts/powershell/check-analyze-stage.ps1 +110 -0
  260. package/templates/scripts/powershell/check-consistency.ps1 +138 -0
  261. package/templates/scripts/powershell/check-plot.ps1 +139 -0
  262. package/templates/scripts/powershell/check-timeline.ps1 +112 -0
  263. package/templates/scripts/powershell/check-writing-state.ps1 +490 -0
  264. package/templates/scripts/powershell/check-writing-state.ps1.backup +135 -0
  265. package/templates/scripts/powershell/clarify-story.ps1 +107 -0
  266. package/templates/scripts/powershell/common.ps1 +36 -0
  267. package/templates/scripts/powershell/constitution.ps1 +142 -0
  268. package/templates/scripts/powershell/generate-tasks.ps1 +75 -0
  269. package/templates/scripts/powershell/init-tracking.ps1 +98 -0
  270. package/templates/scripts/powershell/manage-relations.ps1 +134 -0
  271. package/templates/scripts/powershell/plan-story.ps1 +96 -0
  272. package/templates/scripts/powershell/specify-story.ps1 +82 -0
  273. package/templates/scripts/powershell/tests/bench-ps-cache.ps1 +80 -0
  274. package/templates/scripts/powershell/tests/test-cross-platform.ps1 +27 -0
  275. package/templates/scripts/powershell/tests/test-edge-cases-ps.ps1 +29 -0
  276. package/templates/scripts/powershell/tests/test-phase1-ps.ps1 +28 -0
  277. package/templates/scripts/powershell/tests/test-ps-cache.ps1 +73 -0
  278. package/templates/scripts/powershell/tests/test-regression-ps.ps1 +40 -0
  279. package/templates/scripts/powershell/text-audit.ps1 +100 -0
  280. package/templates/scripts/powershell/track-progress.ps1 +105 -0
  281. package/templates/skills/genre-knowledge/fantasy/SKILL.md +355 -0
  282. package/templates/skills/genre-knowledge/mystery/SKILL.md +337 -0
  283. package/templates/skills/genre-knowledge/romance/SKILL.md +228 -0
  284. package/templates/skills/genre-knowledge/sci-fi/SKILL.md +65 -0
  285. package/templates/skills/genre-knowledge/thriller/SKILL.md +95 -0
  286. package/templates/skills/quality-assurance/consistency-checker/SKILL.md +341 -0
  287. package/templates/skills/quality-assurance/continuity-tracker/SKILL.md +157 -0
  288. package/templates/skills/quality-assurance/forgotten-elements/SKILL.md +147 -0
  289. package/templates/skills/quality-assurance/getting-started/SKILL.md +224 -0
  290. package/templates/skills/quality-assurance/pacing-monitor/SKILL.md +143 -0
  291. package/templates/skills/quality-assurance/pov-validator/SKILL.md +135 -0
  292. package/templates/skills/quality-assurance/pre-write-checklist/SKILL.md +583 -0
  293. package/templates/skills/quality-assurance/requirement-detector/CONFLICT_RESOLUTION.md +119 -0
  294. package/templates/skills/quality-assurance/requirement-detector/EXAMPLES.md +146 -0
  295. package/templates/skills/quality-assurance/requirement-detector/KEYWORDS.md +160 -0
  296. package/templates/skills/quality-assurance/requirement-detector/SKILL.md +149 -0
  297. package/templates/skills/quality-assurance/setting-detector/SKILL.md +611 -0
  298. package/templates/skills/quality-assurance/style-detector/CONFLICT_RESOLUTION.md +126 -0
  299. package/templates/skills/quality-assurance/style-detector/EXAMPLES.md +206 -0
  300. package/templates/skills/quality-assurance/style-detector/KEYWORDS.md +207 -0
  301. package/templates/skills/quality-assurance/style-detector/SKILL.md +126 -0
  302. package/templates/skills/quality-assurance/workflow-guide/SKILL.md +381 -0
  303. package/templates/skills/writing-techniques/character-arc/SKILL.md +267 -0
  304. package/templates/skills/writing-techniques/dialogue-techniques/SKILL.md +366 -0
  305. package/templates/skills/writing-techniques/multi-thread-narrative/SKILL.md +553 -0
  306. package/templates/skills/writing-techniques/multi-thread-narrative/experts/thread-analyst.md +226 -0
  307. package/templates/skills/writing-techniques/pacing-control/SKILL.md +377 -0
  308. package/templates/skills/writing-techniques/reader-expectation/SKILL.md +578 -0
  309. package/templates/skills/writing-techniques/reader-expectation/experts/expectation-analyst.md +209 -0
  310. package/templates/skills/writing-techniques/revision-polish/SKILL.md +496 -0
  311. package/templates/skills/writing-techniques/revision-polish/experts/revision-editor.md +221 -0
  312. package/templates/skills/writing-techniques/scene-structure/SKILL.md +361 -0
  313. package/templates/skills/writing-techniques/style-learning/SKILL.md +436 -0
  314. package/templates/specification-example.md +146 -0
  315. package/templates/tracking/character-state.json +78 -0
  316. package/templates/tracking/plot-tracker.json +62 -0
  317. package/templates/tracking/relationships.json +70 -0
  318. package/templates/tracking/timeline.json +49 -0
  319. package/templates/tracking/tracking-log.md +110 -0
  320. package/templates/tracking/validation-rules.json +128 -0
@@ -0,0 +1,854 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ # 检查写作状态脚本
5
+ # 用于 /write 命令
6
+
7
+ # ============================================
8
+ # Phase 1: 文件时间戳缓存
9
+ # ============================================
10
+ #
11
+ # 缓存值约定:
12
+ # - mtime > 0: 文件存在,值为修改时间戳
13
+ # - mtime = 0: stat 命令失败(权限问题、文件系统错误等)
14
+ # - mtime = -1: 文件不存在(预加载时已确认)
15
+ # - 未缓存: 键不存在于缓存中
16
+
17
+ # Bash 版本检测
18
+ BASH_MAJOR_VERSION="${BASH_VERSION%%.*}"
19
+
20
+ # 缓存存储(关联数组或线性数组)
21
+ if [[ "$BASH_MAJOR_VERSION" -ge 4 ]]; then
22
+ # Bash 4.0+: 使用关联数组
23
+ declare -A FILE_MTIME_CACHE
24
+ else
25
+ # Bash 3.x: 使用线性数组模拟
26
+ FILE_MTIME_CACHE_KEYS=()
27
+ FILE_MTIME_CACHE_VALUES=()
28
+ fi
29
+
30
+ # ============================================
31
+ # Phase 2: 资源去重机制
32
+ # ============================================
33
+ #
34
+ # 用于避免在单次脚本执行中重复检查同一资源文件
35
+
36
+ # Phase 2: 资源去重 - 检测 Bash 版本并选择实现
37
+ if [ "${BASH_VERSINFO[0]}" -ge 4 ]; then
38
+ # Bash 4.0+: 使用关联数组(O(1) 查找)
39
+ USE_ASSOCIATIVE_ARRAY=true
40
+ declare -A loaded_resources_set
41
+ else
42
+ # Bash 3.x: 降级到线性数组(O(n) 查找)
43
+ USE_ASSOCIATIVE_ARRAY=false
44
+ loaded_resources_array=()
45
+ echo "警告: Bash 版本低于 4.0,资源去重使用降级方案(性能稍差)" >&2
46
+ fi
47
+
48
+ # 检查资源是否已加载(Bash 4.0+)
49
+ is_resource_loaded_assoc() {
50
+ local path=$1
51
+ [[ ${loaded_resources_set["$path"]+_} ]]
52
+ }
53
+
54
+ # 标记资源为已加载(Bash 4.0+)
55
+ mark_resource_loaded_assoc() {
56
+ local path=$1
57
+ loaded_resources_set["$path"]=1
58
+ }
59
+
60
+ # 检查资源是否已加载(Bash 3.x 降级)
61
+ is_resource_loaded_array() {
62
+ local path=$1
63
+ for loaded in "${loaded_resources_array[@]}"; do
64
+ if [ "$loaded" = "$path" ]; then
65
+ return 0
66
+ fi
67
+ done
68
+ return 1
69
+ }
70
+
71
+ # 标记资源为已加载(Bash 3.x 降级)
72
+ mark_resource_loaded_array() {
73
+ local path=$1
74
+ loaded_resources_array+=("$path")
75
+ }
76
+
77
+ # 统一接口(自动选择实现)
78
+ is_resource_loaded() {
79
+ if [ "$USE_ASSOCIATIVE_ARRAY" = true ]; then
80
+ is_resource_loaded_assoc "$@"
81
+ else
82
+ is_resource_loaded_array "$@"
83
+ fi
84
+ }
85
+
86
+ mark_resource_loaded() {
87
+ if [ "$USE_ASSOCIATIVE_ARRAY" = true ]; then
88
+ mark_resource_loaded_assoc "$@"
89
+ else
90
+ mark_resource_loaded_array "$@"
91
+ fi
92
+ }
93
+
94
+ # ============================================
95
+ # Phase 3: 缓存文件清理机制
96
+ # ============================================
97
+ #
98
+ # 清理超过 24 小时的旧缓存文件,避免磁盘空间浪费
99
+ # 防御性设计:缓存目录不存在时直接返回
100
+
101
+ cleanup_old_cache() {
102
+ local cache_dir="$PROJECT_ROOT/.specify/.cache"
103
+
104
+ # 缓存目录不存在,无需清理
105
+ if [ ! -d "$cache_dir" ]; then
106
+ return
107
+ fi
108
+
109
+ # 删除超过 24 小时未修改的缓存文件
110
+ find "$cache_dir" -name "*.json" -type f -mtime +1 -delete 2>/dev/null || true
111
+
112
+ # 删除空目录
113
+ find "$cache_dir" -type d -empty -delete 2>/dev/null || true
114
+ }
115
+
116
+ # 预加载文件修改时间到缓存
117
+ # 参数: $@ = 文件路径列表
118
+ # 说明: 由于 Bash 命令替换会创建子shell,我们使用预加载策略
119
+ # 在脚本初始化时一次性加载所有文件的 mtime
120
+ preload_file_mtimes() {
121
+ local file_path
122
+ local mtime
123
+
124
+ for file_path in "$@"; do
125
+ # 文件不存在:记录为 -1
126
+ if [ ! -f "$file_path" ]; then
127
+ if [[ "$BASH_MAJOR_VERSION" -ge 4 ]]; then
128
+ FILE_MTIME_CACHE[$file_path]="-1"
129
+ else
130
+ FILE_MTIME_CACHE_KEYS+=("$file_path")
131
+ FILE_MTIME_CACHE_VALUES+=("-1")
132
+ fi
133
+ continue
134
+ fi
135
+
136
+ # 读取文件时间戳 (macOS/Linux 兼容)
137
+ if [[ "$OSTYPE" == "darwin"* ]]; then
138
+ mtime=$(stat -f "%m" "$file_path" 2>/dev/null || echo "0")
139
+ else
140
+ mtime=$(stat -c "%Y" "$file_path" 2>/dev/null || echo "0")
141
+ fi
142
+
143
+ # 存入缓存
144
+ if [[ "$BASH_MAJOR_VERSION" -ge 4 ]]; then
145
+ FILE_MTIME_CACHE[$file_path]="$mtime"
146
+ else
147
+ FILE_MTIME_CACHE_KEYS+=("$file_path")
148
+ FILE_MTIME_CACHE_VALUES+=("$mtime")
149
+ fi
150
+ done
151
+ }
152
+
153
+ # 获取文件修改时间(从缓存)
154
+ # 参数: $1 = 文件路径
155
+ # 返回: 修改时间戳(秒),如果未缓存则返回 0
156
+ get_file_mtime() {
157
+ local file_path="$1"
158
+
159
+ if [[ "$BASH_MAJOR_VERSION" -ge 4 ]]; then
160
+ # Bash 4.0+: 关联数组查找
161
+ echo "${FILE_MTIME_CACHE[$file_path]:-0}"
162
+ else
163
+ # Bash 3.x: 线性数组查找
164
+ for i in "${!FILE_MTIME_CACHE_KEYS[@]}"; do
165
+ if [[ "${FILE_MTIME_CACHE_KEYS[$i]}" == "$file_path" ]]; then
166
+ echo "${FILE_MTIME_CACHE_VALUES[$i]}"
167
+ return 0
168
+ fi
169
+ done
170
+ echo "0"
171
+ fi
172
+ }
173
+
174
+ # 检查文件是否已被缓存
175
+ # 参数: $1 = 文件路径
176
+ # 返回: 0 = 已缓存, 1 = 未缓存
177
+ is_file_cached() {
178
+ local file_path="$1"
179
+
180
+ if [[ "$BASH_MAJOR_VERSION" -ge 4 ]]; then
181
+ [[ ${FILE_MTIME_CACHE[$file_path]+isset} ]]
182
+ else
183
+ for key in "${FILE_MTIME_CACHE_KEYS[@]}"; do
184
+ [[ "$key" == "$file_path" ]] && return 0
185
+ done
186
+ return 1
187
+ fi
188
+ }
189
+
190
+ # 检查文件是否存在(基于缓存)
191
+ # 参数: $1 = 文件路径
192
+ # 返回: 0 = 文件存在, 1 = 文件不存在或未缓存
193
+ is_file_exists_cached() {
194
+ local file_path="$1"
195
+ local mtime=$(get_file_mtime "$file_path")
196
+
197
+ # mtime > 0: 文件存在
198
+ # mtime = 0: stat 失败或未缓存
199
+ # mtime = -1: 文件不存在
200
+ [[ "$mtime" != "0" && "$mtime" != "-1" ]]
201
+ }
202
+
203
+ # Source common functions
204
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
205
+ source "$SCRIPT_DIR/common.sh"
206
+
207
+ # 预加载文件时间戳缓存(性能优化)
208
+ # 注意:必须在 get_project_root 之前进行,或者在确定项目根目录后再预加载
209
+ # 这里我们延迟到获取 PROJECT_ROOT 之后
210
+ PRELOAD_FILES_PENDING=true
211
+
212
+ # 检查是否为 checklist 模式
213
+ CHECKLIST_MODE=false
214
+ if [ "$1" = "--checklist" ]; then
215
+ CHECKLIST_MODE=true
216
+ fi
217
+
218
+ # 检查是否为 JSON 输出模式
219
+ JSON_MODE=false
220
+ if [ "$1" = "--json" ] || [ "$2" = "--json" ]; then
221
+ JSON_MODE=true
222
+ fi
223
+
224
+ # Get project root
225
+ PROJECT_ROOT=$(get_project_root)
226
+ cd "$PROJECT_ROOT"
227
+
228
+ # 获取当前故事
229
+ STORY_NAME=$(get_active_story)
230
+ STORY_DIR="stories/$STORY_NAME"
231
+
232
+ # 预加载文件时间戳(性能优化)
233
+ if [ "$PRELOAD_FILES_PENDING" = true ]; then
234
+ # 构建待预加载的文件列表
235
+ PRELOAD_FILE_LIST=(
236
+ # 知识库文件
237
+ "$PROJECT_ROOT/templates/knowledge-base/craft/dialogue.md"
238
+ "$PROJECT_ROOT/templates/knowledge-base/craft/scene-structure.md"
239
+ "$PROJECT_ROOT/templates/knowledge-base/craft/character-arc.md"
240
+ "$PROJECT_ROOT/templates/knowledge-base/craft/pacing.md"
241
+ "$PROJECT_ROOT/templates/knowledge-base/craft/show-not-tell.md"
242
+ # Skill 文件
243
+ "$PROJECT_ROOT/templates/skills/writing-techniques/dialogue-techniques/SKILL.md"
244
+ "$PROJECT_ROOT/templates/skills/writing-techniques/scene-structure/SKILL.md"
245
+ "$PROJECT_ROOT/templates/skills/writing-techniques/character-arc/SKILL.md"
246
+ "$PROJECT_ROOT/templates/skills/writing-techniques/pacing-control/SKILL.md"
247
+ "$PROJECT_ROOT/templates/skills/quality-assurance/consistency-checker/SKILL.md"
248
+ # 规格文件
249
+ "$STORY_DIR/specification.md"
250
+ )
251
+
252
+ # 执行预加载
253
+ preload_file_mtimes "${PRELOAD_FILE_LIST[@]}"
254
+ PRELOAD_FILES_PENDING=false
255
+ fi
256
+
257
+ # Phase 3: 清理旧缓存(轻量级,不影响性能)
258
+ cleanup_old_cache
259
+
260
+ # 检查方法论文档
261
+ check_methodology_docs() {
262
+ local missing=()
263
+
264
+ [ ! -f ".specify/memory/constitution.md" ] && missing+=("宪法")
265
+ [ ! -f "$STORY_DIR/specification.md" ] && missing+=("规格")
266
+ [ ! -f "$STORY_DIR/creative-plan.md" ] && missing+=("计划")
267
+ [ ! -f "$STORY_DIR/tasks.md" ] && missing+=("任务")
268
+
269
+ if [ ${#missing[@]} -gt 0 ]; then
270
+ echo "⚠️ 缺少以下基准文档:"
271
+ for doc in "${missing[@]}"; do
272
+ echo " - $doc"
273
+ done
274
+ echo ""
275
+ echo "建议按照七步方法论完成前置步骤:"
276
+ echo "1. /constitution - 创建创作宪法"
277
+ echo "2. /specify - 定义故事规格"
278
+ echo "3. /clarify - 澄清关键决策"
279
+ echo "4. /plan - 制定创作计划"
280
+ echo "5. /tasks - 生成任务清单"
281
+ return 1
282
+ fi
283
+
284
+ echo "✅ 方法论文档完整"
285
+ return 0
286
+ }
287
+
288
+ # 检查待写作任务
289
+ check_pending_tasks() {
290
+ local tasks_file="$STORY_DIR/tasks.md"
291
+
292
+ if [ ! -f "$tasks_file" ]; then
293
+ echo "❌ 任务文件不存在"
294
+ return 1
295
+ fi
296
+
297
+ # 统计任务状态
298
+ local pending=$(grep -c "^- \[ \]" "$tasks_file" 2>/dev/null || echo 0)
299
+ local in_progress=$(grep -c "^- \[~\]" "$tasks_file" 2>/dev/null || echo 0)
300
+ local completed=$(grep -c "^- \[x\]" "$tasks_file" 2>/dev/null || echo 0)
301
+
302
+ echo ""
303
+ echo "任务状态:"
304
+ echo " 待开始:$pending"
305
+ echo " 进行中:$in_progress"
306
+ echo " 已完成:$completed"
307
+
308
+ if [ $pending -eq 0 ] && [ $in_progress -eq 0 ]; then
309
+ echo ""
310
+ echo "🎉 所有任务已完成!"
311
+ echo "建议运行 /analyze 进行综合验证"
312
+ return 0
313
+ fi
314
+
315
+ # 显示下一个待写作任务
316
+ echo ""
317
+ echo "下一个写作任务:"
318
+ grep "^- \[ \]" "$tasks_file" | head -n 1 || echo "(无待处理任务)"
319
+ }
320
+
321
+ # 检查已完成内容
322
+ check_completed_content() {
323
+ local content_dir="$STORY_DIR/content"
324
+ local validation_rules="$STORY_DIR/spec/tracking/validation-rules.json"
325
+ local min_words=2000
326
+ local max_words=4000
327
+
328
+ # 读取验证规则(如果存在)
329
+ if [ -f "$validation_rules" ]; then
330
+ if command -v jq >/dev/null 2>&1; then
331
+ min_words=$(jq -r '.rules.chapterMinWords // 2000' "$validation_rules")
332
+ max_words=$(jq -r '.rules.chapterMaxWords // 4000' "$validation_rules")
333
+ fi
334
+ fi
335
+
336
+ if [ -d "$content_dir" ]; then
337
+ local chapter_count=$(ls "$content_dir"/*.md 2>/dev/null | wc -l)
338
+ if [ $chapter_count -gt 0 ]; then
339
+ echo ""
340
+ echo "已完成章节:$chapter_count"
341
+ echo "字数要求:${min_words}-${max_words} 字"
342
+ echo ""
343
+ echo "最近写作:"
344
+ for file in $(ls -t "$content_dir"/*.md 2>/dev/null | head -n 3); do
345
+ local filename=$(basename "$file")
346
+ local words=$(count_chinese_words "$file")
347
+ local status="✅"
348
+
349
+ if [ "$words" -lt "$min_words" ]; then
350
+ status="⚠️ 字数不足"
351
+ elif [ "$words" -gt "$max_words" ]; then
352
+ status="⚠️ 字数超出"
353
+ fi
354
+
355
+ echo " - $filename: $words 字 $status"
356
+ done
357
+ fi
358
+ else
359
+ echo ""
360
+ echo "尚未开始写作"
361
+ fi
362
+ }
363
+
364
+ # 生成 checklist 格式输出
365
+ output_checklist() {
366
+ local has_constitution=false
367
+ local has_specification=false
368
+ local has_plan=false
369
+ local has_tasks=false
370
+ local pending=0
371
+ local in_progress=0
372
+ local completed=0
373
+ local chapter_count=0
374
+ local bad_chapters=0
375
+ local min_words=2000
376
+ local max_words=4000
377
+
378
+ # 检查文档
379
+ [ -f ".specify/memory/constitution.md" ] && has_constitution=true
380
+ [ -f "$STORY_DIR/specification.md" ] && has_specification=true
381
+ [ -f "$STORY_DIR/creative-plan.md" ] && has_plan=true
382
+ [ -f "$STORY_DIR/tasks.md" ] && has_tasks=true
383
+
384
+ # 统计任务
385
+ if [ "$has_tasks" = true ]; then
386
+ pending=$(grep -c "^- \[ \]" "$STORY_DIR/tasks.md" 2>/dev/null || echo 0)
387
+ in_progress=$(grep -c "^- \[~\]" "$STORY_DIR/tasks.md" 2>/dev/null || echo 0)
388
+ completed=$(grep -c "^- \[x\]" "$STORY_DIR/tasks.md" 2>/dev/null || echo 0)
389
+ fi
390
+
391
+ # 读取验证规则
392
+ local validation_rules="$STORY_DIR/spec/tracking/validation-rules.json"
393
+ if [ -f "$validation_rules" ] && command -v jq >/dev/null 2>&1; then
394
+ min_words=$(jq -r '.rules.chapterMinWords // 2000' "$validation_rules")
395
+ max_words=$(jq -r '.rules.chapterMaxWords // 4000' "$validation_rules")
396
+ fi
397
+
398
+ # 检查章节内容
399
+ local content_dir="$STORY_DIR/content"
400
+ if [ -d "$content_dir" ]; then
401
+ chapter_count=$(ls "$content_dir"/*.md 2>/dev/null | wc -l | tr -d ' ')
402
+
403
+ # 统计不符合字数要求的章节
404
+ for file in "$content_dir"/*.md; do
405
+ [ -f "$file" ] || continue
406
+ local words=$(count_chinese_words "$file")
407
+ if [ "$words" -lt "$min_words" ] || [ "$words" -gt "$max_words" ]; then
408
+ bad_chapters=$((bad_chapters + 1))
409
+ fi
410
+ done
411
+ fi
412
+
413
+ # 计算总任务和完成率
414
+ local total_tasks=$((pending + in_progress + completed))
415
+ local completion_rate=0
416
+ if [ $total_tasks -gt 0 ]; then
417
+ completion_rate=$((completed * 100 / total_tasks))
418
+ fi
419
+
420
+ # 输出 checklist
421
+ cat <<EOF
422
+ # 写作状态检查 Checklist
423
+
424
+ **检查时间**: $(date '+%Y-%m-%d %H:%M:%S')
425
+ **当前故事**: $STORY_NAME
426
+ **字数标准**: ${min_words}-${max_words} 字
427
+
428
+ ---
429
+
430
+ ## 文档完整性
431
+
432
+ - [$([ "$has_constitution" = true ] && echo "x" || echo " ")] CHK001 constitution.md 存在
433
+ - [$([ "$has_specification" = true ] && echo "x" || echo " ")] CHK002 specification.md 存在
434
+ - [$([ "$has_plan" = true ] && echo "x" || echo " ")] CHK003 creative-plan.md 存在
435
+ - [$([ "$has_tasks" = true ] && echo "x" || echo " ")] CHK004 tasks.md 存在
436
+
437
+ ## 任务进度
438
+
439
+ EOF
440
+
441
+ if [ "$has_tasks" = true ]; then
442
+ echo "- [$([ $in_progress -gt 0 ] && echo "x" || echo " ")] CHK005 有进行中的任务($in_progress 个)"
443
+ echo "- [x] CHK006 待开始任务数量($pending 个)"
444
+ echo "- [$([ $completed -gt 0 ] && echo "x" || echo " ")] CHK007 已完成任务进度($completed/$total_tasks = $completion_rate%)"
445
+ else
446
+ echo "- [ ] CHK005 有进行中的任务(tasks.md 不存在)"
447
+ echo "- [ ] CHK006 待开始任务数量(tasks.md 不存在)"
448
+ echo "- [ ] CHK007 已完成任务进度(tasks.md 不存在)"
449
+ fi
450
+
451
+ cat <<EOF
452
+
453
+ ## 内容质量
454
+
455
+ - [$([ $chapter_count -gt 0 ] && echo "x" || echo " ")] CHK008 已完成章节数($chapter_count 章)
456
+ EOF
457
+
458
+ if [ $chapter_count -gt 0 ]; then
459
+ echo "- [$([ $bad_chapters -eq 0 ] && echo "x" || echo "!")] CHK009 字数符合标准($([ $bad_chapters -eq 0 ] && echo "全部符合" || echo "$bad_chapters 章不符合"))"
460
+ else
461
+ echo "- [ ] CHK009 字数符合标准(尚未开始写作)"
462
+ fi
463
+
464
+ cat <<EOF
465
+
466
+ ---
467
+
468
+ ## 后续行动
469
+
470
+ EOF
471
+
472
+ local has_actions=false
473
+
474
+ # 检查缺失文档
475
+ if [ "$has_constitution" = false ] || [ "$has_specification" = false ] || [ "$has_plan" = false ] || [ "$has_tasks" = false ]; then
476
+ echo "- [ ] 完成方法论文档(运行对应命令:/constitution, /specify, /plan, /tasks)"
477
+ has_actions=true
478
+ fi
479
+
480
+ # 检查任务
481
+ if [ $pending -gt 0 ] || [ $in_progress -gt 0 ]; then
482
+ if [ $in_progress -gt 0 ]; then
483
+ echo "- [ ] 继续进行中的任务($in_progress 个)"
484
+ else
485
+ echo "- [ ] 开始下一个待写作任务(共 $pending 个)"
486
+ fi
487
+ has_actions=true
488
+ fi
489
+
490
+ # 检查章节质量
491
+ if [ $bad_chapters -gt 0 ]; then
492
+ echo "- [ ] 修复字数不符合要求的章节($bad_chapters 章)"
493
+ has_actions=true
494
+ fi
495
+
496
+ # 完成建议
497
+ if [ $pending -eq 0 ] && [ $in_progress -eq 0 ] && [ $completed -gt 0 ]; then
498
+ echo "- [ ] 运行 /analyze 进行综合验证"
499
+ has_actions=true
500
+ fi
501
+
502
+ if [ "$has_actions" = false ]; then
503
+ echo "*写作状态良好,无需特别行动*"
504
+ fi
505
+
506
+ cat <<EOF
507
+
508
+ ---
509
+
510
+ **检查工具**: check-writing-state.sh
511
+ **版本**: 1.1 (支持 checklist 输出)
512
+ EOF
513
+ }
514
+
515
+ # ==================== 新增:资源加载检查函数 ====================
516
+
517
+ # JSON 字符串转义辅助函数
518
+ json_escape() {
519
+ local str="$1"
520
+ # 按顺序转义:反斜杠 -> 引号 -> 控制字符
521
+ str="${str//\\/\\\\}" # \ -> \\
522
+ str="${str//\"/\\\"}" # " -> \"
523
+ str="${str//$'\t'/\\t}" # tab -> \t
524
+ str="${str//$'\n'/\\n}" # newline -> \n
525
+ str="${str//$'\r'/\\r}" # carriage return -> \r
526
+ echo "$str"
527
+ }
528
+
529
+ # 解析 specification.md 的 resource-loading 配置
530
+ parse_resource_loading_config() {
531
+ local spec_file="$STORY_DIR/specification.md"
532
+
533
+ if [ ! -f "$spec_file" ]; then
534
+ echo "{}" # 返回空 JSON
535
+ return
536
+ fi
537
+
538
+ # 提取 YAML frontmatter 中的 resource-loading 配置
539
+ # 这里简化处理,实际应该用 yq 或 python 解析 YAML
540
+ # 当前版本:检测是否存在 resource-loading 配置
541
+
542
+ if grep -q "resource-loading:" "$spec_file"; then
543
+ echo '{"configured": true}'
544
+ else
545
+ echo '{"configured": false}'
546
+ fi
547
+ }
548
+
549
+ # 检查 knowledge-base 文件是否存在
550
+ check_knowledge_base_available() {
551
+ local missing=()
552
+ local available=()
553
+
554
+ # 检查所有 craft knowledge-base
555
+ local craft_files=(
556
+ "templates/knowledge-base/craft/dialogue.md"
557
+ "templates/knowledge-base/craft/scene-structure.md"
558
+ "templates/knowledge-base/craft/character-arc.md"
559
+ "templates/knowledge-base/craft/pacing.md"
560
+ "templates/knowledge-base/craft/show-not-tell.md"
561
+ )
562
+
563
+ for file in "${craft_files[@]}"; do
564
+ local full_path="$PROJECT_ROOT/$file"
565
+ # 使用缓存检查文件是否存在
566
+ if is_file_exists_cached "$full_path"; then
567
+ available+=("$file")
568
+ else
569
+ missing+=("$file")
570
+ fi
571
+ done
572
+
573
+ # 输出结果(JSON 格式将在后续步骤实现)
574
+ if [ ${#missing[@]} -gt 0 ]; then
575
+ echo "⚠️ 缺少以下 knowledge-base 文件:"
576
+ for file in "${missing[@]}"; do
577
+ echo " - $file"
578
+ done
579
+ return 1
580
+ fi
581
+
582
+ echo "✅ Knowledge-base 文件完整 (${#available[@]} 个)"
583
+ return 0
584
+ }
585
+
586
+ # 检查 skills 是否存在
587
+ check_skills_available() {
588
+ local missing=()
589
+ local available=()
590
+
591
+ # 检查 writing-techniques skills
592
+ local skill_dirs=(
593
+ "templates/skills/writing-techniques/dialogue-techniques"
594
+ "templates/skills/writing-techniques/scene-structure"
595
+ "templates/skills/writing-techniques/character-arc"
596
+ "templates/skills/writing-techniques/pacing-control"
597
+ )
598
+
599
+ for dir in "${skill_dirs[@]}"; do
600
+ local skill_file="$PROJECT_ROOT/$dir/SKILL.md"
601
+ # 使用缓存检查文件是否存在
602
+ if is_file_exists_cached "$skill_file"; then
603
+ available+=("$dir")
604
+ else
605
+ missing+=("$dir")
606
+ fi
607
+ done
608
+
609
+ if [ ${#missing[@]} -gt 0 ]; then
610
+ echo "⚠️ 缺少以下 skills:"
611
+ for dir in "${missing[@]}"; do
612
+ echo " - $dir/SKILL.md"
613
+ done
614
+ return 1
615
+ fi
616
+
617
+ echo "✅ Skills 完整 (${#available[@]} 个)"
618
+ return 0
619
+ }
620
+
621
+ # 生成资源加载报告(JSON 格式)
622
+ generate_load_report() {
623
+ # Phase 3: 性能监控
624
+ local start_time
625
+ if [[ "$OSTYPE" == "darwin"* ]]; then
626
+ start_time=$(python3 -c 'import time; print(int(time.time() * 1000))' 2>/dev/null || date +%s)
627
+ else
628
+ start_time=$(date +%s%3N 2>/dev/null || date +%s)
629
+ fi
630
+
631
+ local spec_file="$STORY_DIR/specification.md"
632
+
633
+ local knowledge_base_files=(
634
+ "craft/dialogue.md"
635
+ "craft/scene-structure.md"
636
+ "craft/character-arc.md"
637
+ "craft/pacing.md"
638
+ "craft/show-not-tell.md"
639
+ )
640
+
641
+ local skills_files=(
642
+ "writing-techniques/dialogue-techniques"
643
+ "writing-techniques/scene-structure"
644
+ "writing-techniques/character-arc"
645
+ "writing-techniques/pacing-control"
646
+ "quality-assurance/consistency-checker"
647
+ )
648
+
649
+ local disabled_resources=()
650
+
651
+ # 检查配置文件
652
+ local has_config=false
653
+ if [ -f "$spec_file" ] && grep -q "resource-loading:" "$spec_file"; then
654
+ has_config=true
655
+
656
+ # 检查是否禁用了 auto-load
657
+ if grep -A 1 "resource-loading:" "$spec_file" | grep -q "auto-load: false"; then
658
+ # 如果禁用自动加载,清空默认列表
659
+ knowledge_base_files=()
660
+ skills_files=()
661
+ fi
662
+
663
+ # TODO: 解析配置文件中的具体资源列表
664
+ # 当前简化版本,完整解析需要 yq 或 python
665
+ fi
666
+
667
+ # 检查文件是否存在,生成警告(使用缓存 + Phase 2 去重)
668
+ local warnings=()
669
+ for kb in "${knowledge_base_files[@]}"; do
670
+ local kb_path="templates/knowledge-base/$kb"
671
+
672
+ # Phase 2: 资源去重检查
673
+ if is_resource_loaded "$kb_path"; then
674
+ # 资源已检查过,跳过
675
+ continue
676
+ fi
677
+
678
+ # 标记为已加载
679
+ mark_resource_loaded "$kb_path"
680
+
681
+ # 检查文件是否存在(使用完整路径)
682
+ if ! is_file_exists_cached "$PROJECT_ROOT/$kb_path"; then
683
+ warnings+=("知识库文件不存在: $kb")
684
+ fi
685
+ done
686
+
687
+ for skill in "${skills_files[@]}"; do
688
+ local skill_path="templates/skills/$skill/SKILL.md"
689
+
690
+ # Phase 2: 资源去重检查
691
+ if is_resource_loaded "$skill_path"; then
692
+ # 资源已检查过,跳过
693
+ continue
694
+ fi
695
+
696
+ # 标记为已加载
697
+ mark_resource_loaded "$skill_path"
698
+
699
+ # 检查文件是否存在(使用完整路径)
700
+ if ! is_file_exists_cached "$PROJECT_ROOT/$skill_path"; then
701
+ warnings+=("Skill 文件不存在: $skill/SKILL.md")
702
+ fi
703
+ done
704
+
705
+ # Phase 2: 检测缓存命中(基于 specification.md 是否已被缓存加载且有效)
706
+ # 仅当文件在缓存中且成功读取时,才视为"缓存命中"
707
+ # 缓存语义: -1 = 文件不存在, 0 = stat 失败或未缓存, >0 = 成功读取
708
+ local cached=false
709
+ local cache_hint=""
710
+
711
+ # 获取缓存的 mtime
712
+ local spec_mtime=$(get_file_mtime "$spec_file")
713
+
714
+ # 仅当 mtime > 0 时才视为缓存命中
715
+ if [[ "$spec_mtime" != "0" && "$spec_mtime" != "-1" ]]; then
716
+ cached=true
717
+ cache_hint="此报告基于缓存生成(specification.md 未修改)。AI 可复用本次会话中已加载的资源。"
718
+ fi
719
+
720
+ # 生成 JSON 报告(使用 echo 逐行输出,处理空数组)
721
+ echo "{"
722
+ echo " \"status\": \"ready\","
723
+
724
+ # 生成时间戳,带错误处理和回退
725
+ local timestamp
726
+ if timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null); then
727
+ echo " \"timestamp\": \"$timestamp\","
728
+ elif timestamp=$(date +"%Y-%m-%dT%H:%M:%S%z" 2>/dev/null); then
729
+ echo " \"timestamp\": \"$timestamp\","
730
+ else
731
+ echo " \"timestamp\": \"unknown\","
732
+ fi
733
+
734
+ echo " \"has_config\": $has_config,"
735
+
736
+ # Phase 2: 添加缓存标记字段
737
+ echo " \"cached\": $cached,"
738
+ echo " \"session_cache_enabled\": true,"
739
+ if [ "$cached" = true ]; then
740
+ echo " \"cache_hint\": \"$(json_escape "$cache_hint")\","
741
+ fi
742
+
743
+ echo " \"resources\": {"
744
+ echo " \"knowledge-base\": ["
745
+
746
+ # 输出 knowledge-base 列表
747
+ local first=true
748
+ for kb in "${knowledge_base_files[@]}"; do
749
+ if [ "$first" = true ]; then
750
+ echo -n " \"$(json_escape "$kb")\""
751
+ first=false
752
+ else
753
+ echo ","
754
+ echo -n " \"$(json_escape "$kb")\""
755
+ fi
756
+ done
757
+ echo ""
758
+ echo " ],"
759
+
760
+ echo " \"skills\": ["
761
+ # 输出 skills 列表
762
+ first=true
763
+ for skill in "${skills_files[@]}"; do
764
+ if [ "$first" = true ]; then
765
+ echo -n " \"$(json_escape "$skill")\""
766
+ first=false
767
+ else
768
+ echo ","
769
+ echo -n " \"$(json_escape "$skill")\""
770
+ fi
771
+ done
772
+ echo ""
773
+ echo " ],"
774
+
775
+ echo " \"disabled\": ["
776
+ # 输出 disabled 列表
777
+ first=true
778
+ for res in "${disabled_resources[@]}"; do
779
+ if [ "$first" = true ]; then
780
+ echo -n " \"$(json_escape "$res")\""
781
+ first=false
782
+ else
783
+ echo ","
784
+ echo -n " \"$(json_escape "$res")\""
785
+ fi
786
+ done
787
+ echo ""
788
+ echo " ]"
789
+
790
+ echo " },"
791
+
792
+ # Phase 3: 性能指标
793
+ local end_time
794
+ if [[ "$OSTYPE" == "darwin"* ]]; then
795
+ end_time=$(python3 -c 'import time; print(int(time.time() * 1000))' 2>/dev/null || date +%s)
796
+ else
797
+ end_time=$(date +%s%3N 2>/dev/null || date +%s)
798
+ fi
799
+ local generation_time=$((end_time - start_time))
800
+
801
+ echo " \"performance\": {"
802
+ echo " \"generation_time_ms\": $generation_time,"
803
+ echo " \"cache_hit\": $cached"
804
+ echo " },"
805
+
806
+ echo " \"warnings\": ["
807
+ # 输出 warnings 列表
808
+ first=true
809
+ for warn in "${warnings[@]}"; do
810
+ if [ "$first" = true ]; then
811
+ echo -n " \"$(json_escape "$warn")\""
812
+ first=false
813
+ else
814
+ echo ","
815
+ echo -n " \"$(json_escape "$warn")\""
816
+ fi
817
+ done
818
+ echo ""
819
+ echo " ]"
820
+ echo "}"
821
+ }
822
+
823
+ # 主流程
824
+ main() {
825
+ # JSON 模式优先处理
826
+ if [ "$JSON_MODE" = true ]; then
827
+ generate_load_report
828
+ exit 0
829
+ fi
830
+
831
+ # Checklist 模式直接输出并退出
832
+ if [ "$CHECKLIST_MODE" = true ]; then
833
+ output_checklist
834
+ exit 0
835
+ fi
836
+
837
+ # 原有的详细输出模式
838
+ echo "写作状态检查"
839
+ echo "============"
840
+ echo "当前故事:$STORY_NAME"
841
+ echo ""
842
+
843
+ if ! check_methodology_docs; then
844
+ exit 1
845
+ fi
846
+
847
+ check_pending_tasks
848
+ check_completed_content
849
+
850
+ echo ""
851
+ echo "准备就绪,可以开始写作"
852
+ }
853
+
854
+ main