Prechádzať zdrojové kódy

feat(v7): M5 壳接线——SKILL 写章流程接 F1 通道,命令引用模板变量化

- SKILL.md:七态逐条接 persist-*/review-input/save-review/finalize,「继续」= next --json
- 命令引用 {{cmd}} = node .webnovel/bin/webnovel-writer.js(spec §5.9,vendored 离线可跑)
- drift check 绿;335 测试绿
lingfengQAQ 21 hodín pred
rodič
commit
0ad5767f9a

+ 18 - 13
v7/skills/webnovel-writer/SKILL.md

@@ -11,33 +11,38 @@ description: 中文长篇网文写作单入口。说「继续/写下一章/建
 SessionStart 已注入「当前在写哪本 / 共几本 / 全书近况入口」。
 {{/if}}
 {{#unless hasHooks}}
-启动先取会话上下文:读工作目录 `.webnovel/books.jsonl`,报「当前在写哪本 / 共几本」;登记缺失则扫描含 `book.yaml` 的子目录重建书单,请作者选当前书
+启动先运行 `{{cmd}} session-context`,向作者报「当前在写哪本 / 共几本」
 {{/unless}}
+换书说一声即可:`{{cmd}} switch-book <书名>`;看书单:`{{cmd}} list-books`。
 
 ## 单入口:判定下一步
-作者说「继续」,运行 `webnovel-writer next`(git 健康检查先行),按返回的序执行:
-- 序0 修复确认 / 序1 建书 / 序4 卷复盘 / 序6 起草细纲:吃返回的 DTO 产结构化产物,交脚本落盘。
-- 序2 手改补登 / 序3 断点续跑 / 序5 体检:按返回指引执行。
+作者说「继续」,运行 `{{cmd}} next --json`(git 健康检查先行),按返回的 `序` 执行:
+- 序0 修复确认:对 `dto.failures` 逐个给「保留作者意图」的修复方案,作者确认后写 `{"repairs":[{"file","content"}]}`,运行 `{{cmd}} persist-repair --file=<json路径>`。
+- 序1 建书:问答收集书名、类型、主角、金手指、结局,产出 `{"book","总纲","卷纲"}`,运行 `{{cmd}} persist-book --file=<json路径>`。
+- 序2 手改补登 / 序3 断点续跑 / 序5 体检:按返回的 `message` 指引执行。
+- 序4 卷复盘:吃 DTO 与作者对谈,产出 `{"卷号","卷摘要","下卷卷纲","伏笔条目"}`,运行 `{{cmd}} persist-volume-review --file=<json路径>`。
+- 序6 起草细纲:吃 DTO 拟细纲提案(本章定位声明 + 本章要写到的事 + 备选),作者确认后产出 `{"细纲"}`,运行 `{{cmd}} persist-outline --file=<json路径>`。
 
 ## 写章流程
-1. 备料:`webnovel-writer prepare-chapter <章号>`,读 `工作区/本章写作材料.md` 写草稿到 `工作区/草稿.md`。
-2. 机检:`webnovel-writer mechanical-check <章号>`,按清单修可计数项
-3. 两审同一份 ReviewInput:
+1. 备料:`{{cmd}} prepare-chapter <章号>`,读 `工作区/本章写作材料.md` 写草稿到 `工作区/草稿-A.md`。
+2. 机检:`{{cmd}} mechanical-check <章号>`,修可计数项后重跑至过线
+3. 两审:`{{cmd}} review-input <章号>` 生成 `工作区/审稿输入.json`。
 {{#if agentCapable}}
-   完整模式——派两个独立 subagent,分别按 `事实审查`、`编辑审` 任务书审,各自新鲜上下文
+   派两个独立 subagent 按 `事实审查`、`编辑审` 任务书各读同一份审稿输入,各自新鲜上下文出报告
 {{/if}}
 {{#unless agentCapable}}
-   兼容模式——按 `事实审查`、`编辑审` 两份任务书顺序自审;审稿单声明「兼容模式(单上下文顺序审稿),隔离度低于完整两审」
+   兼容模式——按 `事实审查`、`编辑审` 两份任务书顺序自审,`mode` 填 `degraded`
 {{/unless}}
-   审稿单落 `工作区/审稿.md`,作者:接受 / 改完接受 / 打回。
-4. 定稿:作者敲定后由脚本原子 commit(正文入定稿、条目与设定与时间线更新、章摘要入档、工作区清空)
+   两份报告合成 `{"事实审查","编辑审","mode","待确认新专名","章摘要"}`,运行 `{{cmd}} save-review <章号> --file=<json路径>`;审稿单落 `工作区/审稿.md`,作者:接受 / 改完接受 / 打回。
+4. 定稿:作者敲定后组定稿包(`frontMatter`、`body`、`summary`、`threadUpdates`、`characterUpdates`、`rosterUpserts`、`timelineRows`、`secretWrites`、`commitLines`、`workspaceFiles`——本章用过的工作区文件全列进 `workspaceFiles`),运行 `{{cmd}} finalize <章号> --payload=<json路径>`,再运行 `{{cmd}} next --json` 进下一步
 
 ## 例外流程
-- 回到第N章:`webnovel-writer goto-chapter <章号>`,先备份再回滚。
-- 影响分析:`webnovel-writer impact <关键词>`。
+- 回到第N章:`{{cmd}} goto-chapter <章号>`,先备份再回滚。
+- 影响分析:`{{cmd}} impact <关键词>`。
 - 吃书:按状态机指引改设定与条目,retcon commit。
 
 ## 铁律
 - 事实变更只经定稿流程入 git。
 - 能数的交脚本,要判断的交两审。
 - 只吃整理好的 DTO,按提供的上下文工作。
+- 传给命令的 JSON 一律先写成文件再走 `--file`/`--payload`(临时 JSON 放 `工作区/`;建书时放工作目录根)。

+ 4 - 0
v7/src/host-shells/generate.js

@@ -9,12 +9,16 @@ import { FACT_CATEGORIES, EDIT_CATEGORIES, SEVERITIES, SCHEMA_EXAMPLE } from '..
  * 确定性:同输入必同输出(drift check 基础)。不联网、不改业务源。
  */
 
+/** 安装后统一的命令引用(spec §5.9 平台上下文变量;运行时 vendored 进 .webnovel/,离线可跑) */
+const CMD = 'node .webnovel/bin/webnovel-writer.js'
+
 /** schema 单源注入上下文(category/severity/范例来自 schema.js,角色与校验器不双表) */
 function schemaContext() {
   return {
     categories: { factCheck: FACT_CATEGORIES.join('、'), editorial: EDIT_CATEGORIES.join('、') },
     severities: SEVERITIES.join(' | '),
     schema: { example: JSON.stringify(SCHEMA_EXAMPLE, null, 2) },
+    cmd: CMD,
   }
 }
 

+ 9 - 4
v7/test/host-shells/generate.test.js

@@ -17,19 +17,24 @@ test('renderTemplate:agentCapable=false → 渲染兼容(降级)模式块', ()
   assert.ok(!renderTemplate(t, { agentCapable: false }).includes('完整'))
 })
 
-test('生成 claude-code 壳:hasHooks 块入、unless 块去;两审完整模式;占位符全渲染', async () => {
+test('生成 claude-code 壳:hasHooks 块入、unless 块去;两审完整模式;F1 命令接线;占位符全渲染', async () => {
   const out = await generateHostShells(V7)
   const skill = out['claude-code']['skills/webnovel-writer/SKILL.md']
   assert.match(skill, /SessionStart 已注入/)
-  assert.ok(!skill.includes('扫描含'), 'hasHooks=true 应去掉 unless 块')
-  assert.match(skill, /完整模式/)
+  assert.ok(!skill.includes('session-context`,向作者报'), 'hasHooks=true 应去掉 unless 块')
+  assert.match(skill, /独立 subagent/)
+  assert.ok(!skill.includes('兼容模式'), 'agentCapable=true 应去掉兼容模式块')
+  assert.match(skill, /node \.webnovel\/bin\/webnovel-writer\.js next --json/, '命令引用变量应渲染为 vendored 调用')
+  for (const cmdName of ['review-input', 'save-review', 'finalize', 'persist-book', 'persist-outline']) {
+    assert.ok(skill.includes(cmdName), `写章流程应接 F1 命令 ${cmdName}`)
+  }
   assert.ok(!skill.includes('{{'), '占位符应全部渲染')
 })
 
 test('生成 codex 壳:无 hook → unless 块入;角色输出 TOML', async () => {
   const out = await generateHostShells(V7)
   const skill = out['codex']['skills/webnovel-writer/SKILL.md']
-  assert.match(skill, /读工作目录/)
+  assert.match(skill, /session-context/)
   assert.ok(!skill.includes('SessionStart 已注入'))
   const role = out['codex']['agents/事实审查.toml']
   assert.match(role, /name = "事实审查"/)