Ver código fonte

fix(scripts): 修复脚本路径硬编码问题,支持动态定位项目根目录

- webnovel-write.md: 添加 Step -1 环境设置,动态查找 .webnovel/ 目录
- workflow_manager.py: 添加 find_project_root() 函数,状态文件路径动态计算
- extract_entities.py: 用循环查找替代固定 parent.parent,适应任意目录深度

修复会话恢复后路径丢失和状态文件找不到的问题。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
lingfengQAQ 5 meses atrás
pai
commit
eec3d52772

+ 47 - 1
.claude/commands/webnovel-write.md

@@ -28,7 +28,53 @@ description: 按大纲创作指定章节的正文内容(3000-5000字),自
 
 ## Arguments
 
-- `chapter_num`: Chapter number to write (e.g., "45"). If not provided, ask the user.
+- `chapter_num`: Chapter number to write (e.g., "45").
+  - If user says "自动检测" or "下一章" → Auto-detect from state.json
+  - If not provided → Ask the user
+
+---
+
+## Step -1: Environment Setup (MANDATORY - BEFORE ALL STEPS)
+
+### 1. Locate Project Directory
+
+**YOU MUST find the `.webnovel/` directory first**:
+
+```
+Search order:
+1. Current working directory: ./.webnovel/
+2. webnovel-project subdirectory: ./webnovel-project/.webnovel/
+3. Parent directory: ../.webnovel/
+```
+
+**Set PROJECT_ROOT** to the directory containing `.webnovel/`:
+- If found at `./webnovel-project/.webnovel/` → `PROJECT_ROOT = ./webnovel-project`
+- All subsequent paths are relative to PROJECT_ROOT
+
+### 2. Auto-detect Chapter Number (if requested)
+
+**IF user requested auto-detection** ("自动检测" / "下一章" / no chapter specified):
+
+```bash
+# Read state.json to get current progress
+python -c "import json; s=json.load(open('PROJECT_ROOT/.webnovel/state.json')); print(s['progress']['current_chapter'] + 1)"
+```
+
+**Set chapter_num** = current_chapter + 1
+
+### 3. Validate Before Proceeding
+
+**Before Step 0, confirm**:
+- [ ] PROJECT_ROOT is set (contains `.webnovel/state.json`)
+- [ ] chapter_num is determined (from user or auto-detected)
+- [ ] Volume number is calculated: `volume_num = (chapter_num - 1) // 50 + 1`
+
+**Example Output**:
+```
+📍 项目目录: D:\wk\novel skill\webnovel-project
+📖 目标章节: 第2章 (第1卷)
+✅ 环境检查通过,开始执行 Step 0...
+```
 
 ---
 

+ 14 - 2
.claude/skills/webnovel-writer/scripts/extract_entities.py

@@ -578,8 +578,20 @@ def main():
         print("\n⚠️  Dry-run 模式,不执行实际写入")
         return
 
-    # 确定项目根目录
-    project_root = Path(chapter_file).parent.parent
+    # 确定项目根目录(动态查找 .webnovel/ 目录)
+    chapter_path = Path(chapter_file).resolve()
+    project_root = None
+    for parent in [chapter_path.parent] + list(chapter_path.parents):
+        if (parent / ".webnovel").exists():
+            project_root = parent
+            break
+
+    if project_root is None:
+        print(f"❌ 找不到 .webnovel 目录")
+        print(f"   搜索路径: {chapter_path.parent} 及其父目录")
+        print("请先运行 /webnovel-init 初始化项目")
+        sys.exit(1)
+
     state_file = project_root / ".webnovel/state.json"
 
     if not state_file.exists():

+ 26 - 4
.claude/skills/webnovel-writer/scripts/workflow_manager.py

@@ -26,6 +26,26 @@ if sys.platform == 'win32':
 
 WORKFLOW_STATE_FILE = '.webnovel/workflow_state.json'
 
+def find_project_root():
+    """动态查找包含 .webnovel/ 的项目根目录"""
+    # 优先检查环境变量
+    if os.environ.get('WEBNOVEL_PROJECT_ROOT'):
+        return Path(os.environ['WEBNOVEL_PROJECT_ROOT'])
+
+    # 从当前目录往上查找
+    current = Path.cwd()
+    for parent in [current] + list(current.parents):
+        if (parent / '.webnovel').exists():
+            return parent
+
+    # 默认返回当前目录(向后兼容)
+    return current
+
+def get_workflow_state_path():
+    """获取 workflow_state.json 的完整路径"""
+    project_root = find_project_root()
+    return project_root / '.webnovel' / 'workflow_state.json'
+
 def start_task(command, args):
     """开始新任务"""
     state = load_state()
@@ -360,21 +380,23 @@ def clear_current_task():
 
 def load_state():
     """加载workflow状态"""
-    if not os.path.exists(WORKFLOW_STATE_FILE):
+    state_file = get_workflow_state_path()
+    if not state_file.exists():
         return {'current_task': None, 'last_stable_state': None, 'history': []}
-    with open(WORKFLOW_STATE_FILE, 'r', encoding='utf-8') as f:
+    with open(state_file, 'r', encoding='utf-8') as f:
         return json.load(f)
 
 def save_state(state):
     """保存workflow状态"""
+    state_file = get_workflow_state_path()
     # ============================================================================
     # 安全修复:使用安全目录创建函数(P1 MEDIUM)
     # 原代码: os.makedirs(os.path.dirname(WORKFLOW_STATE_FILE), exist_ok=True)
     # 漏洞: 未设置权限,使用OS默认(可能为755,允许同组用户读取)
     # ============================================================================
-    create_secure_directory(os.path.dirname(WORKFLOW_STATE_FILE))
+    create_secure_directory(str(state_file.parent))
 
-    with open(WORKFLOW_STATE_FILE, 'w', encoding='utf-8') as f:
+    with open(state_file, 'w', encoding='utf-8') as f:
         json.dump(state, f, ensure_ascii=False, indent=2)
 
 def get_pending_steps(command):