@cloud411716/fancy-webnovel 2.2.0 → 3.0.3
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.
- package/README.md +65 -0
- package/client.js +201 -15
- package/cordis.patch.yml +5 -1
- package/docs/douyin-plugin.md +118 -0
- package/docs/material-library/README.md +404 -0
- package/docs/material-library/import.js +437 -0
- package/docs/material-library/prompt.md +370 -0
- package/docs/material-library/queries.surql +94 -0
- package/docs/material-library/sample.jsonl +14 -0
- package/docs/material-library/schema.surql +46 -0
- package/docs/mcp-profile.example.yml +24 -0
- package/docs/story-graph-design.md +602 -0
- package/docs/story-graph-validation.md +81 -0
- package/docs/workspace-mcp.md +284 -0
- package/index.js +12 -7
- package/lib/atomic-write.js +109 -0
- package/lib/chapter-order.js +100 -0
- package/lib/confirmation.js +80 -0
- package/lib/db-init.js +123 -0
- package/lib/deployed-state.js +11 -11
- package/lib/direction-labels.js +265 -0
- package/lib/error-codes.js +31 -31
- package/lib/infra.js +29 -4
- package/lib/material-db.js +149 -0
- package/lib/path-guard.js +150 -0
- package/lib/projects.js +400 -0
- package/lib/scraper-utils.js +140 -0
- package/lib/surreal-server.js +338 -0
- package/lib/topic-decision.js +860 -0
- package/lib/topic-fields.js +251 -0
- package/lib/topic-schema.js +225 -0
- package/lib/workspace.js +501 -0
- package/package.json +31 -7
- package/plugins/fancy-analyze/analyze-definitions.md +519 -0
- package/plugins/fancy-analyze/index.js +1128 -77
- package/plugins/fancy-analyze/ingest-coverage.js +107 -0
- package/plugins/fancy-analyze/schema-analyze.md +355 -107
- package/plugins/fancy-analyze/scripts/append-summary-chapter.js +612 -0
- package/plugins/fancy-analyze/summary-progress.js +71 -7
- package/plugins/fancy-analyze/templates.js +437 -139
- package/plugins/fancy-bootstrap/index.js +276 -131
- package/plugins/fancy-bootstrap/scripts/init.js +59 -25
- package/plugins/fancy-bootstrap/templates.js +35 -18
- package/plugins/fancy-db/index.js +172 -0
- package/plugins/fancy-douyin/deploy.js +134 -0
- package/plugins/fancy-douyin/index.js +551 -0
- package/plugins/fancy-douyin/prompts/01-analysis-prompt.md +84 -0
- package/plugins/fancy-douyin/scripts/crawl-note.cjs +194 -0
- package/plugins/fancy-douyin/scripts/lib.cjs +197 -0
- package/plugins/fancy-douyin/scripts/scan-account.cjs +128 -0
- package/plugins/fancy-douyin/scripts/start-chrome.cjs +203 -0
- package/plugins/fancy-douyin/state.js +197 -0
- package/plugins/fancy-douyin/templates.js +127 -0
- package/plugins/fancy-export/exporter.js +805 -0
- package/plugins/fancy-export/index.js +63 -0
- package/plugins/fancy-import/import-driver.js +29 -6
- package/plugins/fancy-import/index.js +13 -5
- package/plugins/fancy-import/prompts/01-import-prompt.md +39 -14
- package/plugins/fancy-import/templates.js +17 -8
- package/plugins/fancy-material/exporter.js +153 -0
- package/plugins/fancy-material/genres.js +256 -0
- package/plugins/fancy-material/index.js +835 -0
- package/plugins/fancy-material/inventory.js +145 -0
- package/plugins/fancy-material/kinds.js +313 -0
- package/plugins/fancy-material/queue.js +55 -0
- package/plugins/fancy-material/scripts/query.js +254 -0
- package/plugins/fancy-material/templates.js +258 -0
- package/plugins/fancy-material/tool-deploy.js +138 -0
- package/plugins/fancy-material/validate.js +149 -0
- package/plugins/fancy-project/index.js +133 -0
- package/plugins/fancy-scan/existing-block.js +100 -0
- package/plugins/fancy-scan/index.js +410 -21
- package/plugins/fancy-scan/prompts/01-analysis-prompt.md +302 -0
- package/plugins/fancy-scan/scripts/append-block.js +725 -0
- package/plugins/fancy-scan/scripts/report-validate.js +824 -0
- package/plugins/fancy-scan/scripts/run-scan.js +169 -22
- package/plugins/fancy-scan/scripts/scrapers/fanqie-rank-scraper.js +145 -16
- package/plugins/fancy-scan/scripts/scrapers/jjwxc-rank-scraper.js +179 -49
- package/plugins/fancy-scan/scripts/scrapers/qimao-rank-scraper.js +38 -20
- package/plugins/fancy-scan/templates/blocks.js +155 -0
- package/plugins/fancy-scan/templates/platforms.js +372 -0
- package/plugins/fancy-scan/templates/report-schema.js +90 -0
- package/plugins/fancy-scan/templates.js +545 -146
- package/plugins/fancy-scan/tool-deploy.js +335 -0
- package/plugins/fancy-scan/wait-turn.js +46 -0
- package/plugins/fancy-story-builder/db/README.md +106 -0
- package/plugins/fancy-story-builder/db/client.js +228 -0
- package/plugins/fancy-story-builder/db/commands.js +145 -0
- package/plugins/fancy-story-builder/db/ids.js +39 -0
- package/plugins/fancy-story-builder/db/import.js +798 -0
- package/plugins/fancy-story-builder/db/project.js +299 -0
- package/plugins/fancy-story-builder/db/query.js +361 -0
- package/plugins/fancy-story-builder/db/schema.js +417 -0
- package/plugins/fancy-story-builder/db/stage-sync.js +238 -0
- package/plugins/fancy-story-builder/flesh-output.js +19 -6
- package/plugins/fancy-story-builder/index.js +4105 -396
- package/plugins/fancy-story-builder/patch-driver.js +390 -0
- package/plugins/fancy-story-builder/prompts/01-skeleton-prompt.md +29 -19
- package/plugins/fancy-story-builder/prompts/02-flesh-prompt.md +72 -10
- package/plugins/fancy-story-builder/prompts/03-rhythm-prompt.md +40 -5
- package/plugins/fancy-story-builder/prompts/04-writing-prompt.md +37 -0
- package/plugins/fancy-story-builder/purpose-template.md +42 -66
- package/plugins/fancy-story-builder/references/formulas-skeleton.json +229 -56
- package/plugins/fancy-story-builder/references/genre-profiles.json +3514 -0
- package/plugins/fancy-story-builder/references/glossary.md +15 -6
- package/plugins/fancy-story-builder/references/index.md +97 -6
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/00-index.md +1 -1
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/07-audit.md +25 -7
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/01-character.md +5 -1
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/03-relations.md +6 -2
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/05-audit.md +15 -5
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/00-index.md +1 -1
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/01-positioning.md +2 -1
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/03-entities.md +1 -1
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/06-audit.md +19 -1
- package/plugins/fancy-story-builder/schema-setting.json +1100 -0
- package/plugins/fancy-story-builder/schema-setting.md +201 -293
- package/plugins/fancy-story-builder/scripts/brief-validate.js +164 -0
- package/plugins/fancy-story-builder/scripts/content-quality.js +4 -1
- package/plugins/fancy-story-builder/scripts/flesh-validate.js +135 -12
- package/plugins/fancy-story-builder/scripts/graph-utils.js +146 -47
- package/plugins/fancy-story-builder/scripts/reference-log.js +110 -0
- package/plugins/fancy-story-builder/scripts/rhythm-validate.js +100 -4
- package/plugins/fancy-story-builder/scripts/schema-pages.js +2668 -0
- package/plugins/fancy-story-builder/scripts/semantic-lint.js +5 -1
- package/plugins/fancy-story-builder/scripts/skeleton-validate.js +158 -17
- package/plugins/fancy-story-builder/scripts/stage-batches.js +36 -2
- package/plugins/fancy-story-builder/scripts/stage-contract.js +12 -0
- package/plugins/fancy-story-builder/scripts/story-artifacts.js +933 -0
- package/plugins/fancy-story-builder/scripts/upstream-query.js +1 -1
- package/plugins/fancy-story-builder/scripts/v2/baseline.js +112 -0
- package/plugins/fancy-story-builder/scripts/v2/cast.js +517 -0
- package/plugins/fancy-story-builder/scripts/v2/context.js +434 -0
- package/plugins/fancy-story-builder/scripts/v2/contracts.js +493 -0
- package/plugins/fancy-story-builder/scripts/v2/core.js +419 -0
- package/plugins/fancy-story-builder/scripts/v2/engine.js +1068 -0
- package/plugins/fancy-story-builder/scripts/v2/export-markdown.js +794 -0
- package/plugins/fancy-story-builder/scripts/v2/genre.js +195 -0
- package/plugins/fancy-story-builder/scripts/v2/knowledge.js +321 -0
- package/plugins/fancy-story-builder/scripts/v2/macro.js +409 -0
- package/plugins/fancy-story-builder/scripts/v2/manifest.js +538 -0
- package/plugins/fancy-story-builder/scripts/v2/migrate.js +137 -0
- package/plugins/fancy-story-builder/scripts/v2/prompts.js +1335 -0
- package/plugins/fancy-story-builder/scripts/v2/rhythm.js +618 -0
- package/plugins/fancy-story-builder/scripts/v2/scale.js +843 -0
- package/plugins/fancy-story-builder/scripts/v2/snapshot.js +121 -0
- package/plugins/fancy-story-builder/scripts/v2/stages.js +273 -0
- package/plugins/fancy-story-builder/scripts/v2/writing.js +216 -0
- package/plugins/fancy-story-builder/storydb/README.md +271 -0
- package/plugins/fancy-story-builder/storydb/cli.js +458 -0
- package/plugins/fancy-story-builder/storydb/client.js +351 -0
- package/plugins/fancy-story-builder/storydb/compile.js +452 -0
- package/plugins/fancy-story-builder/storydb/context.js +288 -0
- package/plugins/fancy-story-builder/storydb/definitions.js +699 -0
- package/plugins/fancy-story-builder/storydb/driver.js +106 -0
- package/plugins/fancy-story-builder/storydb/dsh-tools.js +313 -0
- package/plugins/fancy-story-builder/storydb/export.js +175 -0
- package/plugins/fancy-story-builder/storydb/ids.js +31 -0
- package/plugins/fancy-story-builder/storydb/impact.js +170 -0
- package/plugins/fancy-story-builder/storydb/import.js +559 -0
- package/plugins/fancy-story-builder/storydb/mcp.js +99 -0
- package/plugins/fancy-story-builder/storydb/prompts.js +952 -0
- package/plugins/fancy-story-builder/storydb/stage-command.js +379 -0
- package/plugins/fancy-story-builder/storydb/stage.js +426 -0
- package/plugins/fancy-story-builder/storydb/store.js +891 -0
- package/plugins/fancy-story-builder/storydb/tools.js +592 -0
- package/plugins/fancy-story-builder/storydb/validation.js +183 -0
- package/plugins/fancy-story-builder/templates/v2/10-scale/scale-template.json +144 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/emotion-template.json +15 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/endings-template.json +16 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/lines-template.json +28 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/roles-template.json +10 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/story-template.json +19 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/volumes-template.json +24 -0
- package/plugins/fancy-story-builder/templates/v2/20-macro/world-template.json +24 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/characters-template.json +49 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/climaxes-template.json +14 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/conflicts-template.json +30 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/factions-template.json +18 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/foreshadows-template.json +29 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/information-template.json +29 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/items-template.json +13 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/locations-template.json +12 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/payoffs-template.json +16 -0
- package/plugins/fancy-story-builder/templates/v2/30-cast/relations-template.json +44 -0
- package/plugins/fancy-story-builder/templates/v2/40-rhythm/chapter-map-template.json +81 -0
- package/plugins/fancy-story-builder/templates/v2/40-rhythm/outline-template.json +162 -0
- package/plugins/fancy-story-builder/templates/v2/50-writing/ledger-template.json +46 -0
- package/plugins/fancy-story-builder/templates/v2/60-deslop/report-template.json +10 -0
- package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//346/227/266/345/272/217/346/230/240/345/260/204/346/250/241/346/235/277.json +267 -44
- package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//344/270/226/347/225/214/350/247/202/350/247/204/345/210/231/346/250/241/346/235/277.json +121 -19
- package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/205/263/351/224/256/351/201/223/345/205/267/346/250/241/346/235/277.json +129 -21
- package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/205/266/344/273/226/345/256/236/344/275/223/346/250/241/346/235/277.json +62 -0
- package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/206/262/347/252/201/346/250/241/346/235/277.json +76 -17
- package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/211/247/346/203/205/346/250/241/346/235/277.json +56 -8
- package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/212/277/345/212/233/346/250/241/346/235/277.json +63 -13
- package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//345/234/260/347/202/271/346/250/241/346/235/277.json +34 -6
- package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//347/273/223/345/261/200/346/250/241/346/235/277.json +81 -18
- package/plugins/fancy-story-builder/templates//350/241/200/350/202/211//350/247/222/350/211/262/346/250/241/346/235/277.json +202 -42
- package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//345/205/263/347/263/273/346/250/241/346/235/277.json +160 -27
- package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//345/256/236/344/275/223/346/250/241/346/235/277.json +217 -41
- package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//346/241/206/346/236/266/346/250/241/346/235/277.json +215 -41
- package/plugins/fancy-story-builder/templates.js +272 -74
- package/plugins/fancy-story-builder/wiki-references.js +429 -93
- package/plugins/fancy-story-builder/writing-driver.js +336 -9
- package/plugins/fancy-topic/index.js +414 -61
- package/plugins/fancy-topic/prompts/01-exploration-prompt.md +115 -0
- package/plugins/fancy-topic/scripts/save-suggestion.js +219 -0
- package/plugins/fancy-topic/scripts/select-topic.js +106 -63
- package/plugins/fancy-topic/templates.js +225 -122
- package/plugins/fancy-topic/tool-deploy.js +200 -0
- package/lib/safe-rm.js +0 -94
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* for_write/{voice,sentence_patterns}.md
|
|
12
12
|
* for_write/phrase_bank/{action,psychology,environment,dialogue}.md
|
|
13
13
|
*/
|
|
14
|
+
import { shellSingleQuote } from '#path-guard';
|
|
14
15
|
|
|
15
16
|
export const CONSUMER_DIRS = {
|
|
16
17
|
bible: [
|
|
@@ -79,30 +80,52 @@ function humanAgo(ms, now) {
|
|
|
79
80
|
function renderSummaryStatus(progress, now) {
|
|
80
81
|
const lines = [];
|
|
81
82
|
const total = progress.total;
|
|
82
|
-
lines.push('📊
|
|
83
|
+
lines.push('📊 总结进度(对标/wiki:按章技法 + 跨章节实体)');
|
|
83
84
|
lines.push('');
|
|
84
85
|
|
|
85
|
-
const
|
|
86
|
+
const displayNameOf = (p) => `${p.name}${p.scope === 'entity' ? ' [实体]' : ''}`;
|
|
87
|
+
const nameWidth = Math.max(4, ...progress.dirs.map((d) => displayNameOf(d).length));
|
|
86
88
|
const pad = (name) => name + ' '.repeat(nameWidth - name.length);
|
|
87
89
|
const lineOf = (p) => {
|
|
90
|
+
const isEntity = p.scope === 'entity';
|
|
88
91
|
const parts = [];
|
|
89
92
|
if (p.sourceCount > 0) {
|
|
93
|
+
const unit = isEntity ? '个实体页' : '章';
|
|
90
94
|
parts.push(p.state === 'done'
|
|
91
|
-
? `${p.sourceCount}
|
|
92
|
-
: `已总结 ${p.summarized.length}/${p.sourceCount}
|
|
95
|
+
? `${p.sourceCount} ${unit}全部已总结`
|
|
96
|
+
: `已总结 ${p.summarized.length}/${p.sourceCount} ${unit}`);
|
|
93
97
|
}
|
|
94
|
-
parts.push(`${p.entries}
|
|
98
|
+
parts.push(`${p.entries} 条${isEntity ? '设计原则' : '技法'}`);
|
|
95
99
|
if (p.exists) {
|
|
96
100
|
parts.push(humanBytes(p.bytes));
|
|
97
101
|
parts.push(`${humanAgo(p.updatedAt, now)}更新`);
|
|
98
102
|
}
|
|
99
|
-
if (p.chapterSource === 'inferred')
|
|
100
|
-
|
|
101
|
-
|
|
103
|
+
if (p.chapterSource === 'inferred') {
|
|
104
|
+
parts.push(isEntity ? '实体清单由正文反推' : '章节清单由正文反推');
|
|
105
|
+
}
|
|
106
|
+
if (p.chapterSource === 'none' && p.sourceCount > 0) {
|
|
107
|
+
parts.push(isEntity ? '无实体清单' : '无章节清单');
|
|
108
|
+
}
|
|
109
|
+
return ` · ${pad(displayNameOf(p))} ${parts.join(' · ')}`;
|
|
102
110
|
};
|
|
103
111
|
|
|
112
|
+
if (total === 0
|
|
113
|
+
&& (!progress.legacyDirs || progress.legacyDirs.length === 0)) {
|
|
114
|
+
lines.push('(对标/wiki 下没有可总结的目录)');
|
|
115
|
+
return lines.join('\n');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (progress.legacyDirs && progress.legacyDirs.length > 0) {
|
|
119
|
+
lines.push(`⚠️ 旧版按章结构页(不参与 summary):${progress.legacyDirs.length} 个目录`);
|
|
120
|
+
for (const d of progress.legacyDirs) {
|
|
121
|
+
lines.push(` · ${d.name} ${d.pageCount} 个按章页面`);
|
|
122
|
+
}
|
|
123
|
+
lines.push(' 新版改用跨章节实体页(伏笔布局 / 世界观设计 / 角色弧光设计),重新 ingest 后取代。');
|
|
124
|
+
lines.push('');
|
|
125
|
+
}
|
|
126
|
+
|
|
104
127
|
if (total === 0) {
|
|
105
|
-
lines.push('
|
|
128
|
+
lines.push('(没有需要总结的目录)');
|
|
106
129
|
return lines.join('\n');
|
|
107
130
|
}
|
|
108
131
|
|
|
@@ -129,34 +152,47 @@ function renderSummaryStatus(progress, now) {
|
|
|
129
152
|
if (noList.length > 0) {
|
|
130
153
|
lines.push('');
|
|
131
154
|
lines.push(`⚠️ 既无头部「已总结章节」清单、也无法从正文反推:${noList.map((p) => p.name).join('、')}`);
|
|
132
|
-
lines.push(' 这些目录按"
|
|
155
|
+
lines.push(' 这些目录按"全部来源未总结"处理,下次 summary 会重新派发它们。');
|
|
133
156
|
}
|
|
134
157
|
|
|
135
158
|
lines.push('');
|
|
136
|
-
lines.push('💡 /fancy-analyze summary 只派发"
|
|
137
|
-
lines.push(' /fancy-analyze resummary [
|
|
159
|
+
lines.push('💡 /fancy-analyze summary 只派发"还有来源没总结"的目录;');
|
|
160
|
+
lines.push(' /fancy-analyze resummary [目录名] 强制重来(会把旧总结移入备份目录)。');
|
|
138
161
|
return lines.join('\n');
|
|
139
162
|
}
|
|
140
163
|
|
|
141
164
|
export const analyze = {
|
|
142
165
|
CONSUMER_DIRS,
|
|
143
166
|
|
|
144
|
-
notify({ type, bookName, bookNameClean, projectRoot, refDir, matches, err, missingFiles, queue, extraMessage, summary }) {
|
|
167
|
+
notify({ type, bookName, bookNameClean, projectRoot, refDir, matches, err, missingFiles, queue, extraMessage, coverageMessage, summary, command }) {
|
|
145
168
|
switch (type) {
|
|
169
|
+
case 'help':
|
|
170
|
+
return `🔍 对标分析 用法
|
|
171
|
+
|
|
172
|
+
/fancy-analyze <书名> 拆解爆款小说,产出到 对标/
|
|
173
|
+
/fancy-analyze status 查看拆解与 ingest 状态
|
|
174
|
+
/fancy-analyze fix 重新 ingest 缺失文件
|
|
175
|
+
/fancy-analyze summary 汇总拆解结果
|
|
176
|
+
/fancy-analyze summary-status 查看汇总进度
|
|
177
|
+
/fancy-analyze resummary [目录名] 强制重新汇总(省略目录名则全部)
|
|
178
|
+
/fancy-analyze help 显示本帮助
|
|
179
|
+
|
|
180
|
+
作用:拆解工作区 A/参考资料/ 下的爆款小说,产出到 A/对标/,供多个项目共享。`;
|
|
181
|
+
|
|
146
182
|
case 'cancelled':
|
|
147
|
-
return '🚫
|
|
183
|
+
return '🚫 操作已取消。';
|
|
148
184
|
|
|
149
185
|
case 'no_provider':
|
|
150
|
-
return '⚠️
|
|
186
|
+
return '⚠️ 当前运行环境不支持交互式提问。请在 dsh-tui 中运行该命令,或确认 Web UI 已正确加载交互组件。';
|
|
151
187
|
|
|
152
188
|
case 'no_project_root':
|
|
153
|
-
return '❌
|
|
189
|
+
return '❌ 无法确定工作区:' + (err || '会话未提供当前工作目录') + '。请在 fancy-webnovel 工作区或项目目录下运行,或设置 FANCY_HOME。';
|
|
154
190
|
|
|
155
191
|
case 'ask_failed':
|
|
156
|
-
return '❌
|
|
192
|
+
return '❌ 交互提问失败:' + (err || '未知错误');
|
|
157
193
|
|
|
158
194
|
case 'not_initialized':
|
|
159
|
-
return '⚠️
|
|
195
|
+
return '⚠️ 当前目录不属于已初始化的 fancy-webnovel 工作区。请先运行 /fancy-bootstrap 初始化工作区。';
|
|
160
196
|
|
|
161
197
|
case 'deployed_unreadable':
|
|
162
198
|
// .fancy-deployed 存在但读不了。报"未初始化"会把用户引到
|
|
@@ -164,30 +200,30 @@ export const analyze = {
|
|
|
164
200
|
// (文件已存在),等于白跑一趟。
|
|
165
201
|
return `❌ 状态文件 .fancy-deployed 无法读取(${err || '内容异常'})。
|
|
166
202
|
|
|
167
|
-
|
|
168
|
-
|
|
203
|
+
该状态不是“未初始化”,重新运行 /fancy-bootstrap 无法解决。
|
|
204
|
+
请检查该文件是否被手动修改或损坏,或从版本库/备份恢复。`;
|
|
169
205
|
|
|
170
206
|
case 'source_dir_missing':
|
|
171
|
-
return `❌ 参考资料/ 目录不存在:${refDir || ''}
|
|
207
|
+
return `❌ 工作区的 参考资料/ 目录不存在:${refDir || ''}
|
|
172
208
|
|
|
173
209
|
请按以下步骤准备:
|
|
174
|
-
1.
|
|
210
|
+
1. 在工作区根目录创建 参考资料/ 目录(不是 projects/<uuid>/ 项目目录)
|
|
175
211
|
2. 手动下载《${bookName}》,并拷贝到 参考资料/${bookName}.txt
|
|
176
212
|
3. 重新运行 /fancy-analyze ${bookName}
|
|
177
213
|
|
|
178
|
-
|
|
214
|
+
说明:文件名后缀不限(.txt / .md / .epub 均可),书名匹配时忽略后缀。`;
|
|
179
215
|
|
|
180
216
|
case 'source_missing': {
|
|
181
217
|
const matchList = (matches && matches.length > 0)
|
|
182
218
|
? `\n\n参考资料/ 下已有的条目:\n${matches.map(m => ` - ${m}`).join('\n')}`
|
|
183
219
|
: '';
|
|
184
|
-
return `❌
|
|
220
|
+
return `❌ 未找到《${bookName}》的参考资料${matchList}
|
|
185
221
|
|
|
186
222
|
请按以下步骤准备:
|
|
187
|
-
1. 手动下载《${bookName}
|
|
223
|
+
1. 手动下载《${bookName}》,并拷贝到工作区根目录的 参考资料/ 下(多个项目共享)
|
|
188
224
|
2. 重新运行 /fancy-analyze ${bookName}
|
|
189
225
|
|
|
190
|
-
|
|
226
|
+
说明:匹配书名时忽略后缀。`;
|
|
191
227
|
}
|
|
192
228
|
|
|
193
229
|
case 'source_ambiguous':
|
|
@@ -200,7 +236,7 @@ export const analyze = {
|
|
|
200
236
|
return '❌ 拆分来源文件失败:' + (err || '未知错误');
|
|
201
237
|
|
|
202
238
|
case 'llm_wiki_not_configured':
|
|
203
|
-
return '❌ llm_wiki 未配置。请在 .fancy-deployed 中配置 llm_wiki_url 和 llm_wiki_token';
|
|
239
|
+
return '❌ llm_wiki 未配置。请在 .fancy-deployed 中配置 `llm_wiki_url` 和 `llm_wiki_token`。';
|
|
204
240
|
|
|
205
241
|
case 'llm_wiki_failed':
|
|
206
242
|
return '❌ 调用 llm_wiki 失败:' + (err || '未知错误');
|
|
@@ -218,7 +254,39 @@ export const analyze = {
|
|
|
218
254
|
2. /fancy-analyze fix 重新提交缺失章节`;
|
|
219
255
|
|
|
220
256
|
case 'no_reference_id':
|
|
221
|
-
return '⚠️
|
|
257
|
+
return '⚠️ 尚未设置 reference_id。请先运行 /fancy-analyze <书名> 初始化项目。';
|
|
258
|
+
|
|
259
|
+
case 'status_not_analyzed':
|
|
260
|
+
return '⚠️ 无法执行 status:当前没有任何拆解源文件。\n\n'
|
|
261
|
+
+ '`对标/raw/sources/` 为空,说明尚未执行过 `/fancy-analyze <书名>`。\n'
|
|
262
|
+
+ '请先执行 `/fancy-analyze <书名>` 完成拆书与 ingest,再查看 status。';
|
|
263
|
+
|
|
264
|
+
case 'fix_not_analyzed':
|
|
265
|
+
return '❌ 无法执行 fix:当前没有可修复的拆解产物。\n\n'
|
|
266
|
+
+ '`对标/raw/sources/` 中没有源文件,说明尚未执行过 `/fancy-analyze <书名>`。\n'
|
|
267
|
+
+ '请先执行 `/fancy-analyze <书名>` 完成拆书与 ingest,再执行 fix。';
|
|
268
|
+
|
|
269
|
+
case 'summary_ingest_not_ready': {
|
|
270
|
+
const cmd = command || 'summary';
|
|
271
|
+
const lines = ['⚠️ ' + cmd + ' 未启动:ingest 尚未完成。'];
|
|
272
|
+
const q = queue || {};
|
|
273
|
+
const pending = Number(q.pending) || 0;
|
|
274
|
+
const processing = Number(q.processing) || 0;
|
|
275
|
+
const failed = Number(q.failed) || 0;
|
|
276
|
+
if (missingFiles && missingFiles.length) {
|
|
277
|
+
lines.push('', '❌ 仍有 ' + missingFiles.length + ' 个源文件未进入 wiki:');
|
|
278
|
+
for (const f of missingFiles.slice(0, 20)) lines.push(' - ' + f);
|
|
279
|
+
if (missingFiles.length > 20) lines.push(' - …… 其余 ' + (missingFiles.length - 20) + ' 个省略');
|
|
280
|
+
}
|
|
281
|
+
if (pending || processing || failed) {
|
|
282
|
+
lines.push('', 'ingest 队列:等待 ' + pending + ',处理中 ' + processing + ',失败 ' + failed + '。');
|
|
283
|
+
}
|
|
284
|
+
lines.push('', '请等待 ingest 完成;如存在失败或缺失,请执行:');
|
|
285
|
+
lines.push('1. `/fancy-analyze status` 查看详细状态');
|
|
286
|
+
lines.push('2. `/fancy-analyze fix` 重新提交缺失/失败的章节');
|
|
287
|
+
lines.push('3. 确认队列空闲后再执行 `/fancy-analyze ' + cmd + '`');
|
|
288
|
+
return lines.join('\n');
|
|
289
|
+
}
|
|
222
290
|
|
|
223
291
|
case 'status_query_failed':
|
|
224
292
|
return '❌ 查询 ingest 状态失败:' + (err || '未知错误');
|
|
@@ -227,7 +295,7 @@ export const analyze = {
|
|
|
227
295
|
const lines = [];
|
|
228
296
|
|
|
229
297
|
if (missingFiles && missingFiles.length > 0) {
|
|
230
|
-
lines.push('
|
|
298
|
+
lines.push('❌ 当前仍有文件未拆解完成:');
|
|
231
299
|
for (const f of missingFiles) {
|
|
232
300
|
lines.push(` - ${f}`);
|
|
233
301
|
}
|
|
@@ -243,19 +311,33 @@ export const analyze = {
|
|
|
243
311
|
];
|
|
244
312
|
for (const s of states) {
|
|
245
313
|
if (queue[s.key] > 0) {
|
|
246
|
-
lines.push(
|
|
314
|
+
lines.push(`ℹ️ 当前${s.label}状态的任务为 ${queue[s.key]} 个`);
|
|
247
315
|
}
|
|
248
316
|
}
|
|
317
|
+
if ((queue.pending || 0) + (queue.processing || 0) > 0) {
|
|
318
|
+
lines.push('⏳ 队列中仍有未完成任务;以下缺失或覆盖结果可能尚未落盘,建议等待队列空闲后重新查询。');
|
|
319
|
+
}
|
|
249
320
|
}
|
|
250
321
|
|
|
251
|
-
//
|
|
322
|
+
// 有真实问题:保留“检测到问题”的警示头;覆盖统计单独追加。
|
|
252
323
|
if (extraMessage) {
|
|
253
|
-
const baseMsg = lines.length > 0
|
|
254
|
-
|
|
324
|
+
const baseMsg = lines.length > 0
|
|
325
|
+
? lines.join('\n')
|
|
326
|
+
: '⚠️ ingest 队列状态正常,但检测到以下问题:';
|
|
327
|
+
return baseMsg + '\n\n' + extraMessage +
|
|
328
|
+
(coverageMessage ? '\n\n' + coverageMessage : '');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 没有真实问题:不要再说“检测到以下问题”。覆盖统计只是信息。
|
|
332
|
+
if (coverageMessage) {
|
|
333
|
+
const baseMsg = lines.length > 0
|
|
334
|
+
? lines.join('\n')
|
|
335
|
+
: '✅ ingest 队列状态正常,未发现缺失。';
|
|
336
|
+
return baseMsg + '\n\n' + coverageMessage;
|
|
255
337
|
}
|
|
256
338
|
|
|
257
339
|
if (lines.length === 0) {
|
|
258
|
-
return '✅
|
|
340
|
+
return '✅ 所有文件均已完成 ingest,队列空闲。';
|
|
259
341
|
}
|
|
260
342
|
|
|
261
343
|
return lines.join('\n');
|
|
@@ -264,17 +346,32 @@ export const analyze = {
|
|
|
264
346
|
case 'summary_status':
|
|
265
347
|
return renderSummaryStatus(summary, Date.now());
|
|
266
348
|
|
|
349
|
+
case 'register_script_failed':
|
|
350
|
+
return `❌ 无法部署章节登记脚本到项目内(${projectRoot || '项目根未知'}/.fancy-tools/append-summary-chapter.js)。
|
|
351
|
+
|
|
352
|
+
请确认:
|
|
353
|
+
1. 项目根可写;
|
|
354
|
+
2. .fancy-tools 不是符号链接、也不是普通文件;
|
|
355
|
+
3. 已有目标文件不是符号链接。
|
|
356
|
+
|
|
357
|
+
修复后重新运行 /fancy-analyze summary。`;
|
|
358
|
+
|
|
267
359
|
case 'summary_dispatched': {
|
|
268
360
|
const tasks = summary?.tasks || [];
|
|
269
361
|
const lines = [];
|
|
270
|
-
lines.push('✅
|
|
362
|
+
lines.push('✅ 已派发总结任务');
|
|
271
363
|
lines.push('');
|
|
272
|
-
lines.push(`📋 本次派发 ${tasks.length}
|
|
364
|
+
lines.push(`📋 本次派发 ${tasks.length} 个总结目录:`);
|
|
273
365
|
for (const task of tasks) {
|
|
366
|
+
const isEntity = task.scope === 'entity';
|
|
274
367
|
const parts = [];
|
|
275
|
-
if (Number.isFinite(task.sourceCount))
|
|
276
|
-
|
|
277
|
-
|
|
368
|
+
if (Number.isFinite(task.sourceCount)) {
|
|
369
|
+
parts.push(`共 ${task.sourceCount} ${isEntity ? '个实体页' : '章'}`);
|
|
370
|
+
}
|
|
371
|
+
if (Number.isFinite(task.pendingCount)) {
|
|
372
|
+
parts.push(`还差 ${task.pendingCount} ${isEntity ? '个实体页' : '章'}`);
|
|
373
|
+
}
|
|
374
|
+
lines.push(` · ${task.name}${isEntity ? ' [实体]' : ''}${parts.length ? ' ' + parts.join(' · ') : ''}`);
|
|
278
375
|
}
|
|
279
376
|
if (summary?.skippedDone?.length) {
|
|
280
377
|
lines.push('');
|
|
@@ -285,16 +382,16 @@ export const analyze = {
|
|
|
285
382
|
}
|
|
286
383
|
lines.push('');
|
|
287
384
|
if (Number.isFinite(summary?.remainingChapters)) {
|
|
288
|
-
lines.push(`📚 全部待总结 **${summary.remainingChapters}
|
|
385
|
+
lines.push(`📚 全部待总结 **${summary.remainingChapters} 个来源项**(提示词只给目录,章节/实体页由模型自己去目录里找)`);
|
|
289
386
|
}
|
|
290
|
-
lines.push('💡
|
|
291
|
-
lines.push('⚠️
|
|
387
|
+
lines.push('💡 模型正在后台写入文件,可用 /fancy-analyze summary-status 查看实际进度。');
|
|
388
|
+
lines.push('⚠️ 本命令仅表示提示词已入队,不代表模型已完成写入。');
|
|
292
389
|
return lines.join('\n');
|
|
293
390
|
}
|
|
294
391
|
|
|
295
392
|
case 'summary_nothing_to_do': {
|
|
296
393
|
const lines = [];
|
|
297
|
-
lines.push('✅
|
|
394
|
+
lines.push('✅ 当前没有需要总结的来源。');
|
|
298
395
|
if (summary?.done?.length) {
|
|
299
396
|
lines.push('');
|
|
300
397
|
lines.push(`已完成:${summary.done.join('、')}`);
|
|
@@ -303,13 +400,13 @@ export const analyze = {
|
|
|
303
400
|
lines.push(`ℹ️ 无输入文件:${summary.skippedNoInput.join('、')}`);
|
|
304
401
|
}
|
|
305
402
|
lines.push('');
|
|
306
|
-
lines.push('💡
|
|
403
|
+
lines.push('💡 如需强制重新总结,请运行:/fancy-analyze resummary [目录名]');
|
|
307
404
|
return lines.join('\n');
|
|
308
405
|
}
|
|
309
406
|
|
|
310
407
|
case 'summary_reset': {
|
|
311
408
|
const lines = [];
|
|
312
|
-
lines.push(`♻️ 已重置 ${summary?.reset?.length || 0}
|
|
409
|
+
lines.push(`♻️ 已重置 ${summary?.reset?.length || 0} 个目录的总结`);
|
|
313
410
|
lines.push('');
|
|
314
411
|
for (const item of summary?.reset || []) {
|
|
315
412
|
lines.push(` · ${item.name} 旧总结已备份到:${item.backup}`);
|
|
@@ -319,7 +416,7 @@ export const analyze = {
|
|
|
319
416
|
lines.push(`♻️ 已重新派发:${summary.dispatched.length} 个目录`);
|
|
320
417
|
} else {
|
|
321
418
|
lines.push('');
|
|
322
|
-
lines.push('ℹ️
|
|
419
|
+
lines.push('ℹ️ 没有可派发的目录(源文件为空),仅执行了重置。');
|
|
323
420
|
}
|
|
324
421
|
lines.push('');
|
|
325
422
|
lines.push('💡 用 /fancy-analyze summary-status 查进度。');
|
|
@@ -331,13 +428,13 @@ export const analyze = {
|
|
|
331
428
|
// 也是对标/raw/sources/ 下的目录名)——
|
|
332
429
|
// 用 bookName 的话,调用处不传就会显示成《undefined》。
|
|
333
430
|
const doneBook = bookNameClean || bookName || '';
|
|
334
|
-
return `✅ 《${doneBook}
|
|
431
|
+
return `✅ 《${doneBook}》正在后台拆解…
|
|
335
432
|
|
|
336
433
|
📁 输出位置:${projectRoot}/对标/
|
|
337
434
|
├── schema.md ← LLM Wiki 项目配置
|
|
338
435
|
└── raw/sources/${bookNameClean}/ ← 拆书结果
|
|
339
436
|
|
|
340
|
-
💡 可在
|
|
437
|
+
💡 可在 llm-wiki 中查看拆解进度和结果,也可稍后执行 /fancy-analyze status 查看进度。`;
|
|
341
438
|
}
|
|
342
439
|
|
|
343
440
|
default:
|
|
@@ -678,21 +775,21 @@ ${projectRoot}/对标/${bookName}/
|
|
|
678
775
|
|
|
679
776
|
## 4. 完成检查
|
|
680
777
|
|
|
681
|
-
|
|
682
|
-
-
|
|
683
|
-
-
|
|
684
|
-
-
|
|
685
|
-
-
|
|
686
|
-
-
|
|
687
|
-
-
|
|
688
|
-
-
|
|
689
|
-
-
|
|
690
|
-
-
|
|
691
|
-
-
|
|
692
|
-
-
|
|
693
|
-
-
|
|
694
|
-
-
|
|
695
|
-
-
|
|
778
|
+
写完所有文件后,检查每个文件存在且非空(**用绝对路径查**,别拼相对路径):
|
|
779
|
+
- ${projectRoot}/对标/${bookName}/_summary.md(≥ 200 字)
|
|
780
|
+
- ${projectRoot}/对标/${bookName}/for_bible/characters.md(≥ 500 字)
|
|
781
|
+
- ${projectRoot}/对标/${bookName}/for_bible/relationships.md(≥ 200 字)
|
|
782
|
+
- ${projectRoot}/对标/${bookName}/for_bible/world.md(≥ 200 字)
|
|
783
|
+
- ${projectRoot}/对标/${bookName}/for_outline/plot_structure.md(≥ 200 字)
|
|
784
|
+
- ${projectRoot}/对标/${bookName}/for_outline/pacing.md(≥ 200 字)
|
|
785
|
+
- ${projectRoot}/对标/${bookName}/for_outline/chapter_templates.md(≥ 200 字)
|
|
786
|
+
- ${projectRoot}/对标/${bookName}/for_outline/foreshadow_patterns.md(≥ 200 字)
|
|
787
|
+
- ${projectRoot}/对标/${bookName}/for_write/voice.md(≥ 200 字)
|
|
788
|
+
- ${projectRoot}/对标/${bookName}/for_write/sentence_patterns.md(≥ 200 字)
|
|
789
|
+
- ${projectRoot}/对标/${bookName}/for_write/phrase_bank/action.md(≥ 10 条)
|
|
790
|
+
- ${projectRoot}/对标/${bookName}/for_write/phrase_bank/psychology.md(≥ 10 条)
|
|
791
|
+
- ${projectRoot}/对标/${bookName}/for_write/phrase_bank/environment.md(≥ 10 条)
|
|
792
|
+
- ${projectRoot}/对标/${bookName}/for_write/phrase_bank/dialogue.md(≥ 10 条)
|
|
696
793
|
`;
|
|
697
794
|
},
|
|
698
795
|
|
|
@@ -729,75 +826,308 @@ ${projectRoot}/对标/${bookName}/
|
|
|
729
826
|
* 而不是寄望模型自觉。
|
|
730
827
|
*
|
|
731
828
|
* @param {object} opts
|
|
732
|
-
* @param {Array<{name: string, sourceCount?: number, pendingCount?: number}>} opts.tasks
|
|
733
|
-
*
|
|
734
|
-
*
|
|
735
|
-
*
|
|
829
|
+
* @param {Array<{name: string, scope?: 'chapter'|'entity', sourceCount?: number, pendingCount?: number}>} opts.tasks
|
|
830
|
+
* 每个要处理的总结目录:name=目录名,scope=按章技法/跨章节实体,
|
|
831
|
+
* sourceCount=该目录共多少章节/实体页文件,
|
|
832
|
+
* pendingCount=按现有清单还差多少来源项(算不出来时省略)
|
|
833
|
+
* @param {number} [opts.remainingChapters] - 全部目录加起来还剩多少来源项
|
|
834
|
+
* (章节 + 实体页,只用于给用户看)
|
|
835
|
+
* @param {string} [opts.projectRoot] - 项目根绝对路径。
|
|
836
|
+
* 给了就把目录路径写成**绝对路径** —— 只给相对路径的话,
|
|
837
|
+
* LLM 的工作目录未必是项目根,文件会写到别的地方去。
|
|
838
|
+
* @param {string} [opts.registerScript] - 登记脚本(append-summary-chapter.js)的绝对路径。
|
|
839
|
+
* 给了才给"追加 + 登记"的命令模板 —— 脚本把这两件事做成幂等原子的,
|
|
840
|
+
* 不用命令模板时退回"手工编辑文件头"的旧说法。
|
|
736
841
|
* @returns {string} 提示词正文
|
|
737
842
|
*/
|
|
738
|
-
generateSummarySystemPrompt({ tasks, remainingChapters }) {
|
|
843
|
+
generateSummarySystemPrompt({ tasks, remainingChapters, projectRoot, registerScript }) {
|
|
739
844
|
const list = Array.isArray(tasks) ? tasks : [];
|
|
845
|
+
const quotedRegisterScript = registerScript ? shellSingleQuote(registerScript) : '';
|
|
846
|
+
const projectArg = projectRoot ? ` --project-root=${shellSingleQuote(projectRoot)}` : '';
|
|
740
847
|
const taskBlocks = list.map((task) => {
|
|
848
|
+
const isEntity = task.scope === 'entity';
|
|
741
849
|
const sourceCount = Number.isFinite(task.sourceCount) ? task.sourceCount : null;
|
|
742
850
|
const pendingCount = Number.isFinite(task.pendingCount) ? task.pendingCount : null;
|
|
851
|
+
const sourceUnit = isEntity ? '个实体页' : '个章节文件';
|
|
852
|
+
const pendingUnit = isEntity ? '个实体页' : '章';
|
|
743
853
|
const scale = [];
|
|
744
|
-
if (sourceCount !== null) scale.push(`共有 ${sourceCount}
|
|
745
|
-
if (pendingCount !== null) scale.push(`按现有清单还差 ${pendingCount}
|
|
854
|
+
if (sourceCount !== null) scale.push(`共有 ${sourceCount} ${sourceUnit}`);
|
|
855
|
+
if (pendingCount !== null) scale.push(`按现有清单还差 ${pendingCount} ${pendingUnit}`);
|
|
746
856
|
const scaleLine = scale.length > 0 ? `- 规模:${scale.join(' · ')}\n` : '';
|
|
747
857
|
const dir = task.name;
|
|
858
|
+
// 绝对路径优先:LLM 的工作目录未必是项目根,相对路径会写到别处
|
|
859
|
+
const sourceDir = projectRoot
|
|
860
|
+
? `${projectRoot}/对标/wiki/${dir}/`
|
|
861
|
+
: `对标/wiki/${dir}/`;
|
|
862
|
+
const summaryPath = projectRoot
|
|
863
|
+
? `${projectRoot}/对标/wiki/${dir}/${dir}-总结.md`
|
|
864
|
+
: `对标/wiki/${dir}/${dir}-总结.md`;
|
|
865
|
+
|
|
866
|
+
if (isEntity) {
|
|
867
|
+
return `#### 跨章节实体目录:${dir}
|
|
868
|
+
|
|
869
|
+
- 源文件目录:\`${sourceDir}\`
|
|
870
|
+
- 总结文件:\`${summaryPath}\`
|
|
871
|
+
${scaleLine}- 这个目录是跨章节实体页(角色弧光设计/势力结构/世界观设计/冲突设计/主线支线结构/分卷节奏/伏笔布局/人物关系设计)。每个文件是一个实体页,不是一章;不要按章节总结。
|
|
872
|
+
- **怎么找出这次要总结的实体页**(每个目录都照这个流程走):
|
|
873
|
+
1. 打开总结文件 \`${summaryPath}\`,**只看文件头**(开头两段 \`---\` 之间的 YAML),里面 \`已总结章节:\` 下列的就是**已经总结过**的实体页名(键名沿用旧格式,实际登记的是实体页名)。总结文件还不存在时,这一步跳过(等于一张白纸)
|
|
874
|
+
2. 列出源文件目录 \`${sourceDir}\` 下所有 \`.md\` 文件,**排除** \`${dir}-总结.md\` 本身
|
|
875
|
+
3. 第 2 步的清单减掉第 1 步的清单,**差集**就是这次要总结的实体页
|
|
876
|
+
4. **不要把实体清单写进你的计划或正文** —— 边读边处理,读一个处理一个
|
|
877
|
+
- **每个实体页重点读这两段**:
|
|
878
|
+
- \`## 可复用设计规则\`:作者已经提炼出的设计原则
|
|
879
|
+
- \`## 风险与反例\`:这种设计在什么情况下会失效、写崩、套路化
|
|
880
|
+
- 需要理解上下文时再看 \`## 实体定义\`、\`## 章节演进\`、\`## 当前状态\`,但**不要复述剧情**
|
|
881
|
+
- 如果这两段缺失或不完整,就直接读整页,按下面的角度提炼;不要因为缺段就跳过这个实体页
|
|
882
|
+
- **提炼角度(专业网文作家视角,必须回答)**:
|
|
883
|
+
1. **为什么这么设计**:是为了让人物更立体、服务某段剧情、凸显冲突、建立世界观可信度、控制节奏,还是满足读者期待?
|
|
884
|
+
2. **这么设计的好处**:对人物、剧情、关系、主题、情绪、读者体验分别有什么作用?
|
|
885
|
+
3. **它是怎么服务的**:通过什么结构、选择、代价、信息差、关系变化、节奏位置实现的?
|
|
886
|
+
4. **适用条件**:什么题材、什么阶段、什么冲突强度下有效?
|
|
887
|
+
5. **风险与反例**:什么时候会失效、套路化、写崩?
|
|
888
|
+
6. **迁移到骨架/血肉/节奏**:抽象成下一本书可以直接套用的原则。
|
|
889
|
+
- **不要做的事**:
|
|
890
|
+
- 不要逐条复述实体页的剧情;
|
|
891
|
+
- 不要把某个角色的具体经历当成通用规则;
|
|
892
|
+
- **正文**不要写原书专名、组织名、具体事件名,用“势力A/角色B/事件C”等占位符;
|
|
893
|
+
- **例外**:\`- 来源实体:\` 行必须写实体页的真实文件名(含原书名/角色名),不要替换成占位符;
|
|
894
|
+
- 不要把“可复用设计规则”和“风险与反例”简单拼接,要归纳成更高层、可迁移的设计原则;
|
|
895
|
+
- 不要把实体页的所有细节都搬进来:每条只保留最关键、可迁移的判断,每个字段 1–3 句,风险与反例只写 3–5 个最关键的失效条件。`;
|
|
896
|
+
}
|
|
897
|
+
|
|
748
898
|
return `#### 技法目录:${dir}
|
|
749
899
|
|
|
750
|
-
-
|
|
751
|
-
-
|
|
752
|
-
${scaleLine}-
|
|
753
|
-
|
|
754
|
-
|
|
900
|
+
- 源文件目录:\`${sourceDir}\`
|
|
901
|
+
- 总结文件:\`${summaryPath}\`
|
|
902
|
+
${scaleLine}- 这个目录是按章建页的写作技法或章节功能设计(叙事视角/节奏韵律/场景转换/章节功能设计/…)。
|
|
903
|
+
- **怎么找出这次要总结的章节**(每个目录都照这个流程走):
|
|
904
|
+
1. 打开总结文件 \`${summaryPath}\`,**只看文件头**(开头两段 \`---\` 之间的 YAML),里面 \`已总结章节:\` 下列的就是**已经总结过**的章节名(章节名 = 源文件名去掉 \`.md\` 后缀)。总结文件还不存在时,这一步跳过(等于一张白纸)
|
|
905
|
+
2. 列出源文件目录 \`${sourceDir}\` 下所有 \`.md\` 文件,**排除** \`${dir}-总结.md\` 本身
|
|
755
906
|
3. 第 2 步的清单减掉第 1 步的清单,**差集**就是这次要总结的章节
|
|
756
907
|
4. **不要把章节清单写进你的计划或正文** —— 边读边处理,读一个处理一个`;
|
|
757
908
|
}).join('\n\n');
|
|
758
909
|
|
|
759
|
-
const checkList = list.map((task) =>
|
|
910
|
+
const checkList = list.map((task) => {
|
|
911
|
+
const p = projectRoot
|
|
912
|
+
? `${projectRoot}/对标/wiki/${task.name}/${task.name}-总结.md`
|
|
913
|
+
: `对标/wiki/${task.name}/${task.name}-总结.md`;
|
|
914
|
+
return `- \`${p}\``;
|
|
915
|
+
}).join('\n');
|
|
760
916
|
|
|
761
917
|
const totalPending = Number.isFinite(remainingChapters) ? remainingChapters : null;
|
|
762
918
|
const scaleNote = totalPending !== null
|
|
763
|
-
? `> 全部目录加起来,按现有清单还差 **${totalPending}
|
|
919
|
+
? `> 全部目录加起来,按现有清单还差 **${totalPending} 个来源项(章节/实体页)**没总结。`
|
|
920
|
+
: '';
|
|
921
|
+
const missingFileHint = registerScript
|
|
922
|
+
? '下一次写入命令会自动创建'
|
|
923
|
+
: '按第二步手动写入文件头';
|
|
924
|
+
|
|
925
|
+
// 有登记脚本才给命令模板(脚本负责幂等 + 原子写)
|
|
926
|
+
const chapterWriteSection = registerScript
|
|
927
|
+
? `**第二步:写入(必须用下面这条命令)**
|
|
928
|
+
|
|
929
|
+
一条命令同时做两件事:把技法内容追加到「## 核心技法」下 + 把章节名登记进文件头的「已总结章节」清单。
|
|
930
|
+
|
|
931
|
+
登记脚本已经放在项目里了(下面这条命令里的路径就在你的项目目录下),直接调用即可:
|
|
932
|
+
|
|
933
|
+
\`\`\`bash
|
|
934
|
+
node ${quotedRegisterScript}${projectArg} --file="[总结文件绝对路径]" --chapter="[章节名]" <<'FANCY_SUMMARY_EOF'
|
|
935
|
+
### 提炼出的技法名称
|
|
936
|
+
- 来源章节:[来自哪些文件]
|
|
937
|
+
- 核心要点:[技法的核心内容]
|
|
938
|
+
- 使用场景:[在哪些情况下使用]
|
|
939
|
+
- 应用方式:[如何应用]
|
|
940
|
+
FANCY_SUMMARY_EOF
|
|
941
|
+
\`\`\`
|
|
942
|
+
|
|
943
|
+
- 命令里的 \`[章节名]\` 填这一章去掉 \`.md\` 后缀的名字
|
|
944
|
+
- **\`[章节名]\` 只是占位符,实际值不要带方括号**:\`--chapter\` 和 \`- 来源章节:\` 都写纯文件名(如 \`书名-技法-第3章\`),不要写 \`[书名-技法-第3章]\`
|
|
945
|
+
- **\`- 来源章节:\` 必须原样照抄源文件名**(含原书名、数字、英文),它是登记与反推的依据;它不算“原书专名”,不要翻译或替换成占位符
|
|
946
|
+
- 命令里的 \`--project-root\` 是项目根,脚本会用它校验总结文件没有跑到项目外;不要改成别的路径
|
|
947
|
+
- 命令里的 \`[总结文件绝对路径]\` 填**当前正在处理的那个技法目录**的总结文件绝对路径
|
|
948
|
+
—— 上面每个「技法目录」小节都列了它(\`源文件目录\` / \`总结文件\` 两行)。
|
|
949
|
+
**填错目录,章节就登记到别的目录的清单里**:那一章下次还会被重做,
|
|
950
|
+
而被误登记的那个目录会多出一条它根本不该有的章节
|
|
951
|
+
- heredoc 里是**要写进总结文件的正文内容**(脚本从 stdin 读它),
|
|
952
|
+
格式就照上面这几行:\`###\` 开头是技法名,下面四个 \`-\` 分别是来源、要点、场景、用法
|
|
953
|
+
- **如果这一章没有新的可复用技法,heredoc 可以留空** —— 脚本仍会登记章节,但不会追加正文
|
|
954
|
+
- **\`- 来源章节:\` 这一行必须写** —— 它是头部损坏时反推已总结章节的兜底依据
|
|
955
|
+
(见第三步的 \`entriesMissingSource\`),缺了就只剩头部一个事实源
|
|
956
|
+
- **不要用 write 重写整个总结文件** —— write 会把文件头里已经登记的章节清单一起冲掉,
|
|
957
|
+
之前总结过的章节会被当成"没总结",下次执行时全部重做一遍,文件越滚越胖
|
|
958
|
+
- 正文块里只写技法条目本身(\`###\` 开头的那几行),**不要写代码块围栏**
|
|
959
|
+
- **文件头不用你写** —— 命令会自动建好并维护它,你只要管正文内容
|
|
960
|
+
|
|
961
|
+
**命令返回的 JSON 怎么看**:
|
|
962
|
+
|
|
963
|
+
\`\`\`json
|
|
964
|
+
{"ok":true,"added":true,"alreadyRegistered":false,"bodyAdded":true,"bodyAlreadyPresent":false,"total":12,...}
|
|
965
|
+
\`\`\`
|
|
966
|
+
|
|
967
|
+
- \`ok:true\` = 这次写入**确实落盘了**(写盘失败时命令会非零退出并返回 \`ok:false\` + \`error\`)
|
|
968
|
+
- \`added:true\` = 这一章刚刚登记成功;\`added:false\` 且 \`alreadyRegistered:true\`
|
|
969
|
+
= 这一章之前就登记过,**跳过它**,不要重复总结,也不要再追加正文
|
|
970
|
+
- \`bodyAdded:true\` = 这次确实追加了技法正文;\`bodyAlreadyPresent:true\`
|
|
971
|
+
= 正文里已经有这一章的「来源章节」行,脚本没有重复追加
|
|
972
|
+
- \`total\` = 写完后已登记的章节总数
|
|
973
|
+
|
|
974
|
+
**写入失败怎么办(\`ok:false\`)**:
|
|
975
|
+
|
|
976
|
+
- \`error\` 里说了原因(比如路径不对、目录没权限)→ **先修掉原因再重试**,
|
|
977
|
+
同一章最多重试一次。重试仍失败就**跳过这一章,继续下一章**,
|
|
978
|
+
并在最后的报告里写明"第 N 章写入失败:原因"。
|
|
979
|
+
- **跳过是安全的**:这一章没登记成功,下次执行 summary 会自动再处理它,
|
|
980
|
+
不会漏。**不要为了"必须全部成功"而反复重试或手工改文件**。
|
|
981
|
+
- 如果 \`repaired:true\`(头部原本是坏的,已自动修复)→ 看 \`recovered\`:
|
|
982
|
+
那是脚本从残留内容里反推回来的章节数。清单可能仍不完整,
|
|
983
|
+
**照常继续**,但要在报告里提一句。
|
|
984
|
+
|
|
985
|
+
**第三步:校验登记状态(只读,不修改文件)**
|
|
986
|
+
|
|
987
|
+
第二步那条命令已经把章节登记进文件头了,这一步只是**看一眼**状态:
|
|
988
|
+
|
|
989
|
+
\`\`\`bash
|
|
990
|
+
node ${quotedRegisterScript}${projectArg} --file="[总结文件绝对路径]" --verify
|
|
991
|
+
\`\`\`
|
|
992
|
+
|
|
993
|
+
它会回一行 JSON,**\`ok\` 已经是判定结果** —— 不用你自己推断字段之间的关系:
|
|
994
|
+
|
|
995
|
+
\`\`\`json
|
|
996
|
+
{"ok":true,"problems":[],"total":52,"last":"甲-第3章","entries":48,
|
|
997
|
+
"entriesWithSource":48,"entriesMissingSource":0,"duplicates":[],...}
|
|
998
|
+
\`\`\`
|
|
999
|
+
|
|
1000
|
+
**正常 = \`ok:true\` 且 \`problems\` 是空数组**(这是总的判定,脚本已经把所有
|
|
1001
|
+
问题都算进 \`problems\` 了,每条都有一句中文说明)。各字段的含义:
|
|
1002
|
+
|
|
1003
|
+
- \`total\` = 已登记的章节数;\`last\` = 最后登记的那一条(**跟章节总数无关**,
|
|
1004
|
+
章节攒到几百个也能看清当前状态)
|
|
1005
|
+
- \`entries\` = 「核心技法」下的技法条目数;\`entriesMissingSource\` = 其中缺
|
|
1006
|
+
「来源章节」行的条数,**必须是 0**
|
|
1007
|
+
- \`duplicates\` = 空数组
|
|
1008
|
+
- \`frontmatterOk:true\`、\`hasCoreSection:true\`
|
|
1009
|
+
|
|
1010
|
+
**还要对一下"写进的是不是这个目录"**(填错路径时脚本不会报错,只会把章节登记到别的目录):
|
|
1011
|
+
- \`last\` 必须是你**刚写的那一章**(\`--chapter\` 里给的那个名字)
|
|
1012
|
+
- 写入返回 \`added:true\` 时,\`total\` 必须比写之前**多 1**
|
|
1013
|
+
(写前先跑一次 \`--verify\` 记下 total;总结文件还不存在时基线按 0 算,首次写完 total 应为 1)
|
|
1014
|
+
- 写入返回 \`added:false\` / \`alreadyRegistered:true\` 时,\`total\` **不变**,
|
|
1015
|
+
说明这一章之前就登记过,直接跳过,不要重复总结
|
|
1016
|
+
- 对不上 → **路径填错了**:换成上面那个技法目录的总结文件绝对路径重跑一次。
|
|
1017
|
+
已经写到别的目录的那一条,用那个目录的 \`--verify\` 找到它并说明情况
|
|
1018
|
+
|
|
1019
|
+
**首次写入前总结文件还不存在时**:\`--verify\` 会返回 \`exists:false\`,
|
|
1020
|
+
这是正常的起点,不是坏文件。首次写入后文件应存在,且 \`total\` 为 1。
|
|
1021
|
+
|
|
1022
|
+
**任何一项不对就是不对**(\`ok:false\`)。此时 \`problems\` 里会写清楚是哪一条,
|
|
1023
|
+
按它说的处理:
|
|
1024
|
+
|
|
1025
|
+
- \`frontmatterOk:false\`(文件头坏了)→ **先别写这个目录**。
|
|
1026
|
+
头部是唯一事实源,它坏了你就不知道哪些章节已经总结过,
|
|
1027
|
+
继续写只会把状态搞得更乱。用 \`recovered\` 字段看看脚本反推回了多少章,
|
|
1028
|
+
把它记在报告里,然后**跳到下一个目录**。
|
|
1029
|
+
- \`entriesMissingSource\` > 0(有条目缺「来源章节」行)→ 记进报告,
|
|
1030
|
+
继续处理下一个目录。**不要为了补这一行重复追加条目** —— 脚本只会追加,
|
|
1031
|
+
不会改写已有条目,重复追加只会让文件更乱;下次 summary 仍会按头部清单跳过这些章节。
|
|
1032
|
+
- \`duplicates\` 非空 / \`hasCoreSection:false\` → 记进报告,
|
|
1033
|
+
继续处理下一个目录,别在坏状态上继续写。
|
|
1034
|
+
|
|
1035
|
+
**\`entries\` 和 \`total\` 不要求相等**,谁大谁小都正常:
|
|
1036
|
+
- 一章提炼出多条技法 → \`entries\` > \`total\`
|
|
1037
|
+
- 某章没有新的可复用技法,只登记章节 → \`entries\` < \`total\`
|
|
1038
|
+
- 一章一条 → \`entries\` == \`total\`
|
|
1039
|
+
|
|
1040
|
+
**⚠️ 不要修改登记脚本本身** —— \`${registerScript}\` 是插件提供的**只读工具**,
|
|
1041
|
+
它由插件在每次派发前自动铺好(内容变了会被覆盖回去)。
|
|
1042
|
+
只管调用它,不要读写、改写、移动或删除这个文件;你要写的**只有**
|
|
1043
|
+
总结文件本身(\`--file=\` 指向的那个)。`
|
|
1044
|
+
: `**第二步:写入文件头(仅当文件不存在时)**
|
|
1045
|
+
|
|
1046
|
+
每次追加写入前,先检查总结文件是否存在。如果不存在,则先写入文件头(见下方「写入格式」)。
|
|
1047
|
+
|
|
1048
|
+
**第三步:追加技法内容(每次提炼后)**
|
|
1049
|
+
|
|
1050
|
+
如果文件已存在,则在 \`## 核心技法\` 章节下追加技法条目(见下方「写入格式」)。
|
|
1051
|
+
|
|
1052
|
+
**第四步:登记已总结章节(每总结完一章立刻做)**
|
|
1053
|
+
|
|
1054
|
+
**每总结完一章,立刻把该章节名追加进总结文件头部的「已总结章节」列表**
|
|
1055
|
+
(不要覆盖已有条目,不要重复添加同一个章节名)。
|
|
1056
|
+
|
|
1057
|
+
**重要**:不要用 write 重写整个总结文件 —— 那会把文件头里已登记的章节清单冲掉,
|
|
1058
|
+
之前总结过的章节会被当成"没总结",下次执行时全部重做一遍。`;
|
|
1059
|
+
|
|
1060
|
+
const hasEntityTasks = list.some((task) => task.scope === 'entity');
|
|
1061
|
+
const entityWriteSection = registerScript && hasEntityTasks
|
|
1062
|
+
? `**跨章节实体目录:写入格式**
|
|
1063
|
+
|
|
1064
|
+
跨章节实体目录使用同一登记脚本,但要用 \`--section="核心设计原则"\`,正文按下面格式写:
|
|
1065
|
+
|
|
1066
|
+
\`\`\`bash
|
|
1067
|
+
node ${quotedRegisterScript}${projectArg} --file="[实体目录总结文件绝对路径]" \\
|
|
1068
|
+
--chapter="[实体页名]" --section="核心设计原则" <<'FANCY_SUMMARY_EOF'
|
|
1069
|
+
### 设计原则名称
|
|
1070
|
+
- 来源实体:[实体页名]
|
|
1071
|
+
- 为什么这么设计:[让人物立体/服务剧情/凸显冲突/建立世界观/控制节奏/满足读者期待]
|
|
1072
|
+
- 设计好处:[对人物、剧情、关系、主题、情绪、读者体验的作用]
|
|
1073
|
+
- 如何服务剧情/人物/主题:[通过什么结构、选择、代价、信息差、关系变化实现]
|
|
1074
|
+
- 适用条件:[什么题材、阶段、冲突强度下有效]
|
|
1075
|
+
- 风险与反例:[什么时候会失效、套路化、写崩]
|
|
1076
|
+
- 迁移到骨架/血肉/节奏:[抽象成下一本书可套用的原则]
|
|
1077
|
+
FANCY_SUMMARY_EOF
|
|
1078
|
+
\`\`\`
|
|
1079
|
+
|
|
1080
|
+
- \`--chapter\` 填**实体页名**(去掉 \`.md\` 后缀),不是章节名;
|
|
1081
|
+
- **\`[实体页名]\` 只是占位符,实际值不要带方括号**:\`--chapter\` 和 \`- 来源实体:\` 都写纯文件名(如 \`书名-角色-角色A\`),不要写 \`[书名-角色-角色A]\`
|
|
1082
|
+
- **\`- 来源实体:\` 必须原样照抄实体页文件名**(含原书名、角色名、数字、英文),它是登记与反推的依据;它不算“原书专名”,不要用“角色A/势力A”等占位符替换
|
|
1083
|
+
- \`--section\` 必须是 \`核心设计原则\`,否则脚本会去「核心技法」里找条目并报 \`hasCoreSection:false\`;
|
|
1084
|
+
- \`- 来源实体:\` 这一行必须写,它是头部损坏时反推已总结实体页的兜底依据;
|
|
1085
|
+
- 如果这个实体页没有新的可复用原则,heredoc 可以留空,但仍要登记实体页名;
|
|
1086
|
+
- 校验时用 \`--verify --section="核心设计原则"\`。`
|
|
764
1087
|
: '';
|
|
765
1088
|
|
|
766
1089
|
return `你是一位经验丰富、资历深厚、在小说领域有着独到见解并且逻辑思维缜密的小说技法分析师。
|
|
767
1090
|
|
|
768
1091
|
## 核心铁律
|
|
769
1092
|
**每次调用都是全新流程**,不读取上下文,不读取之前的选择,不读 memory。
|
|
770
|
-
|
|
1093
|
+
**只处理下面明确列出的总结目录**,不要自行扩大范围。
|
|
771
1094
|
|
|
772
1095
|
${scaleNote}
|
|
773
|
-
> ⚠️
|
|
774
|
-
>
|
|
1096
|
+
> ⚠️ 本提示词**故意不列具体来源名** —— 一本长篇可能几千章、上千个实体页,列出来会把提示词撑爆。
|
|
1097
|
+
> 哪些来源还没总结,由你按每个目录下方写的步骤**自己去目录里判断**。
|
|
775
1098
|
|
|
776
1099
|
## ⚠️ 语言强制要求
|
|
777
1100
|
|
|
778
|
-
|
|
779
|
-
- 文件名:使用中文
|
|
1101
|
+
**总结正文和标题必须使用中文:**
|
|
780
1102
|
- 标题:使用中文
|
|
781
1103
|
- 正文内容:所有文字描述必须使用中文
|
|
782
1104
|
- 表格内容:所有表格中的文字必须使用中文
|
|
783
|
-
- YAML
|
|
1105
|
+
- YAML 值:使用中文(文件头由登记脚本维护,键名不用你改;登记的文件名照抄,不要翻译)
|
|
1106
|
+
|
|
1107
|
+
**文件路径 / 文件名沿用上面给出的路径,不要改名。**
|
|
784
1108
|
|
|
785
1109
|
**禁止输出英文内容:**
|
|
786
1110
|
- ❌ 英文描述、英文分析
|
|
787
|
-
- ❌ 英文 YAML 键
|
|
788
1111
|
- ❌ 英文 YAML 值
|
|
789
1112
|
- ❌ 中英混合
|
|
790
1113
|
|
|
1114
|
+
**登记字段例外**:\`--chapter\`、\`- 来源章节:\`、\`- 来源实体:\` 三处必须**原样照抄源文件名/实体页名**(去掉 \`.md\` 后缀)。文件名里可能带英文、数字、原书名,这是登记依据,不算“英文内容”;不要翻译、不要改写、不要加方括号。
|
|
1115
|
+
|
|
791
1116
|
## ⚠️ 占位符说明
|
|
792
1117
|
|
|
793
1118
|
下方提示词中用 **\`[\`** 和 **\`]\`** 包裹的内容都是**占位符**,需要你根据读取到的文件内容进行推理和补充,不要直接输出占位符原文。
|
|
1119
|
+
**代码块只是格式展示** —— 写进文件时只写内容本身,**不要把 \`\`\` 围栏一起写进文件**。
|
|
1120
|
+
|
|
1121
|
+
**特别注意**:\`--chapter\`、\`- 来源章节:\`、\`- 来源实体:\` 这三处**不要保留方括号**。实际值必须是源文件名/实体页名去掉 \`.md\` 后的纯文本(例如 \`书名-角色-角色A\`,不是 \`[书名-角色-角色A]\`)。
|
|
1122
|
+
|
|
1123
|
+
**标题不要保留方括号**:命令块里的 \`### 提炼出的技法名称\` / \`### 设计原则名称\` 只是占位格式;实际把“提炼出的技法名称”/“设计原则名称”替换成真实标题,不要写成 \`### [提炼出的技法名称]\`。
|
|
794
1124
|
|
|
795
1125
|
## 任务
|
|
796
1126
|
|
|
797
|
-
|
|
798
|
-
|
|
1127
|
+
请对下面每个目录里**尚未总结**的来源进行归纳、总结、提炼、挖掘、扩展。
|
|
1128
|
+
按章技法目录提炼“可复用的写作技法”;跨章节实体目录提炼“可复用的设计原则”(为什么这么设计、好处、如何服务剧情/人物/冲突/节奏/主题/读者体验)。
|
|
799
1129
|
|
|
800
|
-
###
|
|
1130
|
+
### 本次要总结的目录
|
|
801
1131
|
|
|
802
1132
|
${taskBlocks}
|
|
803
1133
|
|
|
@@ -805,57 +1135,22 @@ ${taskBlocks}
|
|
|
805
1135
|
|
|
806
1136
|
**增量写入方式**:为避免超出上下文限制,请采用增量写入方式,每个目录处理流程如下:
|
|
807
1137
|
|
|
808
|
-
1.
|
|
809
|
-
2. **逐文件读取提炼**:依次读取算出来的每个文件(文件名去掉 \`.md\`
|
|
810
|
-
3.
|
|
811
|
-
4.
|
|
812
|
-
5. **做不完就停**:按你的上下文能力处理,做到快满时停下,剩下的**下次执行 summary
|
|
813
|
-
6.
|
|
1138
|
+
1. **按上述步骤算出这次要总结的来源**(读总结文件头 → 列源文件 → 求差集)
|
|
1139
|
+
2. **逐文件读取提炼**:依次读取算出来的每个文件(文件名去掉 \`.md\` 后缀即为章节名/实体页名),边读边提炼
|
|
1140
|
+
3. **每读完一个文件,立刻写入**(写入方式见下一节)
|
|
1141
|
+
4. **不要写来源清单**:不要把"待总结来源列表"整体写进计划、进度汇报或正文里(章节/实体可能上千个,写出来会挤爆上下文);只报"处理了 N 个"这类数字即可
|
|
1142
|
+
5. **做不完就停**:按你的上下文能力处理,做到快满时停下,剩下的**下次执行 summary 会继续**(已完成的来源已经登记,不会重复)
|
|
1143
|
+
6. **去重**:新来源与已有条目重复时,不要重复追加同一条;如果这一来源没有新的可复用内容,只登记来源(heredoc 留空),不要用 write 删改已有条目
|
|
814
1144
|
7. **继续下一目录**:完成一个目录后,继续处理下一个目录
|
|
1145
|
+
8. **控制并发**:如果工具支持子代理,可以按目录并行,但一次最多 4–5 个;不要一次开 20 个(可能触发模型服务的 429 限流)。遇到 429/限流就降低并发、等待后重试,不要把同一批全部重开
|
|
815
1146
|
|
|
816
1147
|
### 写入格式
|
|
817
1148
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
每次追加写入前,先检查总结文件是否存在。如果不存在,则先写入以下内容:
|
|
1149
|
+
第一步是“算出这次要总结的来源”(见上面「操作要求」);下面说明写入方式(使用登记脚本时还会做一次只读校验)。
|
|
821
1150
|
|
|
822
|
-
|
|
823
|
-
---
|
|
824
|
-
标题: [目录名]-总结
|
|
825
|
-
标签: [总结, 目录名]
|
|
826
|
-
已总结章节:
|
|
827
|
-
- [已总结的章节名,去掉 .md 后缀]
|
|
828
|
-
---
|
|
829
|
-
# [目录名]技法总结
|
|
830
|
-
|
|
831
|
-
## 核心技法
|
|
832
|
-
\`\`\`
|
|
833
|
-
|
|
834
|
-
**第二步:追加技法内容(每次提炼后)**
|
|
835
|
-
|
|
836
|
-
如果文件已存在,则在 \`## 核心技法\` 章节下追加以下内容:
|
|
837
|
-
|
|
838
|
-
\`\`\`yaml
|
|
839
|
-
### [提炼出的技法名称]
|
|
840
|
-
- 来源章节:[来自哪些文件]
|
|
841
|
-
- 核心要点:[技法的核心内容]
|
|
842
|
-
- 使用场景:[在哪些情况下使用]
|
|
843
|
-
- 应用方式:[如何应用]
|
|
844
|
-
\`\`\`
|
|
845
|
-
|
|
846
|
-
**第三步:登记已总结章节(每总结完一章立刻做)**
|
|
847
|
-
|
|
848
|
-
在文件头部的 \`已总结章节:\` 列表下**追加**一行(不要覆盖已有条目,
|
|
849
|
-
不要重复添加同一个章节名):
|
|
850
|
-
|
|
851
|
-
\`\`\`yaml
|
|
852
|
-
已总结章节:
|
|
853
|
-
- [第1个章节名]
|
|
854
|
-
- [第2个章节名]
|
|
855
|
-
\`\`\`
|
|
1151
|
+
${chapterWriteSection}${entityWriteSection ? '\n\n' + entityWriteSection : ''}
|
|
856
1152
|
|
|
857
|
-
|
|
858
|
-
→ 章节名 \`[书名]-[技法名]-第3章\`)。
|
|
1153
|
+
来源名 = 源文件名去掉 \`.md\` 后缀。例如源文件 \`书名-技法名-第3章.md\` → 来源名 \`书名-技法名-第3章\`;实体页同理,如 \`书名-角色-角色名.md\` → \`书名-角色-角色名\`。实际文件名可能是中文、英文、数字,**原样照抄,不要加方括号**。
|
|
859
1154
|
|
|
860
1155
|
## 闭环检查
|
|
861
1156
|
|
|
@@ -864,17 +1159,20 @@ ${taskBlocks}
|
|
|
864
1159
|
**文件存在性检查**:确认以下每个目录下都存在总结文件:
|
|
865
1160
|
${checkList}
|
|
866
1161
|
|
|
867
|
-
|
|
1162
|
+
如果某个目录还没有总结文件:${missingFileHint}。
|
|
868
1163
|
|
|
869
|
-
|
|
870
|
-
|
|
1164
|
+
**来源登记检查**:确认每个总结文件的 \`已总结章节:\` 列表包含了该目录下
|
|
1165
|
+
**你已经处理过的全部来源(章节 / 实体页)**。少登记一个,下次执行 summary 就会重复总结它。
|
|
871
1166
|
|
|
872
1167
|
## ⚠️ 重要提醒
|
|
873
1168
|
|
|
874
|
-
-
|
|
875
|
-
-
|
|
876
|
-
-
|
|
877
|
-
-
|
|
1169
|
+
- **去重原则**:已存在的条目不要重复追加;没有新内容时只登记来源(heredoc 留空)
|
|
1170
|
+
- **不越界**:只处理算出来的差集,不要顺手处理目录里其它来源
|
|
1171
|
+
- **内容要求**:按章目录总结具体、可操作的写作技法;实体目录总结具体、可迁移的设计原则(为什么这么设计、好处、服务对象、适用条件、风险),不是剧情复述
|
|
1172
|
+
- **控制篇幅**:每条只保留最关键、可迁移的判断;每个字段 1–3 句;风险与反例只写 3–5 个最关键的失效条件,不要把实体页的所有细节都搬进来
|
|
1173
|
+
- **正文抽象**:按章目录也不要把原书人名、地名、组织名写进正文;具体数字、台词只作为示例,用“例如”引出,不要当成通用规则。\`来源章节\` / \`来源实体\` 行例外,必须原样照抄文件名
|
|
1174
|
+
- **写完自检**:正文是否混入原书人名、地名、组织名、专有体系名、英文缩写或方括号?有就替换成通用表述;\`来源章节\` / \`来源实体\` 行必须保留真实文件名,不参与抽象
|
|
1175
|
+
- **别冲掉清单**:不要用 write 覆盖重写总结文件,登记过的来源会丢
|
|
878
1176
|
|
|
879
1177
|
请开始执行总结任务,完成后输出总结报告。`;
|
|
880
1178
|
},
|