novel-maker 2.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 (228) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +209 -0
  3. package/bin/novel-maker.js +229 -0
  4. package/package.json +33 -0
  5. package/skill/CHANGELOG.md +82 -0
  6. package/skill/QUICK-REF.md +168 -0
  7. package/skill/README.md +75 -0
  8. package/skill/SKILL.md +715 -0
  9. package/skill/agents/README.md +59 -0
  10. package/skill/agents/auditor.md +234 -0
  11. package/skill/agents/coordinator.md +150 -0
  12. package/skill/agents/planner.md +220 -0
  13. package/skill/agents/reviewer.md +249 -0
  14. package/skill/agents/reviser.md +144 -0
  15. package/skill/agents/writer.md +213 -0
  16. package/skill/arc-templates/README.md +43 -0
  17. package/skill/arc-templates/apocalypse/ability.md +81 -0
  18. package/skill/arc-templates/apocalypse/faction.md +81 -0
  19. package/skill/arc-templates/apocalypse/humanity.md +81 -0
  20. package/skill/arc-templates/apocalypse/survival.md +81 -0
  21. package/skill/arc-templates/game/competition.md +81 -0
  22. package/skill/arc-templates/game/dungeon.md +81 -0
  23. package/skill/arc-templates/game/guild-war.md +81 -0
  24. package/skill/arc-templates/game/leveling.md +80 -0
  25. package/skill/arc-templates/general/challenge.md +44 -0
  26. package/skill/arc-templates/general/conflict.md +71 -0
  27. package/skill/arc-templates/general/explore.md +71 -0
  28. package/skill/arc-templates/general/growth.md +71 -0
  29. package/skill/arc-templates/general/mystery.md +71 -0
  30. package/skill/arc-templates/general/relation.md +71 -0
  31. package/skill/arc-templates/history/battle.md +71 -0
  32. package/skill/arc-templates/history/politics.md +71 -0
  33. package/skill/arc-templates/history/reform.md +71 -0
  34. package/skill/arc-templates/infinite-flow/boss.md +71 -0
  35. package/skill/arc-templates/infinite-flow/dungeon.md +71 -0
  36. package/skill/arc-templates/infinite-flow/enhance.md +71 -0
  37. package/skill/arc-templates/infinite-flow/team.md +71 -0
  38. package/skill/arc-templates/mystery/case.md +71 -0
  39. package/skill/arc-templates/mystery/deduction.md +71 -0
  40. package/skill/arc-templates/mystery/twist.md +71 -0
  41. package/skill/arc-templates/romance/angst.md +80 -0
  42. package/skill/arc-templates/romance/chase.md +71 -0
  43. package/skill/arc-templates/romance/slow-burn.md +71 -0
  44. package/skill/arc-templates/romance/sweet.md +80 -0
  45. package/skill/arc-templates/sci-fi/awakening.md +81 -0
  46. package/skill/arc-templates/sci-fi/breakthrough.md +81 -0
  47. package/skill/arc-templates/sci-fi/contact.md +81 -0
  48. package/skill/arc-templates/sci-fi/exploration.md +81 -0
  49. package/skill/arc-templates/urban/business.md +71 -0
  50. package/skill/arc-templates/urban/revenge.md +71 -0
  51. package/skill/arc-templates/urban/rise.md +71 -0
  52. package/skill/arc-templates/urban/romance.md +71 -0
  53. package/skill/arc-templates/western-fantasy/adventure.md +80 -0
  54. package/skill/arc-templates/western-fantasy/kingdom.md +71 -0
  55. package/skill/arc-templates/western-fantasy/magic-awakening.md +71 -0
  56. package/skill/arc-templates/western-fantasy/racial-conflict.md +71 -0
  57. package/skill/arc-templates/wuxia/breakthrough.md +71 -0
  58. package/skill/arc-templates/wuxia/grudge.md +71 -0
  59. package/skill/arc-templates/wuxia/hero-path.md +71 -0
  60. package/skill/arc-templates/wuxia/sect-war.md +71 -0
  61. package/skill/arc-templates/xianxia/breakthrough.md +71 -0
  62. package/skill/arc-templates/xianxia/dungeon.md +71 -0
  63. package/skill/arc-templates/xianxia/tournament.md +71 -0
  64. package/skill/arc-templates/xianxia/tribulation.md +71 -0
  65. package/skill/docs/examples.md +61 -0
  66. package/skill/docs/faq.md +81 -0
  67. package/skill/docs/installation.md +87 -0
  68. package/skill/docs/quickstart.md +83 -0
  69. package/skill/genre-packs/README.md +47 -0
  70. package/skill/genre-packs/_default/arc-types.md +153 -0
  71. package/skill/genre-packs/_default/rules.md +56 -0
  72. package/skill/genre-packs/_default/templates.md +135 -0
  73. package/skill/genre-packs/apocalypse/arc-types.md +109 -0
  74. package/skill/genre-packs/apocalypse/rules.md +113 -0
  75. package/skill/genre-packs/apocalypse/settings.md +106 -0
  76. package/skill/genre-packs/apocalypse/templates.md +192 -0
  77. package/skill/genre-packs/game/arc-types.md +109 -0
  78. package/skill/genre-packs/game/rules.md +113 -0
  79. package/skill/genre-packs/game/settings.md +103 -0
  80. package/skill/genre-packs/game/templates.md +173 -0
  81. package/skill/genre-packs/history/arc-types.md +109 -0
  82. package/skill/genre-packs/history/rules.md +107 -0
  83. package/skill/genre-packs/history/settings.md +126 -0
  84. package/skill/genre-packs/history/templates.md +179 -0
  85. package/skill/genre-packs/infinite-flow/arc-types.md +101 -0
  86. package/skill/genre-packs/infinite-flow/rules.md +75 -0
  87. package/skill/genre-packs/infinite-flow/settings.md +102 -0
  88. package/skill/genre-packs/infinite-flow/templates.md +226 -0
  89. package/skill/genre-packs/mystery/arc-types.md +109 -0
  90. package/skill/genre-packs/mystery/rules.md +107 -0
  91. package/skill/genre-packs/mystery/settings.md +103 -0
  92. package/skill/genre-packs/mystery/templates.md +178 -0
  93. package/skill/genre-packs/romance/arc-types.md +130 -0
  94. package/skill/genre-packs/romance/rules.md +88 -0
  95. package/skill/genre-packs/romance/settings.md +146 -0
  96. package/skill/genre-packs/romance/templates.md +245 -0
  97. package/skill/genre-packs/sci-fi/arc-types.md +109 -0
  98. package/skill/genre-packs/sci-fi/rules.md +113 -0
  99. package/skill/genre-packs/sci-fi/settings.md +99 -0
  100. package/skill/genre-packs/sci-fi/templates.md +170 -0
  101. package/skill/genre-packs/urban/arc-types.md +101 -0
  102. package/skill/genre-packs/urban/rules.md +75 -0
  103. package/skill/genre-packs/urban/settings.md +82 -0
  104. package/skill/genre-packs/urban/templates.md +212 -0
  105. package/skill/genre-packs/western-fantasy/arc-types.md +128 -0
  106. package/skill/genre-packs/western-fantasy/rules.md +88 -0
  107. package/skill/genre-packs/western-fantasy/settings.md +160 -0
  108. package/skill/genre-packs/western-fantasy/templates.md +225 -0
  109. package/skill/genre-packs/wuxia/arc-types.md +126 -0
  110. package/skill/genre-packs/wuxia/rules.md +86 -0
  111. package/skill/genre-packs/wuxia/settings.md +150 -0
  112. package/skill/genre-packs/wuxia/templates.md +195 -0
  113. package/skill/genre-packs/xianxia/arc-types.md +101 -0
  114. package/skill/genre-packs/xianxia/rules.md +74 -0
  115. package/skill/genre-packs/xianxia/settings.md +107 -0
  116. package/skill/genre-packs/xianxia/templates.md +202 -0
  117. package/skill/hooks/README.md +102 -0
  118. package/skill/hooks/chapter-complete.md +176 -0
  119. package/skill/hooks/context-injection.md +152 -0
  120. package/skill/hooks/intent-detection.md +183 -0
  121. package/skill/hooks/review-trigger.md +219 -0
  122. package/skill/hooks/summary-trigger.md +185 -0
  123. package/skill/references/act-guidance.md +228 -0
  124. package/skill/references/audit-core.md +130 -0
  125. package/skill/references/audit-dimensions.md +202 -0
  126. package/skill/references/character-voice-card.md +196 -0
  127. package/skill/references/consistency-checker.md +209 -0
  128. package/skill/references/content-expansion.md +68 -0
  129. package/skill/references/creative-constraints.md +200 -0
  130. package/skill/references/data-agent.md +286 -0
  131. package/skill/references/dialogue-writing.md +104 -0
  132. package/skill/references/editorial-perspective.md +166 -0
  133. package/skill/references/emotion-curve.md +127 -0
  134. package/skill/references/genre-rules.md +389 -0
  135. package/skill/references/golden-opening.md +81 -0
  136. package/skill/references/memory-system.md +288 -0
  137. package/skill/references/pacing-analysis.md +201 -0
  138. package/skill/references/platform-rules.md +244 -0
  139. package/skill/references/plot-structures.md +108 -0
  140. package/skill/references/reader-feedback.md +119 -0
  141. package/skill/references/rhythm-system.md +204 -0
  142. package/skill/references/style-imitation.md +193 -0
  143. package/skill/references/sweet-spot-tracking.md +182 -0
  144. package/skill/references/usage-guide.md +174 -0
  145. package/skill/references/writing-methods.md +169 -0
  146. package/skill/rules/anti-ai-expressions.md +206 -0
  147. package/skill/rules/character-voice.md +184 -0
  148. package/skill/rules/consistency-check.md +232 -0
  149. package/skill/rules/smart-query.md +263 -0
  150. package/skill/scripts/README.md +380 -0
  151. package/skill/scripts/auditor/chapter_transition.py +217 -0
  152. package/skill/scripts/auditor/consistency_scan.py +194 -0
  153. package/skill/scripts/auditor/dialogue_checker.py +194 -0
  154. package/skill/scripts/auditor/hook_report.py +115 -0
  155. package/skill/scripts/auditor/pacing_optimizer.py +303 -0
  156. package/skill/scripts/auditor/pacing_report.py +275 -0
  157. package/skill/scripts/auditor/pre_audit.py +203 -0
  158. package/skill/scripts/auditor/style_check.py +158 -0
  159. package/skill/scripts/auditor/worldbuilding_checker.py +637 -0
  160. package/skill/scripts/common/analyze.py +129 -0
  161. package/skill/scripts/common/init_guide.py +796 -0
  162. package/skill/scripts/common/install.py +169 -0
  163. package/skill/scripts/common/nm_utils.py +296 -0
  164. package/skill/scripts/common/validate.py +215 -0
  165. package/skill/scripts/coordinator/stats_report.py +165 -0
  166. package/skill/scripts/coordinator/volume_batch.py +121 -0
  167. package/skill/scripts/planner/outline_extractor.py +89 -0
  168. package/skill/scripts/planner/planner_context.py +220 -0
  169. package/skill/scripts/planner/query_engine.py +289 -0
  170. package/skill/scripts/reviewer/chapter_diff.py +143 -0
  171. package/skill/scripts/reviewer/character_arc_tracker.py +191 -0
  172. package/skill/scripts/reviewer/emotion_curve.py +340 -0
  173. package/skill/scripts/reviewer/foreshadowing_tracker.py +286 -0
  174. package/skill/scripts/reviewer/subplot_tracker.py +207 -0
  175. package/skill/scripts/reviewer/summary_generator.py +130 -0
  176. package/skill/scripts/reviewer/truth_diff.py +227 -0
  177. package/skill/scripts/reviewer/truth_manager.py +120 -0
  178. package/skill/scripts/test_scripts.py +366 -0
  179. package/skill/scripts/writer/build_write_context.py +255 -0
  180. package/skill/scripts/writer/chapter_info.py +67 -0
  181. package/skill/scripts/writer/check_wordcount.py +115 -0
  182. package/skill/scripts/writer/scene_builder.py +227 -0
  183. package/skill/scripts/writer/style_anchor.py +135 -0
  184. package/skill/styles/author-styles.md +53 -0
  185. package/skill/styles/authors//344/270/245/350/260/250/350/256/276/345/256/232/346/265/201//345/277/230/350/257/255.md +110 -0
  186. package/skill/styles/authors//344/270/245/350/260/250/350/256/276/345/256/232/346/265/201//347/210/261/346/275/234/346/260/264/347/232/204/344/271/214/350/264/274.md +110 -0
  187. package/skill/styles/authors//344/270/245/350/260/250/350/256/276/345/256/232/346/265/201//350/250/200/345/275/222/346/255/243/344/274/240.md +110 -0
  188. package/skill/styles/authors//345/244/232/347/245/236/350/257/235/347/203/255/350/241/200/346/265/201//344/270/211/344/271/235/351/237/263/345/237/237.md +108 -0
  189. package/skill/styles/authors//346/202/254/347/226/221/346/216/250/347/220/206/346/265/201//346/235/200/350/231/253/351/230/237/351/230/237/345/221/230.md +108 -0
  190. package/skill/styles/authors//346/220/236/347/254/221/345/271/275/351/273/230/346/265/201//344/270/211/345/244/251/344/270/244/350/247/211.md +110 -0
  191. package/skill/styles/authors//346/220/236/347/254/221/345/271/275/351/273/230/346/265/201//344/274/232/350/257/264/350/257/235/347/232/204/350/202/230/345/255/220.md +110 -0
  192. package/skill/styles/authors//346/220/236/347/254/221/345/271/275/351/273/230/346/265/201//345/215/226/346/212/245/345/260/217/351/203/216/345/220/233.md +108 -0
  193. package/skill/styles/authors//346/220/236/347/254/221/345/271/275/351/273/230/346/265/201//345/274/210/351/235/222/345/263/260.md +123 -0
  194. package/skill/styles/authors//347/203/255/350/241/200/345/215/207/347/272/247/346/265/201//345/224/220/345/256/266/344/270/211/345/260/221.md +109 -0
  195. package/skill/styles/authors//347/203/255/350/241/200/345/215/207/347/272/247/346/265/201//345/244/251/350/232/225/345/234/237/350/261/206.md +110 -0
  196. package/skill/styles/authors//347/203/255/350/241/200/345/215/207/347/272/247/346/265/201//346/210/221/345/220/203/350/245/277/347/272/242/346/237/277.md +108 -0
  197. package/skill/styles/authors//347/203/255/350/241/200/345/215/207/347/272/247/346/265/201//346/273/232/345/274/200.md +109 -0
  198. package/skill/styles/authors//347/203/255/350/241/200/345/215/207/347/272/247/346/265/201//350/276/260/344/270/234.md +109 -0
  199. package/skill/styles/authors//347/211/271/350/211/262/351/242/206/345/237/237/346/265/201//345/244/251/344/270/213/351/234/270/345/224/261.md +110 -0
  200. package/skill/styles/authors//347/211/271/350/211/262/351/242/206/345/237/237/346/265/201//346/234/210/345/205/263.md +108 -0
  201. package/skill/styles/authors//347/211/271/350/211/262/351/242/206/345/237/237/346/265/201//350/220/247/351/274/216.md +109 -0
  202. package/skill/styles/authors//347/211/271/350/211/262/351/242/206/345/237/237/346/265/201//350/235/264/350/235/266/350/223/235.md +112 -0
  203. package/skill/styles/authors//347/273/206/350/205/273/346/226/207/351/235/222/346/265/201//346/204/244/346/200/222/347/232/204/351/246/231/350/225/211.md +109 -0
  204. package/skill/styles/authors//347/273/206/350/205/273/346/226/207/351/235/222/346/265/201//347/203/275/347/201/253/346/210/217/350/257/270/344/276/257.md +109 -0
  205. package/skill/styles/authors//347/273/206/350/205/273/346/226/207/351/235/222/346/265/201//347/214/253/350/205/273.md +110 -0
  206. package/skill/styles/authors//347/273/206/350/205/273/346/226/207/351/235/222/346/265/201//350/200/263/346/240/271.md +109 -0
  207. package/skill/templates/INDEX.md +48 -0
  208. package/skill/templates/act-plan.md +72 -0
  209. package/skill/templates/chapter.md +53 -0
  210. package/skill/templates/character-profile.md +107 -0
  211. package/skill/templates/character-voice.md +106 -0
  212. package/skill/templates/constitution.md +90 -0
  213. package/skill/templates/emotional-arcs.md +37 -0
  214. package/skill/templates/hook-template.md +68 -0
  215. package/skill/templates/outline.md +155 -0
  216. package/skill/templates/plot-card.md +432 -0
  217. package/skill/templates/power-system.md +124 -0
  218. package/skill/templates/presets.json +69 -0
  219. package/skill/templates/review-report.md +135 -0
  220. package/skill/templates/scene-plan.md +221 -0
  221. package/skill/templates/scene-template.md +78 -0
  222. package/skill/templates/subplot-board.md +48 -0
  223. package/skill/templates/summary-10chapters.md +79 -0
  224. package/skill/templates/summary-50chapters.md +131 -0
  225. package/skill/templates/summary-volume.md +148 -0
  226. package/skill/templates/timeline.md +37 -0
  227. package/skill/templates/volume-plan.md +44 -0
  228. package/skill/templates/world-setting.md +151 -0
@@ -0,0 +1,796 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ NovelMaker 交互式引导脚本
4
+
5
+ 使用方式:
6
+ python scripts/common/init_guide.py [--ide trae|claude] [--auto] [--verify]
7
+
8
+ 参数说明:
9
+ --ide: 指定IDE类型(trae 或 claude)
10
+ --auto: 自动模式,使用默认配置
11
+ --verify: 验证安装是否成功
12
+ """
13
+
14
+ import sys
15
+ import argparse
16
+ import json
17
+ from pathlib import Path
18
+
19
+
20
+ # 情绪标签选项
21
+ EMOTION_LABELS = {
22
+ "A": {"name": "打脸爽文", "desc": "扮猪吃虎、逆袭碾压", "interval": "≤3章"},
23
+ "B": {"name": "极致虐恋", "desc": "追妻火葬场", "interval": "5-8章"},
24
+ "C": {"name": "爆笑反套路", "desc": "沙雕吐槽、神转折", "interval": "3-5章"},
25
+ "D": {"name": "悬疑惊悚", "desc": "细思极恐、烧脑", "interval": "5-8章"},
26
+ "E": {"name": "治愈甜宠", "desc": "日常温馨、双向奔赴", "interval": "3-5章"},
27
+ "F": {"name": "脑洞大开", "desc": "系统流、末日囤货", "interval": "5-10章"},
28
+ }
29
+
30
+ # 文风推荐
31
+ STYLE_RECOMMENDATIONS = {
32
+ "玄幻修仙": ["天蚕土豆", "辰东"],
33
+ "都市搞笑": ["弈青峰", "会说话的肘子"],
34
+ "仙侠探案": ["卖报小郎君"],
35
+ "悬疑诡秘": ["爱潜水的乌贼"],
36
+ "历史权谋": ["猫腻", "愤怒的香蕉"],
37
+ "凡人流": ["忘语", "言归正传"],
38
+ "电竞网游": ["蝴蝶蓝"],
39
+ "无限流": ["三天两觉", "杀虫队队员"],
40
+ "盗墓探险": ["天下霸唱"],
41
+ "极道诡异": ["滚开"],
42
+ "稳健搞笑": ["言归正传"],
43
+ "多神话热血": ["三九音域"],
44
+ "悬疑推理": ["杀虫队队员"],
45
+ }
46
+
47
+
48
+ def load_presets():
49
+ """加载预设模板"""
50
+ presets_path = Path(__file__).parent.parent / "templates" / "presets.json"
51
+ try:
52
+ with open(presets_path, 'r', encoding='utf-8') as f:
53
+ data = json.load(f)
54
+ return data.get("presets", {})
55
+ except (FileNotFoundError, json.JSONDecodeError):
56
+ return {}
57
+
58
+
59
+ def show_progress(current_step, total_steps, step_name):
60
+ """显示进度指示器"""
61
+ progress = current_step / total_steps
62
+ bar_length = 30
63
+ filled_length = int(bar_length * progress)
64
+ bar = '█' * filled_length + '░' * (bar_length - filled_length)
65
+ print(f"\n进度:[{bar}] {current_step}/{total_steps} - {step_name}")
66
+
67
+
68
+ def select_preset_template(presets):
69
+ """选择预设模板"""
70
+ print("\n📋 可用预设模板:\n")
71
+ preset_list = list(presets.items())
72
+ for i, (key, preset) in enumerate(preset_list, 1):
73
+ print(f" {i}. {preset['name']} - {preset['description']}")
74
+ print()
75
+
76
+ while True:
77
+ choice = input(f"请选择预设模板 (1-{len(preset_list)}): ").strip()
78
+ try:
79
+ idx = int(choice) - 1
80
+ if 0 <= idx < len(preset_list):
81
+ key, preset = preset_list[idx]
82
+ print(f"\n✅ 已选择:{preset['name']}")
83
+ emotion_key = preset.get("emotion_label", "A")
84
+ emotion_data = EMOTION_LABELS.get(emotion_key, EMOTION_LABELS["A"])
85
+ protag = preset.get("protagonist_template", {})
86
+ config = {
87
+ "emotion_label": emotion_data,
88
+ "book_name": input("书名: ").strip() or preset.get("name", "示例"),
89
+ "genre": preset.get("genre", "玄幻修仙"),
90
+ "synopsis": input("一句话简介: ").strip() or f"{preset.get('name', '示例')}故事",
91
+ "protagonist": {
92
+ "name": input("主角姓名: ").strip() or "主角",
93
+ "contrast": protag.get("contrast", ""),
94
+ "goal": protag.get("goal", ""),
95
+ },
96
+ "conflict": preset.get("conflict_template", ""),
97
+ "chapters": preset.get("chapters", 100),
98
+ "style": preset.get("style", "天蚕土豆"),
99
+ }
100
+ return config
101
+ except ValueError:
102
+ print("❌ 无效选择,请重新输入")
103
+
104
+
105
+ def export_configuration(config, filename="novel-maker-config.json"):
106
+ """导出配置到JSON文件"""
107
+ export_config = config.copy()
108
+ if isinstance(export_config.get('emotion_label'), dict):
109
+ export_config['emotion_label'] = export_config['emotion_label']['name']
110
+ with open(filename, 'w', encoding='utf-8') as f:
111
+ json.dump(export_config, f, ensure_ascii=False, indent=2)
112
+ print(f"✅ 配置已导出到 {filename}")
113
+
114
+
115
+ def import_configuration(filename=None):
116
+ """从JSON文件导入配置"""
117
+ if filename is None:
118
+ filename = input("请输入配置文件路径: ").strip()
119
+ try:
120
+ with open(filename, 'r', encoding='utf-8') as f:
121
+ config = json.load(f)
122
+ if isinstance(config.get('emotion_label'), str):
123
+ matched = False
124
+ for label in EMOTION_LABELS.values():
125
+ if label['name'] == config['emotion_label']:
126
+ config['emotion_label'] = label
127
+ matched = True
128
+ break
129
+ if not matched:
130
+ print(f"⚠️ 未知的 emotion_label: {config['emotion_label']},使用默认值 A")
131
+ config['emotion_label'] = EMOTION_LABELS["A"]
132
+ print(f"✅ 已从 {filename} 导入配置")
133
+ return config
134
+ except FileNotFoundError:
135
+ print(f"❌ 文件 {filename} 不存在")
136
+ return None
137
+ except json.JSONDecodeError:
138
+ print(f"❌ 文件 {filename} 格式错误")
139
+ return None
140
+
141
+
142
+ # 题材包选项
143
+ GENRE_PACKS = {
144
+ "xianxia": {"name": "修仙", "desc": "境界体系、宗门设定、秘境模板、渡劫规则"},
145
+ "urban": {"name": "都市", "desc": "商战模板、复仇套路、崛起路径、感情线"},
146
+ "infinite-flow": {"name": "无限流", "desc": "副本设计、强化体系、团队配置、BOSS战"},
147
+ "mystery": {"name": "悬疑", "desc": "案件设计、推理线索、反转技巧、伏笔布局"},
148
+ "history": {"name": "历史", "desc": "权谋模板、战役描写、改革路线、朝堂设定"},
149
+ "sci-fi": {"name": "科幻", "desc": "科技体系、星际设定、AI描写、时间线管理"},
150
+ "game": {"name": "游戏", "desc": "游戏系统、等级体系、装备设计、副本攻略"},
151
+ "apocalypse": {"name": "末世", "desc": "生存体系、异能设定、势力分布、资源管理"},
152
+ "western-fantasy": {"name": "西幻", "desc": "魔法体系、种族设定、骑士精神、王国政治"},
153
+ "wuxia": {"name": "武侠", "desc": "武功体系、江湖规矩、侠义精神、门派纷争"},
154
+ "romance": {"name": "言情", "desc": "感情线设计、人物关系、情感冲突、甜蜜互动"},
155
+ }
156
+
157
+
158
+ def select_genre_pack():
159
+ """选择题材包"""
160
+ print("\n📋 选择题材包\n")
161
+ print("可选题材包:\n")
162
+
163
+ for key, value in GENRE_PACKS.items():
164
+ print(f" {key}: {value['name']} - {value['desc']}")
165
+
166
+ print()
167
+
168
+ while True:
169
+ choice = input("请输入题材包名称(如 xianxia)或直接输入自定义题材: ").strip()
170
+ if choice in GENRE_PACKS:
171
+ selected = GENRE_PACKS[choice]
172
+ print(f"\n✅ 已选择题材包:{selected['name']}")
173
+ return choice, selected['name']
174
+ elif choice:
175
+ print(f"\n✅ 使用自定义题材:{choice}")
176
+ return None, choice
177
+ else:
178
+ print("❌ 请输入题材包名称或自定义题材")
179
+
180
+
181
+ def custom_configuration():
182
+ """自定义配置流程(带进度指示器)"""
183
+ total_steps = 7
184
+ show_progress(1, total_steps, "选择情绪标签")
185
+ emotion_label = select_emotion_label()
186
+ show_progress(2, total_steps, "选择题材包")
187
+ genre_pack_key, genre_pack_name = select_genre_pack()
188
+ show_progress(3, total_steps, "填写书籍信息")
189
+ book_info = input_book_info()
190
+ show_progress(4, total_steps, "设置主角信息")
191
+ protagonist = input_protagonist()
192
+ show_progress(5, total_steps, "确定核心冲突")
193
+ conflict = input_conflict()
194
+ show_progress(6, total_steps, "设定章节数")
195
+ chapters = input_chapters()
196
+ show_progress(7, total_steps, "选择文风")
197
+ style = select_style(book_info["genre"])
198
+ return {
199
+ "emotion_label": emotion_label,
200
+ "genre_pack_key": genre_pack_key,
201
+ "genre_pack_name": genre_pack_name,
202
+ "book_name": book_info["book_name"],
203
+ "genre": book_info["genre"],
204
+ "synopsis": book_info["synopsis"],
205
+ "protagonist": protagonist,
206
+ "conflict": conflict,
207
+ "chapters": chapters,
208
+ "style": style,
209
+ }
210
+
211
+
212
+ def print_banner():
213
+ """打印欢迎横幅"""
214
+ print("\n" + "=" * 60)
215
+ print(" NovelMaker v2.0.0 - 全能网文写作助手")
216
+ print(" 6角色协作架构,用说话的方式写小说")
217
+ print("=" * 60 + "\n")
218
+
219
+
220
+ def detect_environment():
221
+ """检测当前环境"""
222
+ print("🔍 检测环境...\n")
223
+
224
+ # 检查是否在正确的目录
225
+ cwd = Path.cwd()
226
+ skill_dir = cwd / "skill"
227
+ if not skill_dir.exists():
228
+ print("❌ 错误:当前目录下未找到 skill/ 目录")
229
+ print(" 请确保在 NovelMaker 仓库根目录下运行此脚本")
230
+ return False
231
+
232
+ # 检查 SKILL.md 是否存在
233
+ skill_md = skill_dir / "SKILL.md"
234
+ if not skill_md.exists():
235
+ print("❌ 错误:skill/SKILL.md 不存在")
236
+ print(" 请确保技能文件完整")
237
+ return False
238
+
239
+ print("✅ 环境检测通过")
240
+ print(f" 当前目录:{cwd}")
241
+ print(f" 技能目录:{skill_dir}")
242
+ print()
243
+
244
+ return True
245
+
246
+
247
+ # 支持的 IDE 检测列表(与 install.py / bin/novel-maker.js 保持一致)
248
+ IDE_DETECT = [
249
+ ("Trae", "trae", [".trae"]),
250
+ ("Claude Code", "claude", [".claude"]),
251
+ ("Cursor", "cursor", [".cursor", ".cursorrules"]),
252
+ ("Windsurf", "windsurf", [".windsurf"]),
253
+ ("Gemini CLI", "gemini", ["GEMINI.md"]),
254
+ ("Codex CLI", "codex", [".codex"]),
255
+ ("OpenCode", "opencode", [".opencode"]),
256
+ ("Aider", "aider", [".aider"]),
257
+ ("Hermes Agent", "hermes", [".hermes", "HERMES.md"]),
258
+ ("Qwen Code", "qwen", [".qwen"]),
259
+ ("Claw Code", "claw", [".claw", "CLAW.md"]),
260
+ ("Qoder", "qoder", [".qoder"]),
261
+ ("Antigravity", "antigravity", [".agents"]),
262
+ ("OpenClaw", "openclaw", [".openclaw"]),
263
+ ("Kiro", "kiro", [".kiro"]),
264
+ ("VS Code", "vscode", [".github/copilot-instructions.md"]),
265
+ ("DeerFlow", "deerflow", ["deer_flow"]),
266
+ ("Copilot CLI", "copilot", [".claude"]),
267
+ ]
268
+
269
+
270
+ def detect_ide():
271
+ """检测IDE类型"""
272
+ print("🔍 检测IDE类型...\n")
273
+
274
+ cwd = Path.cwd()
275
+ detected = []
276
+ for name, key, markers in IDE_DETECT:
277
+ for marker in markers:
278
+ if (cwd / marker).exists():
279
+ detected.append((name, key))
280
+ break
281
+
282
+ if detected:
283
+ for name, _ in detected:
284
+ print(f"✅ 检测到 {name}")
285
+ return detected[0][1]
286
+
287
+ print("⚠️ 未检测到IDE类型,请手动指定")
288
+ return None
289
+
290
+
291
+ def select_emotion_label():
292
+ """选择情绪标签"""
293
+ print("\n📋 第一步:选择情绪标签\n")
294
+ print("请选择你想要的核心情绪体验:\n")
295
+
296
+ for key, value in EMOTION_LABELS.items():
297
+ print(f" {key}. {value['name']} - {value['desc']}")
298
+
299
+ print()
300
+
301
+ while True:
302
+ choice = input("请输入选项字母 (A-F): ").strip().upper()
303
+ if choice in EMOTION_LABELS:
304
+ selected = EMOTION_LABELS[choice]
305
+ print(f"\n✅ 已选择:{selected['name']}")
306
+ print(f" 说明:{selected['desc']}")
307
+ print(f" 爽点间隔:{selected['interval']}")
308
+ return selected
309
+ else:
310
+ print("❌ 无效选项,请重新输入")
311
+
312
+
313
+ def input_book_info():
314
+ """输入书籍信息"""
315
+ print("\n📋 第二步:填写书籍信息\n")
316
+
317
+ book_name = input("书名(如:废材逆天记): ").strip()
318
+ while not book_name:
319
+ print("❌ 书名不能为空")
320
+ book_name = input("书名(如:废材逆天记): ").strip()
321
+
322
+ genre = input("题材(如:玄幻修仙、都市搞笑): ").strip()
323
+ while not genre:
324
+ print("❌ 题材不能为空")
325
+ genre = input("题材(如:玄幻修仙、都市搞笑): ").strip()
326
+
327
+ synopsis = input("一句话简介(如:废材逆袭成仙帝): ").strip()
328
+ while not synopsis:
329
+ print("❌ 简介不能为空")
330
+ synopsis = input("一句话简介(如:废材逆袭成仙帝): ").strip()
331
+
332
+ print(f"\n✅ 书籍信息:")
333
+ print(f" 书名:{book_name}")
334
+ print(f" 题材:{genre}")
335
+ print(f" 简介:{synopsis}")
336
+
337
+ return {
338
+ "book_name": book_name,
339
+ "genre": genre,
340
+ "synopsis": synopsis,
341
+ }
342
+
343
+
344
+ def input_protagonist():
345
+ """输入主角信息"""
346
+ print("\n📋 第三步:设置主角信息\n")
347
+
348
+ name = input("主角姓名(如:林轩): ").strip()
349
+ while not name:
350
+ print("❌ 主角姓名不能为空")
351
+ name = input("主角姓名(如:林轩): ").strip()
352
+
353
+ contrast = input("核心反差点(如:表面废物实际天才): ").strip()
354
+ while not contrast:
355
+ print("❌ 核心反差点不能为空")
356
+ contrast = input("核心反差点(如:表面废物实际天才): ").strip()
357
+
358
+ goal = input("核心目标(如:成为仙帝): ").strip()
359
+ while not goal:
360
+ print("❌ 核心目标不能为空")
361
+ goal = input("核心目标(如:成为仙帝): ").strip()
362
+
363
+ print(f"\n✅ 主角信息:")
364
+ print(f" 姓名:{name}")
365
+ print(f" 反差点:{contrast}")
366
+ print(f" 目标:{goal}")
367
+
368
+ return {
369
+ "name": name,
370
+ "contrast": contrast,
371
+ "goal": goal,
372
+ }
373
+
374
+
375
+ def input_conflict():
376
+ """输入核心冲突"""
377
+ print("\n📋 第四步:确定核心冲突\n")
378
+
379
+ conflict = input("核心冲突(如:与宗门天才的宿命对决): ").strip()
380
+ while not conflict:
381
+ print("❌ 核心冲突不能为空")
382
+ conflict = input("核心冲突(如:与宗门天才的宿命对决): ").strip()
383
+
384
+ print(f"\n✅ 核心冲突:{conflict}")
385
+
386
+ return conflict
387
+
388
+
389
+ def input_chapters():
390
+ """输入章节数"""
391
+ print("\n📋 第五步:设定章节数\n")
392
+
393
+ while True:
394
+ try:
395
+ chapters = int(input("目标章节数(如:100): ").strip())
396
+ if chapters > 0:
397
+ print(f"\n✅ 目标章节数:{chapters}章")
398
+ return chapters
399
+ else:
400
+ print("❌ 章节数必须大于0")
401
+ except ValueError:
402
+ print("❌ 请输入有效的数字")
403
+
404
+
405
+ def select_style(genre):
406
+ """选择文风"""
407
+ print("\n📋 第六步:选择文风\n")
408
+
409
+ # 根据题材推荐文风
410
+ recommendations = STYLE_RECOMMENDATIONS.get(genre, [])
411
+ if recommendations:
412
+ print(f"根据题材「{genre}」,推荐以下文风:")
413
+ for i, style in enumerate(recommendations, 1):
414
+ print(f" {i}. {style}")
415
+ print()
416
+
417
+ print("可选文风:")
418
+ styles = [
419
+ "天蚕土豆", "辰东", "弈青峰", "会说话的肘子", "卖报小郎君",
420
+ "爱潜水的乌贼", "猫腻", "愤怒的香蕉", "忘语", "言归正传",
421
+ "蝴蝶蓝", "三天两觉", "杀虫队队员", "天下霸唱", "滚开", "三九音域"
422
+ ]
423
+ for i, style in enumerate(styles, 1):
424
+ print(f" {i}. {style}")
425
+
426
+ print()
427
+
428
+ while True:
429
+ choice = input("请输入文风名称或序号: ").strip()
430
+
431
+ # 检查是否是序号
432
+ try:
433
+ idx = int(choice) - 1
434
+ if 0 <= idx < len(styles):
435
+ selected = styles[idx]
436
+ print(f"\n✅ 已选择文风:{selected}")
437
+ return selected
438
+ except ValueError:
439
+ pass
440
+
441
+ # 检查是否是文风名称
442
+ if choice in styles:
443
+ print(f"\n✅ 已选择文风:{choice}")
444
+ return choice
445
+
446
+ print("❌ 无效选项,请重新输入")
447
+
448
+
449
+ def confirm_config(config):
450
+ """确认配置"""
451
+ print("\n" + "=" * 60)
452
+ print(" 配置摘要")
453
+ print("=" * 60 + "\n")
454
+
455
+ print(f" 情绪标签:{config['emotion_label']['name']}")
456
+ print(f" 书名:{config['book_name']}")
457
+ print(f" 题材:{config['genre']}")
458
+ print(f" 简介:{config['synopsis']}")
459
+ print(f" 主角:{config['protagonist']['name']}")
460
+ print(f" 反差点:{config['protagonist']['contrast']}")
461
+ print(f" 目标:{config['protagonist']['goal']}")
462
+ print(f" 核心冲突:{config['conflict']}")
463
+ print(f" 章节数:{config['chapters']}章")
464
+ print(f" 文风:{config['style']}")
465
+ print()
466
+
467
+ while True:
468
+ choice = input("确认配置?(y/n): ").strip().lower()
469
+ if choice in ["y", "yes", "是", "确认"]:
470
+ return True
471
+ elif choice in ["n", "no", "否", "取消"]:
472
+ return False
473
+ else:
474
+ print("❌ 请输入 y 或 n")
475
+
476
+
477
+ def generate_config_files(config):
478
+ """生成配置文件"""
479
+ print("\n📁 生成配置文件...\n")
480
+
481
+ try:
482
+ # 创建 .novel-maker 目录
483
+ NOVEL_MAKER_dir = Path(".novel-maker")
484
+ NOVEL_MAKER_dir.mkdir(exist_ok=True)
485
+
486
+ # 创建 truth-files 目录
487
+ truth_files_dir = NOVEL_MAKER_dir / "truth-files"
488
+ truth_files_dir.mkdir(exist_ok=True)
489
+
490
+ # 创建 memory 目录
491
+ memory_dir = NOVEL_MAKER_dir / "memory"
492
+ memory_dir.mkdir(exist_ok=True)
493
+
494
+ # 创建 novels 目录
495
+ novels_dir = Path("novels")
496
+ novels_dir.mkdir(exist_ok=True)
497
+
498
+ # 生成 constitution.md
499
+ constitution_content = f"""# 创作宪法
500
+
501
+ ## 基本信息
502
+
503
+ - **书名**:{config['book_name']}
504
+ - **题材**:{config['genre']}
505
+ - **情绪标签**:{config['emotion_label']['name']}
506
+ - **文风**:{config['style']}
507
+ - **目标章节数**:{config['chapters']}章
508
+
509
+ ## 一句话简介
510
+
511
+ {config['synopsis']}
512
+
513
+ ## 主角设定
514
+
515
+ - **姓名**:{config['protagonist']['name']}
516
+ - **核心反差点**:{config['protagonist']['contrast']}
517
+ - **核心目标**:{config['protagonist']['goal']}
518
+
519
+ ## 核心冲突
520
+
521
+ {config['conflict']}
522
+
523
+ ## 写作约束
524
+
525
+ - 字数范围:2500-3500字/章
526
+ - 平台适配:默认
527
+ - 节奏控制:S1-S5五级评级
528
+ - 质量审计:15维度核心审计
529
+
530
+ ## 情绪标签规则
531
+
532
+ - **爽点间隔**:{config['emotion_label']['interval']}
533
+ - **节奏要求**:根据情绪标签自动调整
534
+ """
535
+ constitution_path = memory_dir / "constitution.md"
536
+ constitution_path.write_text(constitution_content, encoding="utf-8")
537
+
538
+ # 生成 characters.md
539
+ characters_content = f"""# 角色档案
540
+
541
+ ## 主角
542
+
543
+ ### {config['protagonist']['name']}
544
+
545
+ - **核心反差点**:{config['protagonist']['contrast']}
546
+ - **核心目标**:{config['protagonist']['goal']}
547
+ - **当前状态**:初始状态
548
+ - **能力等级**:待设定
549
+ - **性格特点**:待设定
550
+
551
+ ## 配角
552
+
553
+ (待添加)
554
+
555
+ ## 反派
556
+
557
+ (待添加)
558
+ """
559
+ characters_path = truth_files_dir / "characters.md"
560
+ characters_path.write_text(characters_content, encoding="utf-8")
561
+
562
+ # 生成 current-state.md
563
+ current_state_content = f"""# 世界状态
564
+
565
+ ## 当前时间
566
+
567
+ - **卷**:第1卷
568
+ - **章**:第0章
569
+ - **幕**:第1幕
570
+
571
+ ## 主角状态
572
+
573
+ - **姓名**:{config['protagonist']['name']}
574
+ - **位置**:待设定
575
+ - **状态**:初始状态
576
+
577
+ ## 世界状态
578
+
579
+ (待更新)
580
+ """
581
+ current_state_path = truth_files_dir / "current-state.md"
582
+ current_state_path.write_text(current_state_content, encoding="utf-8")
583
+
584
+ # 生成 world-setting.md
585
+ world_setting_content = f"""# 世界观设定
586
+
587
+ ## 世界背景
588
+
589
+ (待设定)
590
+
591
+ ## 力量体系
592
+
593
+ (待设定)
594
+
595
+ ## 势力分布
596
+
597
+ (待设定)
598
+
599
+ ## 地理环境
600
+
601
+ (待设定)
602
+ """
603
+ world_setting_path = truth_files_dir / "world-setting.md"
604
+ world_setting_path.write_text(world_setting_content, encoding="utf-8")
605
+
606
+ # 生成 pending-hooks.md
607
+ pending_hooks_content = """# 伏笔表
608
+
609
+ ## 待回收伏笔
610
+
611
+ | ID | 伏笔内容 | 埋设章节 | 状态 | 优先级 |
612
+ |----|---------|---------|------|--------|
613
+ | H001 | (示例)主角隐藏血脉 | 第1章 | 待回收 | 高 |
614
+
615
+ ## 已回收伏笔
616
+
617
+ | ID | 伏笔内容 | 埋设章节 | 回收章节 | 说明 |
618
+ |----|---------|---------|---------|------|
619
+ """
620
+ pending_hooks_path = truth_files_dir / "pending-hooks.md"
621
+ pending_hooks_path.write_text(pending_hooks_content, encoding="utf-8")
622
+
623
+ # 生成 power-system.md
624
+ power_system_content = """# 力量体系
625
+
626
+ ## 等级划分
627
+
628
+ (待设定)
629
+
630
+ ## 能力类型
631
+
632
+ (待设定)
633
+
634
+ ## 突破条件
635
+
636
+ (待设定)
637
+
638
+ ## 特殊规则
639
+
640
+ (待设定)
641
+ """
642
+ power_system_path = truth_files_dir / "power-system.md"
643
+ power_system_path.write_text(power_system_content, encoding="utf-8")
644
+
645
+ print("✅ 配置文件生成完成")
646
+ print(f" .novel-maker/memory/constitution.md")
647
+ print(f" .novel-maker/truth-files/characters.md")
648
+ print(f" .novel-maker/truth-files/current-state.md")
649
+ print(f" .novel-maker/truth-files/world-setting.md")
650
+ print(f" .novel-maker/truth-files/pending-hooks.md")
651
+ print(f" .novel-maker/truth-files/power-system.md")
652
+
653
+ except OSError as e:
654
+ print(f"❌ 文件操作失败:{e}")
655
+ print(" 请检查磁盘空间和文件权限")
656
+ sys.exit(1)
657
+
658
+
659
+ def print_next_steps():
660
+ """打印下一步指引"""
661
+ print("\n" + "=" * 60)
662
+ print(" 🎉 初始化完成!")
663
+ print("=" * 60 + "\n")
664
+
665
+ print("接下来,你可以:\n")
666
+ print(" 1. 生成大纲")
667
+ print(" /novel-maker plan 帮我生成总大纲\n")
668
+ print(" 2. 开始写作")
669
+ print(" /novel-maker write 写第一章\n")
670
+ print(" 3. 查看帮助")
671
+ print(" /novel-maker help\n")
672
+ print(" 4. 查看快速上手教程")
673
+ print(" 参考 skill/docs/quickstart.md\n")
674
+
675
+
676
+ def verify_installation():
677
+ """验证安装"""
678
+ print("\n🔍 验证安装...\n")
679
+
680
+ # 检查 SKILL.md
681
+ skill_md = Path("skill/SKILL.md")
682
+ if skill_md.exists():
683
+ print("✅ skill/SKILL.md 存在")
684
+ else:
685
+ print("❌ skill/SKILL.md 不存在")
686
+ return False
687
+
688
+ # 检查 scripts 目录
689
+ scripts_dir = Path("skill/scripts")
690
+ if scripts_dir.exists():
691
+ print("✅ skill/scripts/ 目录存在")
692
+ else:
693
+ print("❌ skill/scripts/ 目录不存在")
694
+ return False
695
+
696
+ # 检查 QUICKSTART.md
697
+ quickstart_md = Path("skill/QUICKSTART.md")
698
+ if quickstart_md.exists():
699
+ print("✅ skill/QUICKSTART.md 存在")
700
+ else:
701
+ print("⚠️ skill/QUICKSTART.md 不存在(可选)")
702
+
703
+ print("\n✅ 安装验证通过")
704
+ return True
705
+
706
+
707
+ def main():
708
+ """主函数"""
709
+ parser = argparse.ArgumentParser(description="NovelMaker 交互式引导脚本")
710
+ parser.add_argument("--ide", choices=["trae", "claude"], help="指定IDE类型")
711
+ parser.add_argument("--auto", action="store_true", help="自动模式,使用默认配置")
712
+ parser.add_argument("--verify", action="store_true", help="验证安装是否成功")
713
+ parser.add_argument("--export", action="store_true", help="导出配置到文件")
714
+ parser.add_argument("--import", dest="import_file", help="从文件导入配置")
715
+ args = parser.parse_args()
716
+
717
+ print_banner()
718
+
719
+ # 验证模式
720
+ if args.verify:
721
+ success = verify_installation()
722
+ sys.exit(0 if success else 1)
723
+
724
+ # 检测环境
725
+ if not detect_environment():
726
+ sys.exit(1)
727
+
728
+ # 检测IDE
729
+ ide = args.ide or detect_ide()
730
+ if ide:
731
+ print(f" IDE类型:{ide}")
732
+ print()
733
+
734
+ # 自动模式
735
+ if args.auto:
736
+ print("🚀 自动模式:使用默认配置\n")
737
+ config = {
738
+ "emotion_label": EMOTION_LABELS["A"],
739
+ "book_name": "默认书名",
740
+ "genre": "玄幻修仙",
741
+ "synopsis": "默认简介",
742
+ "protagonist": {
743
+ "name": "主角",
744
+ "contrast": "表面废物实际天才",
745
+ "goal": "成为最强",
746
+ },
747
+ "conflict": "与强敌的宿命对决",
748
+ "chapters": 100,
749
+ "style": "天蚕土豆",
750
+ }
751
+ # 导入配置模式
752
+ elif args.import_file:
753
+ config = import_configuration(args.import_file)
754
+ if not config:
755
+ sys.exit(1)
756
+ else:
757
+ # 加载预设模板
758
+ presets = load_presets()
759
+
760
+ # 选择启动方式
761
+ print("📋 选择启动方式:\n")
762
+ print(" 1. 使用预设模板(推荐新手)")
763
+ print(" 2. 自定义配置(完全控制)")
764
+ print(" 3. 从现有配置导入")
765
+ print()
766
+
767
+ choice = input("请选择 (1/2/3): ").strip()
768
+
769
+ if choice == "1" and presets:
770
+ config = select_preset_template(presets)
771
+ elif choice == "3":
772
+ config = import_configuration()
773
+ if not config:
774
+ print("❌ 导入失败,切换到自定义配置")
775
+ config = custom_configuration()
776
+ else:
777
+ config = custom_configuration()
778
+
779
+ # 确认配置
780
+ if not confirm_config(config):
781
+ print("\n❌ 已取消配置")
782
+ sys.exit(0)
783
+
784
+ # 生成配置文件
785
+ generate_config_files(config)
786
+
787
+ # 导出配置(可选)
788
+ if args.export:
789
+ export_configuration(config)
790
+
791
+ # 打印下一步指引
792
+ print_next_steps()
793
+
794
+
795
+ if __name__ == "__main__":
796
+ main()