Procházet zdrojové kódy

chore: refine documentation migration, unify SQLite connection logging, and harden exception handling

lingfengQAQ před 2 měsíci
rodič
revize
70f9783261

+ 45 - 90
README.md

@@ -5,29 +5,36 @@
 [![Claude Code](https://img.shields.io/badge/Claude%20Code-Compatible-purple.svg)](https://claude.ai/claude-code)
 
 <a href="https://trendshift.io/repositories/22487" target="_blank"><img src="https://trendshift.io/api/badge/repositories/22487" alt="lingfengQAQ%2Fwebnovel-writer | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
-## 项目简单介绍
+## 这是什么?
 
-`Webnovel Writer` 是基于 Claude Code 的长篇网文创作系统,目标是降低 AI 写作中的“遗忘”和“幻觉”,支持长周期连载创作
+`Webnovel Writer` 是一个基于 Claude Code 的长篇网文创作系统。
 
-详细文档已拆分到 `docs/`:
+它的目标很简单:**让 AI 在写长篇小说时不乱编、不忘事**。
 
-- 架构与模块:`docs/architecture/overview.md`
-- 命令详解:`docs/guides/commands.md`
-- RAG 与配置:`docs/guides/rag-and-config.md`
-- 题材模板:`docs/guides/genres.md`
-- 运维与恢复:`docs/operations/operations.md`
-- 文档导航:`docs/README.md`
+系统会自动管理角色设定、剧情伏笔、世界观规则,让你可以安心连载几百章而不用担心前后矛盾。
+
+📖 详细文档在 `docs/` 目录:
+
+- [架构与模块](docs/architecture/overview.md) — 系统怎么工作的
+- [命令详解](docs/guides/commands.md) — 所有可用命令
+- [RAG 与配置](docs/guides/rag-and-config.md) — 检索和环境变量配置
+- [题材模板](docs/guides/genres.md) — 37 个内置网文题材
+- [运维与恢复](docs/operations/operations.md) — 项目结构与日常运维
+- [插件发版](docs/operations/plugin-release.md) — 发版流程
+- [文档导航](docs/README.md) — 所有文档索引
 
 ## 快速开始
 
-### 1) 安装插件(官方 Marketplace)
+### 1) 安装插件
+
+通过 Claude Code 官方 Marketplace 安装:
 
 ```bash
 claude plugin marketplace add lingfengQAQ/webnovel-writer --scope user
 claude plugin install webnovel-writer@webnovel-writer-marketplace --scope user
 ```
 
-> 仅当前项目生效时,将 `--scope user` 改为 `--scope project`。
+> 如果只想在当前项目生效,把 `--scope user` 改成 `--scope project`。
 
 ### 2) 安装 Python 依赖
 
@@ -35,27 +42,25 @@ claude plugin install webnovel-writer@webnovel-writer-marketplace --scope user
 python -m pip install -r https://raw.githubusercontent.com/lingfengQAQ/webnovel-writer/HEAD/requirements.txt
 ```
 
-说明:该入口会同时安装核心写作链路与 Dashboard 依赖。
-
 ### 3) 初始化小说项目
 
-在 Claude Code 中执行
+在 Claude Code 中输入
 
 ```bash
 /webnovel-init
 ```
 
-说明:`/webnovel-init` 会在当前 Workspace 下按书名创建 `PROJECT_ROOT`(子目录),并在 `workspace/.claude/.webnovel-current-project` 写入当前项目指针
+系统会引导你填写书名、题材、主角等信息,然后在当前工作区下创建项目目录
 
-### 4) 配置 RAG 环境(必做)
+### 4) 配置 RAG(必做)
 
-进入初始化后的书项目根目录,创建 `.env`
+进入书项目根目录,把配置模板复制为 `.env` 并填写 API Key
 
 ```bash
 cp .env.example .env
 ```
 
-最小配置示例
+最小配置:
 
 ```bash
 EMBED_BASE_URL=https://api-inference.modelscope.cn/v1
@@ -67,106 +72,56 @@ RERANK_MODEL=jina-reranker-v3
 RERANK_API_KEY=your_rerank_api_key
 ```
 
-### 5) 开始使用
-
-```bash
-/webnovel-plan 1
-/webnovel-write 1
-/webnovel-review 1-5
-```
-
-如需排查本地 CLI / 插件目录 / 项目根解析问题,可直接运行统一预检:
-
-```bash
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<WORKSPACE_ROOT>" preflight
-```
-
-### Story System Phase 1-4
-
-当前已经补齐合同种子、合同优先运行时、章节提交主链、统一事件主链四段:
-
-- `.story-system/MASTER_SETTING.json` / `chapters/` / `volumes/` / `reviews/`
-- `.story-system/commits/chapter_XXX.commit.json`
-- `.story-system/events/chapter_XXX.events.json`
-
-常用统一 CLI:
+### 5) 开始写作
 
 ```bash
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" story-system "玄幻退婚流" --persist
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" story-system "玄幻退婚流" --emit-runtime-contracts --chapter 12
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" chapter-commit --chapter 12 --review-result .webnovel/tmp/review.json --fulfillment-result .webnovel/tmp/fulfillment.json --disambiguation-result .webnovel/tmp/disambiguation.json --extraction-result .webnovel/tmp/extraction.json
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" story-events --health
+/webnovel-plan 1      # 规划第 1 卷大纲
+/webnovel-write 1     # 写第 1 章
+/webnovel-review 1-5  # 审查第 1-5 章
 ```
 
-### 6) 启动可视化面板(可选)
+### 6) 可视化面板(可选)
 
 ```bash
 /webnovel-dashboard
 ```
 
-说明:
-- Dashboard 为只读面板(项目状态、实体图谱、章节/大纲浏览、追读力查看)。
-- 前端构建产物已随插件发布,使用者无需本地 `npm build`。
+只读面板,可以浏览项目状态、实体图谱、章节内容和追读力数据。前端已随插件预构建,不需要本地 `npm build`。
 
 ### 7) Agent 模型设置(可选)
 
-本项目所有内置 Agent 默认配置为
+所有内置 Agent 默认继承当前会话模型:
 
 ```yaml
 model: inherit
 ```
 
-表示子 Agent 继承当前 Claude 会话所用模型。
-
-如果要单独给某个 Agent 指定模型,编辑对应文件(`webnovel-writer/agents/*.md`)的 frontmatter,例如:
+如需单独指定,编辑对应 `agents/*.md` 的 frontmatter:
 
 ```yaml
 ---
-name: context-agent
-description: ...
-tools: Read, Grep, Bash
-model: sonnet
+model: sonnet  # 可选:inherit / sonnet / opus / haiku
 ---
 ```
 
-常见可选值:`inherit` / `sonnet` / `opus` / `haiku`(以 Claude Code 当前支持为准)。
-
 ## 更新简介
 
-| 版本 | 说明 |
-|------|------|
-| **v6.0.0 (当前)** | 正式切换到 Story System 主链(Phase 1-4):合同种子 + 合同优先运行时 + 章节提交链 + 事件审计链;补齐 `chapter-commit` 与 `story-events` 统一 CLI,并加入基于 skills 实际调用流程的集成测试(含向量调用桩)。 |
-| **v5.5.5** | 新增长期记忆闭环:写前注入 `long_term_memory`,写后沉淀 `memory_facts` 到 `memory_scratchpad.json`;补齐 `memory` 运维子命令。 |
-| **v5.5.4** | 补齐写作链提示词强约束(流程硬约束、中文思维写作约束、Step 职责边界);统一中文化审查/润色/Agent 报告文案;清理文档内部版本号与版本历史,降低与插件发版版本混淆。 |
-| **v5.5.3** | 新增统一 `preflight` 预检命令;写作链 CLI 示例统一为 UTF-8 运行方式,收口文档中的长 shell 预检片段并降低 Windows 终端乱码风险。 |
-| **v5.5.2** | 支持将详细大纲中的章节名同步到正文文件名;修复 workflow_manager 在无参 find_project_root monkeypatch 下的兼容性问题。 |
-| **v5.5.1** | 修复卷级单文件大纲在上下文快照中的章节提取问题;补齐命令文档中遗漏的 `/webnovel-dashboard` 与 `/webnovel-learn`。 |
-| **v5.5.0** | 新增只读可视化 Dashboard Skill(`/webnovel-dashboard`)与实时刷新能力;支持插件目录启动与预构建前端分发 |
-| **v5.4.4** | 引入官方 Plugin Marketplace 安装机制;统一修复 Skills/Agents/References 的 CLI 调用(`CLAUDE_PLUGIN_ROOT` 单路径,透传命令统一 `--`) |
-| **v5.4.3** | 增强智能 RAG 上下文辅助(`auto/graph_hybrid` 回退 BM25) |
+| 版本 | 主要变化 |
+|------|----------|
+| **v6.0.0** | Story System 全链路上线(合同种子 + 运行时合同 + 章节提交 + 事件审计),补齐集成测试 |
+| **v5.5.5** | 长期记忆闭环:写前注入 + 写后沉淀,新增 `memory` 运维命令 |
+| **v5.5.4** | 写作链提示词强约束,统一中文化审查和报告文案 |
+| **v5.5.3** | 统一 `preflight` 预检命令,修复 Windows 终端编码问题 |
+| **v5.5.2** | 大纲章节名同步到正文文件名 |
+| **v5.5.1** | 修复卷级大纲上下文提取,补齐 Dashboard 和 Learn 命令文档 |
+| **v5.5.0** | 新增只读可视化 Dashboard,支持实时刷新 |
+| **v5.4.4** | 接入 Plugin Marketplace 安装机制 |
+| **v5.4.3** | 增强 RAG 智能上下文(`auto/graph_hybrid` 回退 BM25) |
 | **v5.3** | 引入追读力系统(Hook / Cool-point / 微兑现 / 债务追踪) |
 
-## 插件发版
-
-推荐使用 GitHub Actions 的 `Plugin Release` 工作流统一发版:
-
-1. 先在本地同步版本信息:
-   ```bash
-   python -X utf8 webnovel-writer/scripts/sync_plugin_version.py --version 6.0.0 --release-notes "本次版本说明"
-   ```
-2. 提交并推送版本变更(`README.md`、`plugin.json`、`marketplace.json`)。
-3. 打开仓库的 Actions 页面,选择 `Plugin Release`。
-4. 输入与当前仓库元数据一致的 `version`(例如 `6.0.0`)和用于 GitHub Release 的 `release_notes`。
-5. 工作流会执行以下动作:
-   - 校验 `plugin.json`、`marketplace.json` 与 README 当前版本已经一致
-   - 校验当前版本与输入的 `version` 一致
-   - 创建并推送 `vX.Y.Z` Tag
-   - 创建同名 GitHub Release
-
-日常开发中,`Plugin Version Check` 会在 Push / PR 时自动校验版本信息是否一致。
-
 ## 开源协议
-本项目使用 `GPL v3` 协议,详见 `LICENSE`。
+
+本项目使用 `GPL v3` 协议,详见 [LICENSE](LICENSE)。
 
 ## Star 历史
 

+ 23 - 21
docs/README.md

@@ -1,49 +1,51 @@
 # 文档中心
 
-`docs/` 目录已按稳定分区整理,避免所有说明文档平铺在根下
+`docs/` 目录按功能分区整理,方便查阅
 
 ## 目录索引
 
 ### 架构
 
-- [`architecture/overview.md`](./architecture/overview.md):系统架构、核心理念、Agent 分工与主数据链
-- [`architecture/current-system-diagnosis.md`](./architecture/current-system-diagnosis.md):当前实现状态的系统级诊断,聚焦读写链、提交语义与事实层问题
+- [`architecture/overview.md`](./architecture/overview.md):系统架构、Agent 分工、Story System 设计
+- [`architecture/current-system-diagnosis.md`](./architecture/current-system-diagnosis.md):当前系统状态诊断
 
 ### 使用指南
 
-- [`guides/commands.md`](./guides/commands.md):`/webnovel-*` 命令入口与常见用法
-- [`guides/rag-and-config.md`](./guides/rag-and-config.md):RAG 检索链路、环境变量与配置优先级
-- [`guides/genres.md`](./guides/genres.md):题材模板与复合题材规则说明
+- [`guides/commands.md`](./guides/commands.md):Skill 命令与 CLI 子命令速查
+- [`guides/rag-and-config.md`](./guides/rag-and-config.md):RAG 检索链路、环境变量与配置
+- [`guides/genres.md`](./guides/genres.md):37 个题材模板与复合题材规则
 
 ### 运维
 
-- [`operations/operations.md`](./operations/operations.md):项目目录、恢复、插件目录与运行期路径说明
+- [`operations/operations.md`](./operations/operations.md):项目目录结构、运维命令、备份恢复
+- [`operations/plugin-release.md`](./operations/plugin-release.md):插件发版流程与版本同步
 
 ### 记忆系统
 
-- [`memory/long-term-memory-architecture-v2.md`](./memory/long-term-memory-architecture-v2.md):基于当前代码状态的长期记忆现有架构说明
+- [`memory/long-term-memory-architecture-v2.md`](./memory/long-term-memory-architecture-v2.md):长期记忆架构说明
 
 ### 研究与外部方案
 
-- [`research/long-term-memory-research-report.md`](./research/long-term-memory-research-report.md):长期记忆论文、基准与开源项目调研
-- [`research/storyteller-paper-summary.md`](./research/storyteller-paper-summary.md):`STORYTELLER` 论文总结
+- [`research/long-term-memory-research-report.md`](./research/long-term-memory-research-report.md):长期记忆论文与开源方案调研
+- [`research/storyteller-paper-summary.md`](./research/storyteller-paper-summary.md):STORYTELLER 论文总结
 
 ### Specs
-- [`superpowers/README.md`](./superpowers/README.md):当前架构 spec 与设计文档导航
+
+- [`superpowers/README.md`](./superpowers/README.md):架构 spec 与设计文档导航
 
 ## 分类原则
 
-- `architecture/`:当前系统的稳定结构说明
-- `guides/`:使用者需要直接查阅的命令、配置、题材说明
-- `operations/`:运行、恢复、目录与部署相关手册
-- `memory/`:当前已实现的长期记忆架构说明
+- `architecture/`:系统结构与技术架构
+- `guides/`:使用者需要查阅的命令、配置、题材说明
+- `operations/`:运维、发版、备份与恢复
+- `memory/`:长期记忆架构说明
 - `research/`:论文总结与外部方案调研
-- `superpowers/`:当前仍保留的架构 spec 与设计收敛文档
+- `superpowers/`:架构 spec 与设计文档
 
 ## 推荐阅读顺序
 
-1. 先看 [`../README.md`](../README.md) 了解安装与基本使用
-2. 再看 [`architecture/overview.md`](./architecture/overview.md) 建立整体结构认知。
-3. 需要配置检索时看 [`guides/rag-and-config.md`](./guides/rag-and-config.md)
-4. 需要实际使用命令时看 [`guides/commands.md`](./guides/commands.md)
-5. 需要排查运行问题时看 [`operations/operations.md`](./operations/operations.md)
+1. 先看 [`../README.md`](../README.md) 了解安装与基本使用
+2. 再看 [`architecture/overview.md`](./architecture/overview.md) 了解整体架构
+3. 需要配置检索时看 [`guides/rag-and-config.md`](./guides/rag-and-config.md)
+4. 需要使用命令时看 [`guides/commands.md`](./guides/commands.md)
+5. 排查运行问题时看 [`operations/operations.md`](./operations/operations.md)

+ 34 - 23
docs/architecture/overview.md

@@ -5,15 +5,15 @@
 ### 防幻觉三定律
 
 | 定律 | 说明 | 执行方式 |
-|------|------|---------|
+|------|------|----------|
 | **大纲即法律** | 遵循大纲,不擅自发挥 | Context Agent 强制加载章节大纲 |
-| **设定即物理** | 遵守设定,不自相矛盾 | Consistency Checker 实时校验 |
+| **设定即物理** | 遵守设定,不自相矛盾 | Reviewer Agent 内置一致性审查 |
 | **发明需识别** | 新实体必须入库管理 | Data Agent 自动提取并消歧 |
 
 ### Strand Weave 节奏系统
 
 | Strand | 含义 | 理想占比 | 说明 |
-|--------|------|---------|------|
+|--------|------|----------|------|
 | **Quest** | 主线剧情 | 60% | 推动核心冲突 |
 | **Fire** | 感情线 | 20% | 人物关系发展 |
 | **Constellation** | 世界观扩展 | 20% | 背景/势力/设定 |
@@ -30,28 +30,39 @@
 ┌─────────────────────────────────────────────────────────────┐
 │                      Claude Code                           │
 ├─────────────────────────────────────────────────────────────┤
-│  Skills (7个): init / plan / write / review / query / ... │
+│  Skills (7个):                                             │
+│    init / plan / write / review / query / learn / dashboard │
 ├─────────────────────────────────────────────────────────────┤
-│  Agents (8个): Context / Data / 多维 Checker               │
+│  Agents (3个):                                             │
+│    Context Agent / Data Agent / Reviewer (含六维审查)        │
 ├─────────────────────────────────────────────────────────────┤
-│  Data Layer: state.json / index.db / vectors.db            │
+│  Data Layer:                                               │
+│    state.json / index.db (SQLite) / vectors.db             │
+├─────────────────────────────────────────────────────────────┤
+│  Story System:                                             │
+│    .story-system/ (合同·提交·事件)                           │
 └─────────────────────────────────────────────────────────────┘
 ```
 
-## 双 Agent 架构
+## Agent 分工
 
 ### Context Agent(读)
 
-职责:在写作前构建“创作任务书”,提供本章上下文、约束和追读力策略。
+- 文件:`agents/context-agent.md`
+- 职责:在写作前构建"创作任务书",提供本章上下文、约束和追读力策略。
 
 ### Data Agent(写)
 
-职责:从正文提取实体与状态变化,更新 `state.json`、`index.db`、`vectors.db`,保证数据链闭环。
+- 文件:`agents/data-agent.md`
+- 职责:从正文提取实体与状态变化,更新 `state.json`、`index.db`、`vectors.db`,保证数据链闭环。
+
+### Reviewer(审)
 
-## 六维并行审查
+- 文件:`agents/reviewer.md`
+- 职责:章节质量审查,内部包含以下六个审查维度:
 
-| Checker | 检查重点 |
-|---------|---------|
+| 审查维度 | 检查重点 |
+|----------|----------|
 | High-point Checker | 爽点密度与质量 |
 | Consistency Checker | 设定一致性(战力/地点/时间线) |
 | Pacing Checker | Strand 比例与断档 |
@@ -59,27 +70,27 @@
 | Continuity Checker | 场景与叙事连贯性 |
 | Reader-pull Checker | 钩子强度、期待管理、追读力 |
 
-## Story System Phase 1-4
+## Story System(合同驱动体系)
 
-Story System 现在以 `.story-system/` 为独立运行面,分四段递进:
+Story System 以 `.story-system/` 为独立运行面,分四段递进:
 
-1. Phase 1:`MASTER_SETTING / chapter brief / anti_patterns`
-2. Phase 2:`VOLUME_BRIEF / REVIEW_CONTRACT / prewrite validation`
-3. Phase 3:`CHAPTER_COMMIT + state/index/summary/memory` 投影
-4. Phase 4:`story_events + amend proposal + override ledger`
+1. **Phase 1**:合同种子 — `MASTER_SETTING.json` + 章节合同 + 反模式配置
+2. **Phase 2**:合同优先运行时 — 卷合同 (`volumes/`) + 审查合同 (`reviews/`) + 写前校验
+3. **Phase 3**:章节提交链 — `commits/chapter_XXX.commit.json` + state/index/summary/memory 投影
+4. **Phase 4**:事件审计链 — `events/chapter_XXX.events.json` + 修订提案 + 覆写账本
 
 核心链路:
 
 ```text
 story-system --persist
-    -> 合同种子
+    -> 写入合同种子(MASTER_SETTING.json 等)
 story-system --emit-runtime-contracts --chapter N
-    -> runtime contracts + prewrite validation
+    -> 生成运行时合同 + 写前校验
 chapter-commit --chapter N
-    -> commit + projections
+    -> 提交 commit + 执行各投影写入
 story-events --chapter N / --health
-    -> event 审计与健康检查
+    -> 事件审计与健康检查
 ```
 
-其中 Phase 4 不起第二套投影循环,事件路由仅负责声明式激活 writer,
+其中 Phase 4 不起第二套投影循环,事件路由仅负责声明式激活 writer,
 实际执行入口仍是 `ChapterCommitService.apply_projections()`。

+ 81 - 71
docs/guides/commands.md

@@ -1,81 +1,67 @@
 # 命令详解
 
-## `/webnovel-init`
+## Skill 命令(在 Claude Code 中使用)
 
-用途:初始化小说项目(目录、设定模板、状态文件)。
+### `/webnovel-init`
 
-产出:
+初始化小说项目,生成目录结构、设定模板和状态文件。
 
-- `.webnovel/state.json`
-- `设定集/`
-- `大纲/总纲.md`
+产出:
 
-## `/webnovel-plan [卷号]`
+- `.webnovel/state.json`(运行时状态)
+- `设定集/`(世界观、力量体系、主角卡、金手指设计、反派设计等)
+- `大纲/总纲.md`、`大纲/爽点规划.md`
+- `.env.example`(RAG 配置模板)
 
-用途:生成卷级规划与章节大纲。
+### `/webnovel-plan [卷号]`
 
-示例:
+生成卷级规划与章节大纲。
 
 ```bash
 /webnovel-plan 1
 /webnovel-plan 2-3
 ```
 
-## `/webnovel-write [章号]`
+### `/webnovel-write [章号]`
 
-用途:执行完整章节创作流程(上下文 → 草稿 → 审查 → 润色 → 数据落盘)。
-
-示例:
+执行完整章节创作流程(上下文构建 → 草稿 → 审查 → 润色 → 数据落盘)。
 
 ```bash
 /webnovel-write 1
 /webnovel-write 45
 ```
 
-常见模式:
-
-- 标准模式:全流程
-- 快速模式:`--fast`
-- 极简模式:`--minimal`
+### `/webnovel-review [范围]`
 
-## `/webnovel-review [范围]`
-
-用途:对历史章节做多维质量审查。
-
-示例:
+对已有章节做多维质量审查。
 
 ```bash
 /webnovel-review 1-5
 /webnovel-review 45
 ```
 
-## `/webnovel-query [关键词]`
+### `/webnovel-query [关键词]`
 
-用途:查询角色、伏笔、节奏、状态等运行时信息。
-
-示例:
+查询角色、伏笔、节奏、状态等运行时信息。
 
 ```bash
 /webnovel-query 萧炎
 /webnovel-query 伏笔
-/webnovel-query 紧急
 ```
 
-## `/webnovel-resume`
-
-用途:任务中断后自动识别断点并恢复。
+### `/webnovel-learn [内容]`
 
-示例:
+从当前会话或用户输入中提取可复用写作模式,写入项目记忆。
 
 ```bash
-/webnovel-resume
+/webnovel-learn "本章的危机钩设计很有效,悬念拉满"
 ```
 
-## `/webnovel-dashboard`
+产出:`.webnovel/project_memory.json`
 
-用途:启动只读可视化面板,查看项目状态、实体关系、章节与大纲内容。
+### `/webnovel-dashboard`
 
-示例:
+启动只读可视化面板,查看项目状态、实体关系、章节与大纲内容。
 
 ```bash
 /webnovel-dashboard
@@ -84,63 +70,87 @@
 说明:
 
 - 默认只读,不会修改项目文件
-- 适合排查上下文、实体关系和章节进度
+- 前端构建产物已随插件发布,无需本地 `npm build`
 
-## `/webnovel-learn [内容]`
+## 统一 CLI(命令行使用)
 
-用途:从当前会话或用户输入中提取可复用写作模式,并写入项目记忆。
-
-示例:
+所有 CLI 命令的入口都是 `webnovel.py`,格式:
 
 ```bash
-/webnovel-learn "本章的危机钩设计很有效,悬念拉满"
+python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" <子命令> [参数]
 ```
 
-产出:
-
-- `.webnovel/project_memory.json`
-
-## 长期记忆运维命令(CLI)
-
-用途:查看、查询、回填和手动更新长期记忆(`memory_scratchpad.json`)。
+### 常用工具子命令
+
+| 子命令 | 说明 |
+|--------|------|
+| `where` | 打印当前解析出的项目根目录 |
+| `preflight` | 校验 CLI 环境、脚本路径和项目根是否可用 |
+| `use <路径>` | 绑定当前工作区使用的书项目 |
+
+### 数据模块子命令
+
+| 子命令 | 说明 |
+|--------|------|
+| `index` | 索引管理(`process-chapter`、`stats` 等) |
+| `state` | 状态管理 |
+| `rag` | RAG 向量索引(`index-chapter`、`stats` 等) |
+| `entity` | 实体链接 |
+| `context` | 上下文管理 |
+| `style` | 风格采样 |
+| `migrate` | state.json → SQLite 迁移 |
+
+### 运维子命令
+
+| 子命令 | 说明 |
+|--------|------|
+| `status` | 健康报告(`--focus all` / `--focus urgency`) |
+| `update-state` | 手动更新状态 |
+| `backup` | 备份管理 |
+| `archive` | 归档管理 |
+| `extract-context` | 提取章节上下文(`--chapter N --format json`) |
+
+### 长期记忆子命令
+
+| 子命令 | 说明 |
+|--------|------|
+| `memory stats` | 查看总量、分类统计 |
+| `memory query` | 按 category/subject/status 过滤查询 |
+| `memory dump` | 导出完整 scratchpad 内容 |
+| `memory conflicts` | 查看同主键 active 冲突项 |
+| `memory bootstrap` | 从 index.db 与 summaries 回填初始长期记忆 |
+| `memory update` | 对指定章节结果执行手动映射写入 |
 
 示例:
 
 ```bash
 python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" memory stats
 python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" memory query --category character_state --subject xiaoyan
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" memory dump
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" memory conflicts
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" memory bootstrap
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" memory update --chapter 100 --data '{...}'
 ```
 
-常用子命令:
-
-- `memory stats`:查看总量、状态分布、分类统计
-- `memory query`:按 `category/subject/status` 过滤查询
-- `memory dump`:导出完整 scratchpad 内容
-- `memory conflicts`:查看同主键 active 冲突项
-- `memory bootstrap`:从 `index.db` 与 `summaries` 回填初始长期记忆
-- `memory update`:对指定章节结果执行一次手动映射写入
-
-## Story System 统一 CLI
+### Story System 子命令
 
-用途:管理合同、提交链与事件审计。
+| 子命令 | 说明 |
+|--------|------|
+| `story-system "<题材>" --persist` | 写入合同种子(`MASTER_SETTING.json` 等) |
+| `story-system "<题材>" --emit-runtime-contracts --chapter N` | 生成运行时合同 + 写前校验 |
+| `chapter-commit --chapter N` | 提交章节 commit(可附带 review/fulfillment/disambiguation/extraction 结果) |
+| `story-events --chapter N` | 查询指定章节事件 |
+| `story-events --health` | 事件链健康检查 |
+| `memory-contract` | 记忆合同管理 |
+| `review-pipeline --chapter N --review-results <file>` | 审查流水线 |
 
 示例:
 
 ```bash
 python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" story-system "玄幻退婚流" --persist
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" story-system "玄幻退婚流" --emit-runtime-contracts --chapter 12
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" chapter-commit --chapter 12 --review-result .webnovel/tmp/review.json --fulfillment-result .webnovel/tmp/fulfillment.json --disambiguation-result .webnovel/tmp/disambiguation.json --extraction-result .webnovel/tmp/extraction.json
-python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" story-events --chapter 12
+python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" chapter-commit --chapter 12 --review-result .webnovel/tmp/review.json
 python -X utf8 "<CLAUDE_PLUGIN_ROOT>/scripts/webnovel.py" --project-root "<PROJECT_ROOT>" story-events --health
 ```
 
 产物:
 
-- `story-system --persist`:写入 `.story-system/MASTER_SETTING.json`
-- `--emit-runtime-contracts`:写入 `volumes/*.json` 与 `reviews/*.review.json`
-- `chapter-commit`:写入 `commits/*.commit.json`
-- `story-events`读取 `events/*.events.json` 或 `index.db.story_events`
+- `story-system --persist` `.story-system/MASTER_SETTING.json`
+- `--emit-runtime-contracts` `volumes/*.json` 与 `reviews/*.review.json`
+- `chapter-commit` `commits/*.commit.json`
+- `story-events`读取 `events/*.events.json` 或 `index.db.story_events`

+ 51 - 37
docs/guides/genres.md

@@ -1,47 +1,61 @@
 # 题材模板说明
 
-系统内置 37+ 网文题材模板,支持单题材与复合题材。
-
-## 玄幻修仙类(示例)
-
-- 修仙
-- 系统流
-- 高武
-- 西幻
-- 无限流
-- 末世
-- 科幻
-
-## 都市现代类(示例)
-
-- 都市异能
-- 都市日常
-- 都市脑洞
-- 现实题材
-- 电竞
-- 直播文
-
-## 言情类(示例)
-
-- 古言
-- 宫斗宅斗
-- 青春甜宠
-- 豪门总裁
-- 职场婚恋
-- 民国言情
-- 幻想言情
-- 现言脑洞
-- 女频悬疑
-- 种田
-- 年代
+## 概述
+
+系统内置 37 个网文题材模板(位于 `templates/genres/`),涵盖主流网文题材。
+支持单题材与复合题材(最多 2 个)。
+
+## 支持的题材分类
+
+### 玄幻修仙类
+
+修仙、系统流、高武、西幻、无限流、末世、科幻
+
+### 都市现代类
+
+都市异能、都市日常、都市脑洞、现实题材、电竞、直播文
+
+### 言情类
+
+古言、宫斗宅斗、青春甜宠、豪门总裁、职场婚恋、民国言情、幻想言情、现言脑洞、女频悬疑、种田、年代
+
+### 其他题材
+
+规则怪谈、悬疑脑洞、悬疑灵异、克苏鲁、狗血言情、替身文、知乎短篇 等
+
+## 题材别名
+
+系统会自动识别常见别名,例如:
+
+| 输入 | 自动映射为 |
+|------|-----------|
+| 玄幻、修真、玄幻修仙 | 修仙 |
+| 都市修真 | 都市异能 |
+| 游戏电竞、电竞文 | 电竞 |
+| 直播、主播、直播带货 | 直播文 |
+| 克系、克系悬疑 | 克苏鲁 |
 
 ## 复合题材规则
 
-- 支持 `题材A+题材B`(最多 2 个)
-- 建议主辅比例 7:3
-- 主线遵循主题材逻辑,副题材提供钩子/规则/爽点
+- 用 `+` 连接两个题材(如 `都市脑洞+规则怪谈`),也支持 `/`、`、`、`与` 分隔
+- 最多组合 2 个题材
+- 建议主辅比例 7:3:主线遵循主题材逻辑,副题材提供钩子/规则/爽点
 
 示例:
 
 - `都市脑洞+规则怪谈`
 - `修仙+系统流`
+- `古言+宫斗宅斗`
+
+## 题材模板产物
+
+初始化项目时,指定的题材模板内容会自动注入到 `设定集/世界观.md` 的"参考题材模板"章节中。
+
+另外,系统内还有 6 个精调题材配置目录(`genres/`),用于为特定题材提供更细粒度的写作指导:
+
+- `xuanhuan`(玄幻)
+- `dog-blood-romance`(狗血言情)
+- `period-drama`(年代)
+- `realistic`(现实题材)
+- `rules-mystery`(规则怪谈)
+- `zhihu-short`(知乎短篇)

+ 28 - 10
docs/guides/rag-and-config.md

@@ -1,25 +1,42 @@
 # RAG 与配置说明
 
-## RAG 检索架构
+## RAG 检索流程
+
+系统在写作时自动从历史章节中检索相关内容,辅助保持一致性。
 
 ```text
 查询 → QueryRouter(auto) → vector / bm25 / hybrid / graph_hybrid
                      └→ RRF 融合 + Rerank → Top-K
 ```
 
-默认模型:
+- 默认模式为 `auto`:优先用向量检索,失败时自动回退到 BM25
+- `graph_hybrid` 模式会叠加实体图谱关联
+
+### 默认模型
 
-- Embedding:`Qwen/Qwen3-Embedding-8B`
-- Reranker:`jina-reranker-v3`
+| 组件 | 默认模型 |
+|------|----------|
+| Embedding | `Qwen/Qwen3-Embedding-8B`(ModelScope 托管) |
+| Reranker | `jina-reranker-v3`(Jina AI 托管) |
 
 ## 环境变量加载顺序
 
-1. 进程环境变量(最高优先级)
-2. 书项目根目录下的 `.env`
-3. 用户级全局:`~/.claude/webnovel-writer/.env`
+系统按以下优先级加载配置(靠前的优先):
+
+1. **进程环境变量**(最高优先级)
+2. **书项目根目录**下的 `.env`
+3. **用户级全局**:`~/.claude/webnovel-writer/.env`
 
 ## `.env` 最小配置
 
+初始化项目后会自动生成 `.env.example`,复制为 `.env` 后填写 API Key 即可:
+
+```bash
+cp .env.example .env
+```
+
+必填内容:
+
 ```bash
 EMBED_BASE_URL=https://api-inference.modelscope.cn/v1
 EMBED_MODEL=Qwen/Qwen3-Embedding-8B
@@ -30,7 +47,8 @@ RERANK_MODEL=jina-reranker-v3
 RERANK_API_KEY=your_rerank_api_key
 ```
 
-说明:
+## 注意事项
 
-- 未配置 Embedding Key 时,语义检索会回退到 BM25。
-- 推荐每本书单独配置 `${PROJECT_ROOT}/.env`,避免多项目串配置。
+- 未配置 Embedding Key 时,语义检索会自动回退到 BM25(仍可正常使用,但效果弱于向量检索)。
+- 推荐每本书单独配置 `${PROJECT_ROOT}/.env`,避免多项目之间串配置。
+- Embedding 和 Rerank 的模型可以替换为任何兼容 OpenAI 格式的 API。

+ 54 - 57
docs/operations/operations.md

@@ -1,75 +1,87 @@
 # 项目结构与运维
 
-## 目录层级(真实运行)
+## 目录层级
 
-在 Claude Code + Marketplace 安装下,至少有 4 层概念
+系统涉及 4 层目录,使用前需要了解它们的区别
 
-1. `WORKSPACE_ROOT`(Claude 工作区根,通常是 `${CLAUDE_PROJECT_DIR}`)
-2. `WORKSPACE_ROOT/.claude/`(工作区级指针与配置)
-3. `PROJECT_ROOT`(真实小说项目根,`/webnovel-init` 按书名创建)
-4. `CLAUDE_PLUGIN_ROOT`(插件缓存目录,不在项目内)
+| 层级 | 说明 | 示例 |
+|------|------|------|
+| `WORKSPACE_ROOT` | Claude Code 工作区根目录 | `D:\wk\novels` |
+| `.claude/` | 工作区级配置与项目指针 | `D:\wk\novels\.claude\` |
+| `PROJECT_ROOT` | 某本书的项目根目录(由 `/webnovel-init` 创建) | `D:\wk\novels\凡人资本论` |
+| `CLAUDE_PLUGIN_ROOT` | 插件缓存目录(不在项目内,由 Marketplace 安装管理) | 自动管理 |
 
-### A) Workspace 目录(含 `.claude`)
+### 工作区目录
 
 ```text
 workspace-root/
 ├── .claude/
-│   ├── .webnovel-current-project   # 指向当前小说项目根
+│   ├── .webnovel-current-project   # 指向当前项目根
 │   └── settings.json
-├── 小说A/
+├── 小说A/                          # PROJECT_ROOT
 ├── 小说B/
 └── ...
 ```
 
-### B) 小说项目目录(`PROJECT_ROOT`)
+一个工作区可以包含多本书,通过 `.webnovel-current-project` 指针切换当前操作的书。
+
+### 书项目目录(PROJECT_ROOT)
 
 ```text
 project-root/
-├── .webnovel/            # 运行时数据(state/index/vectors/summaries)
+├── .webnovel/            # 运行时数据
+│   ├── state.json        # 项目状态
+│   ├── index.db          # SQLite 索引(实体/关系/章节数据)
+│   ├── vectors.db        # 向量索引
+│   ├── summaries/        # 章节摘要
+│   ├── backups/          # 自动备份
+│   └── archive/          # 归档
+├── .story-system/        # Story System 数据
+│   ├── MASTER_SETTING.json
+│   ├── chapters/
+│   ├── volumes/
+│   ├── reviews/
+│   ├── commits/
+│   └── events/
 ├── 正文/                  # 正文章节
 ├── 大纲/                  # 总纲与卷纲
-└── 设定集/                # 世界观、角色、力量体系
+├── 设定集/                # 世界观、角色、力量体系
+└── 审查报告/              # 审查输出
 ```
 
-## 插件目录(Marketplace 安装)
+### 插件目录
 
-插件不在小说项目目录内,而在 Claude 插件缓存目录。运行时统一用 `CLAUDE_PLUGIN_ROOT` 引用:
+插件安装在 Claude 插件缓存目录,不在书项目内。运行时通过 `CLAUDE_PLUGIN_ROOT` 引用:
 
 ```text
 ${CLAUDE_PLUGIN_ROOT}/
-├── skills/
-├── agents/
-├── scripts/
-└── references/
+├── skills/       # 7 个 Skill 命令定义
+├── agents/       # 3 个 Agent 定义
+├── scripts/      # Python 脚本与数据模块
+├── references/   # 参考文档(题材画像、追读力分类法等)
+├── templates/    # 初始化模板
+├── genres/       # 精调题材配置
+└── dashboard/    # 可视化面板前端
 ```
 
-### C) 用户级全局映射(兜底)
+### 用户级全局映射
 
-当工作区没有可用指针时,会使用用户级 registry 做 `workspace -> current_project_root` 映射:
+当工作区指针不可用时,系统会从用户级 registry 查找 workspace → project 映射:
 
 ```text
 ${CLAUDE_HOME:-~/.claude}/webnovel-writer/workspaces.json
 ```
 
-## 模拟目录实测(2026-03-03)
-
-基于 `D:\wk\novel skill\plugin-sim-20260303-012048` 的实际结果:
-
-- `WORKSPACE_ROOT`:`D:\wk\novel skill\plugin-sim-20260303-012048`
-- 指针文件:`D:\wk\novel skill\plugin-sim-20260303-012048\.claude\.webnovel-current-project`
-- 指针内容:`D:\wk\novel skill\plugin-sim-20260303-012048\凡人资本论-二测`
-- 已创建项目示例:`凡人资本论/`、`凡人资本论-二测/`
-
 ## 常用运维命令
 
-统一前置(手动 CLI 场景):
+### 环境预检
 
 ```bash
-export WORKSPACE_ROOT="${CLAUDE_PROJECT_DIR:-$PWD}"
-export SCRIPTS_DIR="${CLAUDE_PLUGIN_ROOT}/scripts"
-export PROJECT_ROOT="$(python "${SCRIPTS_DIR}/webnovel.py" --project-root "${WORKSPACE_ROOT}" where)"
+python -X utf8 "${CLAUDE_PLUGIN_ROOT}/scripts/webnovel.py" --project-root "${WORKSPACE_ROOT}" preflight
 ```
 
+检查项:插件脚本路径 / 项目根是否可解析 / Skill 目录是否存在。
+
 ### 索引重建
 
 ```bash
@@ -91,7 +103,7 @@ python "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" rag index-c
 python "${SCRIPTS_DIR}/webnovel.py" --project-root "${PROJECT_ROOT}" rag stats
 ```
 
-### 测试入口
+### 测试
 
 ```bash
 pwsh "${CLAUDE_PLUGIN_ROOT}/scripts/run_tests.ps1" -Mode smoke
@@ -100,40 +112,25 @@ pwsh "${CLAUDE_PLUGIN_ROOT}/scripts/run_tests.ps1" -Mode full
 
 ## Story System 运维
 
-### preflight
-
-检查统一入口与事件链目录是否可用:
+### 健康检查
 
 ```bash
-python -X utf8 "${CLAUDE_PLUGIN_ROOT}/scripts/webnovel.py" --project-root "${PROJECT_ROOT}" preflight
 python -X utf8 "${CLAUDE_PLUGIN_ROOT}/scripts/webnovel.py" --project-root "${PROJECT_ROOT}" story-events --health
 ```
 
-重点看三项:
+返回字段:`sqlite_rows` / `event_files` / `ok`
+
+重点关注:
 
 - `.story-system/events/` 是否可读
-- `.webnovel/index.db` 中 `story_events` 是否可查
+- `index.db` 中 `story_events` 是否可查
 - `override_contracts` 是否能统计 `amend_proposal`
 
-### health
+### 备份
 
-最小健康检查命令
+做 Story System 相关备份时,至少同时备份以下内容
 
-```bash
-python -X utf8 "${CLAUDE_PLUGIN_ROOT}/scripts/webnovel.py" --project-root "${PROJECT_ROOT}" story-events --health
-```
-
-返回字段:
-
-- `sqlite_rows`
-- `event_files`
-- `ok`
-
-### backup
-
-做 Story System 相关备份时,至少同时备这两块:
-
-```bash
+```text
 .story-system/
 .webnovel/index.db
 ```

+ 40 - 0
docs/operations/plugin-release.md

@@ -0,0 +1,40 @@
+# 插件发版指南
+
+## 版本同步
+
+发版前,先在本地同步 `plugin.json`、`marketplace.json` 和 `README.md` 中的版本号:
+
+```bash
+python -X utf8 webnovel-writer/scripts/sync_plugin_version.py --version X.Y.Z --release-notes "本次版本说明"
+```
+
+该命令会自动更新以下文件中的版本信息:
+
+- `webnovel-writer/.claude-plugin/plugin.json`
+- `.claude-plugin/marketplace.json`
+- `README.md` 中的版本标记
+
+## 通过 GitHub Actions 发版
+
+推荐使用 `Plugin Release` 工作流统一发版:
+
+1. 在本地执行版本同步(见上方命令)
+2. 提交并推送版本变更
+3. 打开仓库 Actions 页面,选择 `Plugin Release`
+4. 输入 `version`(如 `6.0.0`)和 `release_notes`
+5. 工作流自动执行:
+   - 校验 `plugin.json`、`marketplace.json` 与 README 版本一致
+   - 校验输入版本与仓库元数据一致
+   - 创建并推送 `vX.Y.Z` Tag
+   - 创建 GitHub Release
+
+## 自动版本校验
+
+`Plugin Version Check` 工作流会在每次 Push / PR 时自动检查版本一致性。
+
+触发文件变更:
+
+- `.claude-plugin/marketplace.json`
+- `webnovel-writer/.claude-plugin/plugin.json`
+- `webnovel-writer/scripts/sync_plugin_version.py`
+- `README.md`

+ 1 - 1
webnovel-writer/scripts/__init__.py

@@ -4,7 +4,7 @@ webnovel-writer scripts package
 This package contains all Python scripts for the webnovel-writer plugin.
 """
 
-__version__ = "5.5.4"
+__version__ = "5.5.5"
 __author__ = "lcy"
 
 # Expose main modules

+ 2 - 2
webnovel-writer/scripts/data_modules/api_client.py

@@ -23,6 +23,7 @@ Data Modules - API 客户端 (v5.4,v5.0 OpenAI 兼容接口沿用)
 
 import asyncio
 import aiohttp
+import json
 import time
 from typing import List, Dict, Any, Optional
 from dataclasses import dataclass
@@ -145,8 +146,7 @@ class EmbeddingAPIClient:
                     ) as resp:
                         if resp.status == 200:
                             text = await resp.text()
-                            import json as json_module
-                            data = json_module.loads(text)
+                            data = json.loads(text)
                             embeddings = self._parse_response(data)
 
                             if embeddings:

+ 43 - 39
webnovel-writer/scripts/data_modules/event_log_store.py

@@ -4,8 +4,10 @@ from __future__ import annotations
 
 import json
 import sqlite3
+import sys
+from contextlib import contextmanager
 from pathlib import Path
-from typing import Any, Dict, List
+from typing import Any, Dict, Iterator, List
 
 from .story_contracts import StoryContractPaths, read_json_if_exists, write_json
 from .story_event_schema import StoryEvent
@@ -16,6 +18,19 @@ class EventLogStore:
         self.project_root = Path(project_root).expanduser().resolve()
         self.paths = StoryContractPaths.from_project_root(self.project_root)
 
+    @contextmanager
+    def _connect(self, *, row_factory: bool = False) -> Iterator[sqlite3.Connection]:
+        """统一 SQLite 连接管理,确保连接始终关闭。"""
+        db_path = self.project_root / ".webnovel" / "index.db"
+        db_path.parent.mkdir(parents=True, exist_ok=True)
+        conn = sqlite3.connect(str(db_path))
+        if row_factory:
+            conn.row_factory = sqlite3.Row
+        try:
+            yield conn
+        finally:
+            conn.close()
+
     def write_events(self, chapter: int, events: List[dict]) -> Path:
         normalized = self._normalize_events(chapter, events)
         path = self.paths.event_json(chapter)
@@ -30,34 +45,31 @@ class EventLogStore:
         db_path = self.project_root / ".webnovel" / "index.db"
         if not db_path.is_file():
             return []
-        conn = sqlite3.connect(str(db_path))
-        conn.row_factory = sqlite3.Row
-        try:
-            if chapter is not None:
-                rows = conn.execute(
-                    """
-                    SELECT event_id, chapter, event_type, subject, payload_json
-                    FROM story_events
-                    WHERE chapter = ?
-                    ORDER BY id DESC
-                    LIMIT ?
-                    """,
-                    (chapter, limit),
-                ).fetchall()
-            else:
-                rows = conn.execute(
-                    """
-                    SELECT event_id, chapter, event_type, subject, payload_json
-                    FROM story_events
-                    ORDER BY chapter DESC, id DESC
-                    LIMIT ?
-                    """,
-                    (limit,),
-                ).fetchall()
-        except sqlite3.OperationalError:
-            return []
-        finally:
-            conn.close()
+        with self._connect(row_factory=True) as conn:
+            try:
+                if chapter is not None:
+                    rows = conn.execute(
+                        """
+                        SELECT event_id, chapter, event_type, subject, payload_json
+                        FROM story_events
+                        WHERE chapter = ?
+                        ORDER BY id DESC
+                        LIMIT ?
+                        """,
+                        (chapter, limit),
+                    ).fetchall()
+                else:
+                    rows = conn.execute(
+                        """
+                        SELECT event_id, chapter, event_type, subject, payload_json
+                        FROM story_events
+                        ORDER BY chapter DESC, id DESC
+                        LIMIT ?
+                        """,
+                        (limit,),
+                    ).fetchall()
+            except sqlite3.OperationalError:
+                return []
 
         result: List[Dict[str, Any]] = []
         for row in rows:
@@ -82,16 +94,13 @@ class EventLogStore:
         file_count = len(list(self.paths.events_dir.glob("chapter_*.events.json")))
         sqlite_rows = 0
         if db_path.is_file():
-            conn = sqlite3.connect(str(db_path))
-            try:
+            with self._connect() as conn:
                 try:
                     sqlite_rows = int(
                         conn.execute("SELECT COUNT(*) FROM story_events").fetchone()[0]
                     )
                 except sqlite3.OperationalError:
                     sqlite_rows = 0
-            finally:
-                conn.close()
         return {"ok": True, "sqlite_rows": sqlite_rows, "event_files": file_count}
 
     def _normalize_events(self, chapter: int, events: List[dict]) -> List[Dict[str, Any]]:
@@ -105,10 +114,7 @@ class EventLogStore:
         return normalized
 
     def _write_sqlite_mirror(self, events: List[Dict[str, Any]]) -> None:
-        db_path = self.project_root / ".webnovel" / "index.db"
-        db_path.parent.mkdir(parents=True, exist_ok=True)
-        conn = sqlite3.connect(str(db_path))
-        try:
+        with self._connect() as conn:
             conn.execute(
                 """
                 CREATE TABLE IF NOT EXISTS story_events (
@@ -145,5 +151,3 @@ class EventLogStore:
                 ],
             )
             conn.commit()
-        finally:
-            conn.close()

+ 3 - 1
webnovel-writer/scripts/data_modules/memory/orchestrator.py

@@ -128,7 +128,9 @@ class MemoryOrchestrator:
             return {}
         try:
             return json.loads(path.read_text(encoding="utf-8"))
-        except Exception:
+        except Exception as exc:
+            import sys
+            print(f"⚠️ state.json 读取失败: {exc}", file=sys.stderr)
             return {}
 
     def _load_recent_summaries(self, chapter: int, window: int) -> List[Dict[str, Any]]:

+ 1 - 0
webnovel-writer/scripts/data_modules/override_ledger_service.py

@@ -39,6 +39,7 @@ def ensure_override_ledger_columns(conn: sqlite3.Connection) -> None:
     }
     for name, ddl in wanted.items():
         if name not in existing:
+            # SECURITY: name 和 ddl 均来自上方硬编码字典,非用户输入,无 SQL 注入风险
             conn.execute(f"ALTER TABLE override_contracts ADD COLUMN {name} {ddl}")
     conn.execute(
         "CREATE INDEX IF NOT EXISTS idx_override_contracts_record_type ON override_contracts(record_type)"

+ 6 - 2
webnovel-writer/scripts/data_modules/webnovel.py

@@ -158,7 +158,9 @@ def cmd_use(args: argparse.Namespace) -> int:
     project_root = normalize_windows_path(args.project_root).expanduser()
     try:
         project_root = project_root.resolve()
-    except Exception:
+    except Exception as exc:
+        import sys
+        print(f"⚠️ path.resolve() 失败 ({project_root}): {exc}", file=sys.stderr)
         project_root = project_root
 
     workspace_root: Optional[Path] = None
@@ -166,7 +168,9 @@ def cmd_use(args: argparse.Namespace) -> int:
         workspace_root = normalize_windows_path(args.workspace_root).expanduser()
         try:
             workspace_root = workspace_root.resolve()
-        except Exception:
+        except Exception as exc:
+            import sys
+            print(f"⚠️ path.resolve() 失败 ({workspace_root}): {exc}", file=sys.stderr)
             workspace_root = workspace_root
 
     # 1) 写入工作区指针(若工作区内存在 `.claude/`)