|
|
@@ -2,115 +2,386 @@
|
|
|
# -*- coding: utf-8 -*-
|
|
|
"""
|
|
|
网文项目初始化脚本
|
|
|
-创建 AI 工作室所需的完整项目结构
|
|
|
+
|
|
|
+目标:
|
|
|
+- 生成可运行的项目结构(webnovel-project)
|
|
|
+- 创建/更新 .webnovel/state.json(运行时真相)
|
|
|
+- 生成基础设定集与大纲模板文件(供 /webnovel-plan 与 /webnovel-write 使用)
|
|
|
+
|
|
|
+说明:
|
|
|
+- 该脚本是命令 /webnovel-init 的“唯一允许的文件生成入口”(与命令文档保持一致)。
|
|
|
+- 生成的内容以“模板骨架”为主,便于 AI/作者后续补全;但保证所有关键文件存在。
|
|
|
"""
|
|
|
|
|
|
-import os
|
|
|
-import sys
|
|
|
+from __future__ import annotations
|
|
|
+
|
|
|
+import argparse
|
|
|
import json
|
|
|
import subprocess
|
|
|
-from pathlib import Path
|
|
|
+import sys
|
|
|
from datetime import datetime
|
|
|
+from pathlib import Path
|
|
|
+from typing import Any, Dict
|
|
|
+
|
|
|
|
|
|
# Windows 编码兼容性修复
|
|
|
-if sys.platform == 'win32':
|
|
|
+if sys.platform == "win32":
|
|
|
import io
|
|
|
- sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
|
- sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
|
|
|
|
|
-def init_project(project_dir, title, genre):
|
|
|
- """初始化项目结构"""
|
|
|
+ sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
|
|
|
+ sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")
|
|
|
+
|
|
|
|
|
|
- project_path = Path(project_dir)
|
|
|
+def _read_text_if_exists(path: Path) -> str:
|
|
|
+ if not path.exists():
|
|
|
+ return ""
|
|
|
+ return path.read_text(encoding="utf-8")
|
|
|
|
|
|
- # 创建目录结构
|
|
|
+
|
|
|
+def _write_text_if_missing(path: Path, content: str) -> None:
|
|
|
+ path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
+ if path.exists():
|
|
|
+ return
|
|
|
+ path.write_text(content, encoding="utf-8")
|
|
|
+
|
|
|
+
|
|
|
+def _ensure_state_schema(state: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
+ """确保 state.json 具备脚本族所需的最低字段集合(兼容旧版本)。"""
|
|
|
+ state.setdefault("project_info", {})
|
|
|
+ state.setdefault("progress", {})
|
|
|
+ state.setdefault("protagonist_state", {})
|
|
|
+ state.setdefault("relationships", {})
|
|
|
+ state.setdefault("world_settings", {"power_system": [], "factions": [], "locations": []})
|
|
|
+ state.setdefault("plot_threads", {"active_threads": [], "foreshadowing": []})
|
|
|
+ state.setdefault("review_checkpoints", [])
|
|
|
+ state.setdefault(
|
|
|
+ "strand_tracker",
|
|
|
+ {
|
|
|
+ "last_quest_chapter": 0,
|
|
|
+ "last_fire_chapter": 0,
|
|
|
+ "last_constellation_chapter": 0,
|
|
|
+ "current_dominant": "quest",
|
|
|
+ "chapters_since_switch": 0,
|
|
|
+ "history": [],
|
|
|
+ },
|
|
|
+ )
|
|
|
+ state.setdefault(
|
|
|
+ "entities",
|
|
|
+ {"characters": [], "locations": [], "items": [], "factions": [], "techniques": []},
|
|
|
+ )
|
|
|
+
|
|
|
+ # progress schema evolution
|
|
|
+ state["progress"].setdefault("current_chapter", 0)
|
|
|
+ state["progress"].setdefault("total_words", 0)
|
|
|
+ state["progress"].setdefault("last_updated", datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
|
|
+ state["progress"].setdefault("volumes_completed", [])
|
|
|
+ state["progress"].setdefault("current_volume", 1)
|
|
|
+ state["progress"].setdefault("volumes_planned", [])
|
|
|
+
|
|
|
+ # protagonist schema evolution
|
|
|
+ ps = state["protagonist_state"]
|
|
|
+ ps.setdefault("name", "")
|
|
|
+ ps.setdefault("power", {"realm": "", "layer": 1, "bottleneck": ""})
|
|
|
+ ps.setdefault("location", {"current": "", "last_chapter": 0})
|
|
|
+ ps.setdefault("golden_finger", {"name": "", "level": 1, "cooldown": 0, "skills": []})
|
|
|
+ ps.setdefault("attributes", {})
|
|
|
+
|
|
|
+ return state
|
|
|
+
|
|
|
+
|
|
|
+def _build_master_outline(target_chapters: int, *, chapters_per_volume: int = 50) -> str:
|
|
|
+ volumes = (target_chapters - 1) // chapters_per_volume + 1 if target_chapters > 0 else 1
|
|
|
+ lines: list[str] = [
|
|
|
+ "# 总纲",
|
|
|
+ "",
|
|
|
+ "> 本文件为“总纲骨架”,用于 /webnovel-plan 细化为卷大纲与章纲。",
|
|
|
+ "",
|
|
|
+ "## 卷结构",
|
|
|
+ "",
|
|
|
+ ]
|
|
|
+
|
|
|
+ for v in range(1, volumes + 1):
|
|
|
+ start = (v - 1) * chapters_per_volume + 1
|
|
|
+ end = min(v * chapters_per_volume, target_chapters)
|
|
|
+ lines.extend(
|
|
|
+ [
|
|
|
+ f"### 第{v}卷(第{start}-{end}章)",
|
|
|
+ "- 核心冲突:",
|
|
|
+ "- 关键爽点:",
|
|
|
+ "- 卷末高潮:",
|
|
|
+ "- 主要登场角色:",
|
|
|
+ "- 关键伏笔(埋/收):",
|
|
|
+ "",
|
|
|
+ ]
|
|
|
+ )
|
|
|
+
|
|
|
+ return "\n".join(lines).rstrip() + "\n"
|
|
|
+
|
|
|
+
|
|
|
+def init_project(
|
|
|
+ project_dir: str,
|
|
|
+ title: str,
|
|
|
+ genre: str,
|
|
|
+ *,
|
|
|
+ protagonist_name: str = "",
|
|
|
+ target_words: int = 2_000_000,
|
|
|
+ target_chapters: int = 600,
|
|
|
+ golden_finger_name: str = "",
|
|
|
+ golden_finger_type: str = "",
|
|
|
+ golden_finger_style: str = "",
|
|
|
+ core_selling_points: str = "",
|
|
|
+ protagonist_desire: str = "",
|
|
|
+ protagonist_flaw: str = "",
|
|
|
+ protagonist_archetype: str = "",
|
|
|
+ antagonist_level: str = "",
|
|
|
+ target_reader: str = "",
|
|
|
+ platform: str = "",
|
|
|
+) -> None:
|
|
|
+ project_path = Path(project_dir).expanduser().resolve()
|
|
|
+ project_path.mkdir(parents=True, exist_ok=True)
|
|
|
+
|
|
|
+ # 目录结构(同时兼容“卷目录”与后续扩展)
|
|
|
directories = [
|
|
|
".webnovel/backups",
|
|
|
+ ".webnovel/archive",
|
|
|
"设定集/角色库/主要角色",
|
|
|
"设定集/角色库/次要角色",
|
|
|
"设定集/角色库/反派角色",
|
|
|
+ "设定集/物品库",
|
|
|
+ "设定集/其他设定",
|
|
|
"大纲",
|
|
|
- "正文",
|
|
|
- "审查报告"
|
|
|
+ "正文/第1卷",
|
|
|
+ "审查报告",
|
|
|
]
|
|
|
-
|
|
|
for dir_path in directories:
|
|
|
(project_path / dir_path).mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
- # 创建 state.json(动态状态管理)
|
|
|
- state = {
|
|
|
- "project_info": {
|
|
|
+ # state.json(创建或增量补齐)
|
|
|
+ state_path = project_path / ".webnovel" / "state.json"
|
|
|
+ if state_path.exists():
|
|
|
+ try:
|
|
|
+ state: Dict[str, Any] = json.loads(state_path.read_text(encoding="utf-8"))
|
|
|
+ except json.JSONDecodeError:
|
|
|
+ state = {}
|
|
|
+ else:
|
|
|
+ state = {}
|
|
|
+
|
|
|
+ state = _ensure_state_schema(state)
|
|
|
+ created_at = state.get("project_info", {}).get("created_at") or datetime.now().strftime("%Y-%m-%d")
|
|
|
+
|
|
|
+ state["project_info"].update(
|
|
|
+ {
|
|
|
"title": title,
|
|
|
"genre": genre,
|
|
|
- "author": "",
|
|
|
- "created_at": datetime.now().strftime("%Y-%m-%d"),
|
|
|
- "target_words": 2000000,
|
|
|
- "target_chapters": 600
|
|
|
- },
|
|
|
- "progress": {
|
|
|
- "current_chapter": 0,
|
|
|
- "total_words": 0,
|
|
|
- "last_updated": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
|
- "volumes_completed": [],
|
|
|
- "current_volume": 1
|
|
|
- },
|
|
|
- "protagonist_state": {
|
|
|
- "name": "",
|
|
|
- "power": {
|
|
|
- "realm": "",
|
|
|
- "layer": 1,
|
|
|
- "bottleneck": ""
|
|
|
- },
|
|
|
- "location": {
|
|
|
- "current": "",
|
|
|
- "last_chapter": 0
|
|
|
- },
|
|
|
- "golden_finger": {
|
|
|
- "name": "",
|
|
|
- "level": 1,
|
|
|
- "cooldown": 0
|
|
|
- }
|
|
|
- },
|
|
|
- "relationships": {},
|
|
|
- "world_settings": {
|
|
|
- "power_system": [],
|
|
|
- "factions": [],
|
|
|
- "locations": []
|
|
|
- },
|
|
|
- "plot_threads": {
|
|
|
- "active_threads": [],
|
|
|
- "foreshadowing": []
|
|
|
- },
|
|
|
- "strand_tracker": {
|
|
|
- "last_quest_chapter": 0,
|
|
|
- "last_fire_chapter": 0,
|
|
|
- "last_constellation_chapter": 0,
|
|
|
- "current_dominant": "quest",
|
|
|
- "chapters_since_switch": 0,
|
|
|
- "history": []
|
|
|
- },
|
|
|
- "review_checkpoints": []
|
|
|
- }
|
|
|
-
|
|
|
- with open(project_path / ".webnovel/state.json", "w", encoding="utf-8") as f:
|
|
|
- json.dump(state, f, ensure_ascii=False, indent=2)
|
|
|
-
|
|
|
- # Git 初始化(自动版本控制)
|
|
|
- print("\n🔧 正在初始化 Git 仓库...")
|
|
|
- try:
|
|
|
- # Step 1: git init
|
|
|
- subprocess.run(
|
|
|
- ["git", "init"],
|
|
|
- cwd=project_path,
|
|
|
- check=True,
|
|
|
- capture_output=True,
|
|
|
- text=True
|
|
|
+ "created_at": created_at,
|
|
|
+ "target_words": int(target_words),
|
|
|
+ "target_chapters": int(target_chapters),
|
|
|
+ # 下面字段属于“初始化元信息”,不影响运行时脚本
|
|
|
+ "golden_finger_name": golden_finger_name,
|
|
|
+ "golden_finger_type": golden_finger_type,
|
|
|
+ "golden_finger_style": golden_finger_style,
|
|
|
+ "core_selling_points": core_selling_points,
|
|
|
+ "target_reader": target_reader,
|
|
|
+ "platform": platform,
|
|
|
+ }
|
|
|
+ )
|
|
|
+
|
|
|
+ if protagonist_name:
|
|
|
+ state["protagonist_state"]["name"] = protagonist_name
|
|
|
+
|
|
|
+ if golden_finger_name:
|
|
|
+ state["protagonist_state"]["golden_finger"]["name"] = golden_finger_name
|
|
|
+
|
|
|
+ # 确保 golden_finger 字段存在且可编辑
|
|
|
+ if not state["protagonist_state"]["golden_finger"].get("name"):
|
|
|
+ state["protagonist_state"]["golden_finger"]["name"] = "未命名金手指"
|
|
|
+
|
|
|
+ state["progress"]["last_updated"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
|
+ state_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
+ state_path.write_text(json.dumps(state, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
|
+
|
|
|
+ # 读取内置模板(可选)
|
|
|
+ script_dir = Path(__file__).resolve().parent
|
|
|
+ templates_dir = script_dir.parent / "templates"
|
|
|
+ genre_key = (genre or "").strip()
|
|
|
+ genre_template_key = {
|
|
|
+ "修仙/玄幻": "修仙",
|
|
|
+ "玄幻": "修仙",
|
|
|
+ }.get(genre_key, genre_key)
|
|
|
+ genre_template = _read_text_if_exists(templates_dir / "genres" / f"{genre_template_key}.md")
|
|
|
+ golden_finger_templates = _read_text_if_exists(templates_dir / "golden-finger-templates.md")
|
|
|
+
|
|
|
+ # 基础文件(只在缺失时生成,避免覆盖已有内容)
|
|
|
+ now = datetime.now().strftime("%Y-%m-%d")
|
|
|
+
|
|
|
+ _write_text_if_missing(
|
|
|
+ project_path / "设定集" / "世界观.md",
|
|
|
+ "\n".join(
|
|
|
+ [
|
|
|
+ "# 世界观",
|
|
|
+ "",
|
|
|
+ f"> 项目:{title}|题材:{genre}|创建:{now}",
|
|
|
+ "",
|
|
|
+ "## 一句话世界观",
|
|
|
+ "- (用一句话说明世界的核心规则与卖点)",
|
|
|
+ "",
|
|
|
+ "## 核心规则(设定即物理)",
|
|
|
+ "- 规则1:",
|
|
|
+ "- 规则2:",
|
|
|
+ "- 规则3:",
|
|
|
+ "",
|
|
|
+ "## 势力与地理(简版)",
|
|
|
+ "- 主要势力:",
|
|
|
+ "- 关键地点:",
|
|
|
+ "",
|
|
|
+ "## 参考题材模板(可删/可改)",
|
|
|
+ "",
|
|
|
+ (genre_template.strip() + "\n") if genre_template else "(未找到对应题材模板,可自行补充)\n",
|
|
|
+ ]
|
|
|
+ ),
|
|
|
+ )
|
|
|
+
|
|
|
+ _write_text_if_missing(
|
|
|
+ project_path / "设定集" / "力量体系.md",
|
|
|
+ "\n".join(
|
|
|
+ [
|
|
|
+ "# 力量体系",
|
|
|
+ "",
|
|
|
+ f"> 项目:{title}|题材:{genre}|创建:{now}",
|
|
|
+ "",
|
|
|
+ "## 等级/境界划分",
|
|
|
+ "- (列出从弱到强的等级,含突破条件与代价)",
|
|
|
+ "",
|
|
|
+ "## 技能/招式规则",
|
|
|
+ "- 获得方式:",
|
|
|
+ "- 成本与副作用:",
|
|
|
+ "- 进阶与组合:",
|
|
|
+ "",
|
|
|
+ "## 禁止事项(防崩坏)",
|
|
|
+ "- 未达等级不得使用高阶能力(设定即物理)",
|
|
|
+ "- 新增能力必须申报并入库(发明需申报)",
|
|
|
+ "",
|
|
|
+ ]
|
|
|
+ ),
|
|
|
+ )
|
|
|
+
|
|
|
+ _write_text_if_missing(
|
|
|
+ project_path / "设定集" / "主角卡.md",
|
|
|
+ "\n".join(
|
|
|
+ [
|
|
|
+ "# 主角卡",
|
|
|
+ "",
|
|
|
+ f"> 主角:{protagonist_name or '(待填写)'}|项目:{title}|创建:{now}",
|
|
|
+ "",
|
|
|
+ "## 三要素",
|
|
|
+ f"- 欲望:{protagonist_desire or '(待填写)'}",
|
|
|
+ f"- 弱点:{protagonist_flaw or '(待填写)'}",
|
|
|
+ f"- 人设类型:{protagonist_archetype or '(待填写)'}",
|
|
|
+ "",
|
|
|
+ "## 初始状态(开局)",
|
|
|
+ "- 身份:",
|
|
|
+ "- 资源:",
|
|
|
+ "- 约束:",
|
|
|
+ "",
|
|
|
+ "## 金手指概览",
|
|
|
+ f"- 称呼:{golden_finger_name or '(待填写)'}",
|
|
|
+ f"- 类型:{golden_finger_type or '(待填写)'}",
|
|
|
+ f"- 风格:{golden_finger_style or '(待填写)'}",
|
|
|
+ "- 成长曲线:",
|
|
|
+ "",
|
|
|
+ ]
|
|
|
+ ),
|
|
|
+ )
|
|
|
+
|
|
|
+ _write_text_if_missing(
|
|
|
+ project_path / "设定集" / "金手指设计.md",
|
|
|
+ "\n".join(
|
|
|
+ [
|
|
|
+ "# 金手指设计",
|
|
|
+ "",
|
|
|
+ f"> 项目:{title}|题材:{genre}|创建:{now}",
|
|
|
+ "",
|
|
|
+ "## 选型",
|
|
|
+ f"- 称呼:{golden_finger_name or '(待填写)'}",
|
|
|
+ f"- 类型:{golden_finger_type or '(待填写)'}",
|
|
|
+ f"- 风格:{golden_finger_style or '(待填写)'}",
|
|
|
+ "",
|
|
|
+ "## 规则(必须写清)",
|
|
|
+ "- 触发条件:",
|
|
|
+ "- 冷却/代价:",
|
|
|
+ "- 上限:",
|
|
|
+ "- 反噬/风险:",
|
|
|
+ "",
|
|
|
+ "## 成长曲线(章节规划)",
|
|
|
+ "- Lv1:",
|
|
|
+ "- Lv2:",
|
|
|
+ "- Lv3:",
|
|
|
+ "",
|
|
|
+ "## 模板参考(可删/可改)",
|
|
|
+ "",
|
|
|
+ (golden_finger_templates.strip() + "\n") if golden_finger_templates else "(未找到金手指模板库)\n",
|
|
|
+ ]
|
|
|
+ ),
|
|
|
+ )
|
|
|
+
|
|
|
+ if antagonist_level:
|
|
|
+ _write_text_if_missing(
|
|
|
+ project_path / "设定集" / "反派设计.md",
|
|
|
+ "\n".join(
|
|
|
+ [
|
|
|
+ "# 反派设计",
|
|
|
+ "",
|
|
|
+ f"> 项目:{title}|创建:{now}",
|
|
|
+ "",
|
|
|
+ f"- 反派等级:{antagonist_level}",
|
|
|
+ "- 动机:",
|
|
|
+ "- 资源/势力:",
|
|
|
+ "- 与主角的镜像关系:",
|
|
|
+ "- 终局:",
|
|
|
+ "",
|
|
|
+ ]
|
|
|
+ ),
|
|
|
)
|
|
|
|
|
|
- # Step 2: 创建 .gitignore
|
|
|
- gitignore_file = project_path / ".gitignore"
|
|
|
- with open(gitignore_file, 'w', encoding='utf-8') as f:
|
|
|
- f.write("""# Python
|
|
|
+ _write_text_if_missing(project_path / "大纲" / "总纲.md", _build_master_outline(int(target_chapters)))
|
|
|
+
|
|
|
+ _write_text_if_missing(
|
|
|
+ project_path / "大纲" / "爽点规划.md",
|
|
|
+ "\n".join(
|
|
|
+ [
|
|
|
+ "# 爽点规划",
|
|
|
+ "",
|
|
|
+ f"> 项目:{title}|题材:{genre}|创建:{now}",
|
|
|
+ "",
|
|
|
+ "## 核心卖点(来自初始化输入)",
|
|
|
+ f"- {core_selling_points or '(待填写,建议 1-3 条,用逗号分隔)'}",
|
|
|
+ "",
|
|
|
+ "## 密度目标(建议)",
|
|
|
+ "- 每章至少 1 个小爽点",
|
|
|
+ "- 每 5 章至少 1 个大爽点",
|
|
|
+ "",
|
|
|
+ "## 分布表(示例,可改)",
|
|
|
+ "",
|
|
|
+ "| 章节范围 | 主导爽点类型 | 备注 |",
|
|
|
+ "|---|---|---|",
|
|
|
+ "| 1-5 | 金手指/打脸/反转 | 开篇钩子 + 立人设 |",
|
|
|
+ "| 6-10 | 升级/收获 | 进入主线节奏 |",
|
|
|
+ "",
|
|
|
+ ]
|
|
|
+ ),
|
|
|
+ )
|
|
|
+
|
|
|
+ # Git 初始化(仅当项目目录内尚无 .git)
|
|
|
+ git_dir = project_path / ".git"
|
|
|
+ if not git_dir.exists():
|
|
|
+ print("\nInitializing Git repository...")
|
|
|
+ try:
|
|
|
+ subprocess.run(["git", "init"], cwd=project_path, check=True, capture_output=True, text=True)
|
|
|
+
|
|
|
+ gitignore_file = project_path / ".gitignore"
|
|
|
+ if not gitignore_file.exists():
|
|
|
+ gitignore_file.write_text(
|
|
|
+ """# Python
|
|
|
__pycache__/
|
|
|
*.py[cod]
|
|
|
*.so
|
|
|
@@ -127,52 +398,78 @@ __pycache__/
|
|
|
# Don't ignore .webnovel (we need to track state.json)
|
|
|
# But ignore cache files
|
|
|
.webnovel/context_cache.json
|
|
|
-""")
|
|
|
-
|
|
|
- # Step 3: git add .
|
|
|
- subprocess.run(
|
|
|
- ["git", "add", "."],
|
|
|
- cwd=project_path,
|
|
|
- check=True,
|
|
|
- capture_output=True
|
|
|
- )
|
|
|
+""",
|
|
|
+ encoding="utf-8",
|
|
|
+ )
|
|
|
|
|
|
- # Step 4: Initial commit
|
|
|
- subprocess.run(
|
|
|
- ["git", "commit", "-m", "Initial commit: Project initialized"],
|
|
|
- cwd=project_path,
|
|
|
- check=True,
|
|
|
- capture_output=True
|
|
|
- )
|
|
|
+ subprocess.run(["git", "add", "."], cwd=project_path, check=True, capture_output=True)
|
|
|
+ subprocess.run(
|
|
|
+ ["git", "commit", "-m", f"初始化网文项目:{title}"],
|
|
|
+ cwd=project_path,
|
|
|
+ check=True,
|
|
|
+ capture_output=True,
|
|
|
+ )
|
|
|
+ print("Git initialized.")
|
|
|
+ except FileNotFoundError:
|
|
|
+ print("Git not found; skipping Git initialization.")
|
|
|
+ except subprocess.CalledProcessError as e:
|
|
|
+ print(f"Git init failed (non-fatal): {e}")
|
|
|
|
|
|
- print("✅ Git 仓库已初始化(原子性版本控制已启用)")
|
|
|
-
|
|
|
- except subprocess.CalledProcessError as e:
|
|
|
- print(f"⚠️ Git 初始化失败(非致命): {e.stderr if hasattr(e, 'stderr') else str(e)}")
|
|
|
- print("💡 提示:可以稍后手动执行 git init")
|
|
|
- except FileNotFoundError:
|
|
|
- print("⚠️ 未检测到 Git(请安装 Git 以启用版本控制功能)")
|
|
|
-
|
|
|
- print(f"\n✅ 项目初始化完成:{project_path}")
|
|
|
- print(f"📁 项目目录已创建")
|
|
|
- print(f"💾 状态文件已保存:.webnovel/state.json")
|
|
|
- print(f"🔖 Git 版本控制已启用")
|
|
|
- print(f"\n📚 题材:{genre}")
|
|
|
- print(f"📖 标题:{title}")
|
|
|
- print(f"\n🎯 下一步:")
|
|
|
- print(f" 1. 编辑设定集中的文件")
|
|
|
- print(f" 2. 运行 /webnovel-plan 1 规划第一卷")
|
|
|
- print(f" 3. 运行 /webnovel-write 1 开始创作")
|
|
|
- print(f"\n💾 版本控制提示:")
|
|
|
- print(f" - 每章完成后自动 Git 备份")
|
|
|
- print(f" - 回滚命令:python backup_manager.py --rollback N")
|
|
|
- print(f" - 查看历史:python backup_manager.py --list")
|
|
|
+ print(f"\nProject initialized at: {project_path}")
|
|
|
+ print("Key files:")
|
|
|
+ print(" - .webnovel/state.json")
|
|
|
+ print(" - 设定集/世界观.md")
|
|
|
+ print(" - 设定集/力量体系.md")
|
|
|
+ print(" - 设定集/主角卡.md")
|
|
|
+ print(" - 设定集/金手指设计.md")
|
|
|
+ print(" - 大纲/总纲.md")
|
|
|
+ print(" - 大纲/爽点规划.md")
|
|
|
|
|
|
-if __name__ == "__main__":
|
|
|
- import sys
|
|
|
- if len(sys.argv) < 4:
|
|
|
- print("用法: python init_project.py <项目目录> <标题> <题材>")
|
|
|
- print("示例: python init_project.py ./my-novel '废柴崛起' '修仙'")
|
|
|
- sys.exit(1)
|
|
|
|
|
|
- init_project(sys.argv[1], sys.argv[2], sys.argv[3])
|
|
|
+def main() -> None:
|
|
|
+ parser = argparse.ArgumentParser(description="网文项目初始化脚本(生成项目结构 + state.json + 基础模板)")
|
|
|
+ parser.add_argument("project_dir", help="项目目录(建议 ./webnovel-project)")
|
|
|
+ parser.add_argument("title", help="小说标题")
|
|
|
+ parser.add_argument("genre", help="题材类型(如:修仙/系统流/都市异能/狗血言情/古言/现实题材/规则怪谈/知乎短篇)")
|
|
|
+
|
|
|
+ parser.add_argument("--protagonist-name", default="", help="主角姓名")
|
|
|
+ parser.add_argument("--target-words", type=int, default=2_000_000, help="目标总字数(默认 2000000)")
|
|
|
+ parser.add_argument("--target-chapters", type=int, default=600, help="目标总章节数(默认 600)")
|
|
|
+
|
|
|
+ parser.add_argument("--golden-finger-name", default="", help="金手指称呼/系统名(建议读者可见的代号)")
|
|
|
+ parser.add_argument("--golden-finger-type", default="", help="金手指类型(如 系统流/鉴定流/签到流)")
|
|
|
+ parser.add_argument("--golden-finger-style", default="", help="金手指风格(如 冷漠工具型/毒舌吐槽型)")
|
|
|
+ parser.add_argument("--core-selling-points", default="", help="核心卖点(逗号分隔)")
|
|
|
+
|
|
|
+ # 深度模式可选参数(用于预填模板)
|
|
|
+ parser.add_argument("--protagonist-desire", default="", help="主角核心欲望(深度模式)")
|
|
|
+ parser.add_argument("--protagonist-flaw", default="", help="主角性格弱点(深度模式)")
|
|
|
+ parser.add_argument("--protagonist-archetype", default="", help="主角人设类型(深度模式)")
|
|
|
+ parser.add_argument("--antagonist-level", default="", help="反派等级(深度模式)")
|
|
|
+ parser.add_argument("--target-reader", default="", help="目标读者(深度模式)")
|
|
|
+ parser.add_argument("--platform", default="", help="发布平台(深度模式)")
|
|
|
+
|
|
|
+ args = parser.parse_args()
|
|
|
+
|
|
|
+ init_project(
|
|
|
+ args.project_dir,
|
|
|
+ args.title,
|
|
|
+ args.genre,
|
|
|
+ protagonist_name=args.protagonist_name,
|
|
|
+ target_words=args.target_words,
|
|
|
+ target_chapters=args.target_chapters,
|
|
|
+ golden_finger_name=args.golden_finger_name,
|
|
|
+ golden_finger_type=args.golden_finger_type,
|
|
|
+ golden_finger_style=args.golden_finger_style,
|
|
|
+ core_selling_points=args.core_selling_points,
|
|
|
+ protagonist_desire=args.protagonist_desire,
|
|
|
+ protagonist_flaw=args.protagonist_flaw,
|
|
|
+ protagonist_archetype=args.protagonist_archetype,
|
|
|
+ antagonist_level=args.antagonist_level,
|
|
|
+ target_reader=args.target_reader,
|
|
|
+ platform=args.platform,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|