@cloud411716/fancy-webnovel 1.0.46 → 3.0.0
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/client.js +369 -0
- package/cordis.patch.yml +4 -13
- 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 +26 -38
- package/lib/atomic-write.js +109 -0
- package/lib/chapter-order.js +100 -0
- package/lib/chinese-num.js +111 -0
- package/lib/confirmation.js +80 -0
- package/lib/db-init.js +123 -0
- package/lib/deployed-state.js +141 -0
- package/lib/direction-labels.js +265 -0
- package/lib/error-codes.js +104 -0
- package/lib/infra.js +594 -0
- package/lib/llm-wiki.js +109 -0
- 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 +305 -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 +62 -5
- package/plugins/fancy-analyze/analyze-definitions.md +519 -0
- package/plugins/fancy-analyze/command-input.js +65 -0
- package/plugins/fancy-analyze/index.js +2024 -0
- package/plugins/fancy-analyze/ingest-coverage.js +107 -0
- package/plugins/fancy-analyze/schema-analyze.md +627 -0
- package/plugins/fancy-analyze/schema-write.md +178 -0
- package/plugins/fancy-analyze/scripts/append-summary-chapter.js +612 -0
- package/plugins/fancy-analyze/scripts/split_source.js +292 -0
- package/plugins/fancy-analyze/summary-progress.js +495 -0
- package/plugins/fancy-analyze/templates.js +1179 -0
- package/plugins/fancy-bootstrap/index.js +313 -0
- package/plugins/fancy-bootstrap/scripts/init.js +177 -0
- package/plugins/fancy-bootstrap/scripts/path-utils.js +21 -0
- package/plugins/fancy-bootstrap/templates.js +71 -0
- 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-core.js +1118 -0
- package/plugins/fancy-import/import-driver.js +604 -0
- package/plugins/fancy-import/index.js +738 -0
- package/plugins/fancy-import/prompts/01-import-prompt.md +131 -0
- package/plugins/fancy-import/templates.js +58 -0
- 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 +143 -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 +708 -0
- 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 +667 -0
- package/plugins/fancy-scan/scripts/scrapers/fanqie-rank-scraper.js +923 -0
- package/plugins/fancy-scan/scripts/scrapers/jjwxc-rank-scraper.js +469 -0
- package/plugins/fancy-scan/scripts/scrapers/qimao-rank-scraper.js +629 -0
- 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 +694 -0
- 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/deploy-manifest.js +239 -0
- package/plugins/fancy-story-builder/deslop-driver.js +821 -0
- package/plugins/fancy-story-builder/flesh-output.js +168 -0
- package/plugins/fancy-story-builder/index.js +6861 -0
- package/plugins/fancy-story-builder/patch-driver.js +390 -0
- package/plugins/fancy-story-builder/prompts/01-skeleton-prompt.md +563 -0
- package/plugins/fancy-story-builder/prompts/02-flesh-prompt.md +602 -0
- package/plugins/fancy-story-builder/prompts/03-rhythm-prompt.md +539 -0
- package/plugins/fancy-story-builder/prompts/04-writing-prompt.md +225 -0
- package/plugins/fancy-story-builder/prompts/05-deslop-prompt.md +96 -0
- package/plugins/fancy-story-builder/purpose-template.md +83 -0
- package/plugins/fancy-story-builder/references/formulas-rhythm.json +302 -0
- package/plugins/fancy-story-builder/references/formulas-skeleton.json +402 -0
- package/plugins/fancy-story-builder/references/genre-profiles.json +3514 -0
- package/plugins/fancy-story-builder/references/glossary.md +594 -0
- package/plugins/fancy-story-builder/references/index.md +581 -0
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/00-index.md +174 -0
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/01-scene.md +150 -0
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/02-chapter-workflow.md +99 -0
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/03-opening-chapter.md +104 -0
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/04-dialogue-mastery.md +274 -0
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/05-genre-formulas.md +432 -0
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/06-tone-ensemble.md +259 -0
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/07-ledger-audit.md +222 -0
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/08-diagnosis-revision.md +205 -0
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/09-reading-power.md +135 -0
- package/plugins/fancy-story-builder/references//345/206/231/344/275/234/346/214/207/345/257/274/10-deslop.md +129 -0
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/00-index.md +98 -0
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/01-upgrade-rhythm.md +300 -0
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/02-arc-rhythm.md +168 -0
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/03-highpoint-hooks.md +189 -0
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/04-satisfaction.md +226 -0
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/05-foreshadow-density.md +195 -0
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/06-chapter-outline.md +226 -0
- package/plugins/fancy-story-builder/references//350/212/202/345/245/217/346/214/207/345/257/274/07-audit.md +181 -0
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/00-index.md +100 -0
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/01-character.md +243 -0
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/02-worldbuilding.md +245 -0
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/03-relations.md +185 -0
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/04-emotion-methods.md +286 -0
- package/plugins/fancy-story-builder/references//350/241/200/350/202/211/346/214/207/345/257/274/05-audit.md +187 -0
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/00-index.md +91 -0
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/01-positioning.md +209 -0
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/02-packaging.md +135 -0
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/03-entities.md +305 -0
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/04-conflict.md +328 -0
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/05-quota.md +187 -0
- package/plugins/fancy-story-builder/references//351/252/250/346/236/266/346/214/207/345/257/274/06-audit.md +160 -0
- package/plugins/fancy-story-builder/rhythm-output.js +140 -0
- package/plugins/fancy-story-builder/schema-setting.json +1100 -0
- package/plugins/fancy-story-builder/schema-setting.md +232 -0
- package/plugins/fancy-story-builder/scripts/ai-patterns.js +619 -0
- package/plugins/fancy-story-builder/scripts/brief-validate.js +164 -0
- package/plugins/fancy-story-builder/scripts/check-guide-compliance.js +289 -0
- package/plugins/fancy-story-builder/scripts/content-quality.js +416 -0
- package/plugins/fancy-story-builder/scripts/deslop-validate.js +143 -0
- package/plugins/fancy-story-builder/scripts/error-classes.js +112 -0
- package/plugins/fancy-story-builder/scripts/flesh-validate.js +1312 -0
- package/plugins/fancy-story-builder/scripts/formula-engine.js +112 -0
- package/plugins/fancy-story-builder/scripts/graph-utils.js +1484 -0
- package/plugins/fancy-story-builder/scripts/label-coverage.js +171 -0
- package/plugins/fancy-story-builder/scripts/prompt-drift-check.js +441 -0
- package/plugins/fancy-story-builder/scripts/reading-power.js +298 -0
- package/plugins/fancy-story-builder/scripts/reference-log.js +110 -0
- package/plugins/fancy-story-builder/scripts/rhythm-validate.js +1312 -0
- package/plugins/fancy-story-builder/scripts/schema-pages.js +2668 -0
- package/plugins/fancy-story-builder/scripts/semantic-lint.js +381 -0
- package/plugins/fancy-story-builder/scripts/skeleton-quota.js +145 -0
- package/plugins/fancy-story-builder/scripts/skeleton-validate.js +1973 -0
- package/plugins/fancy-story-builder/scripts/stage-batches.js +1405 -0
- package/plugins/fancy-story-builder/scripts/stage-contract.js +429 -0
- package/plugins/fancy-story-builder/scripts/story-artifacts.js +933 -0
- package/plugins/fancy-story-builder/scripts/upstream-query.js +198 -0
- 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/scripts/writing-rules.js +167 -0
- package/plugins/fancy-story-builder/scripts/writing-validate.js +682 -0
- package/plugins/fancy-story-builder/skeleton-output.js +199 -0
- package/plugins/fancy-story-builder/stage-driver.js +1049 -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 +427 -0
- package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//347/253/240/350/212/202/347/273/206/347/272/262/346/250/241/346/235/277.json +34 -0
- package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//347/253/240/350/212/202/350/247/204/345/210/222/346/250/241/346/235/277.json +24 -0
- package/plugins/fancy-story-builder/templates//350/212/202/345/245/217//350/212/202/345/245/217/345/217/202/346/225/260/346/250/241/346/235/277.json +27 -0
- 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 +168 -0
- 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 +184 -0
- 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 +131 -0
- 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 +85 -0
- 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 +103 -0
- 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 +68 -0
- 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 +142 -0
- 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 +292 -0
- 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 +266 -0
- 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 +336 -0
- package/plugins/fancy-story-builder/templates//351/252/250/346/236/266//345/261/202/347/272/247/346/250/241/346/235/277.json +65 -0
- 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 +308 -0
- package/plugins/fancy-story-builder/templates.js +1446 -0
- package/plugins/fancy-story-builder/wiki-references.js +540 -0
- package/plugins/fancy-story-builder/writing-driver.js +1388 -0
- package/plugins/fancy-topic/index.js +1057 -0
- 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 +317 -0
- package/plugins/fancy-topic/templates.js +476 -0
- package/plugins/fancy-topic/tool-deploy.js +200 -0
- package/commands/bootstrap/index.js +0 -164
- package/commands/bootstrap/init-project.js +0 -121
- package/commands/scan/index.js +0 -283
- package/commands/scan/platforms/fanqie.js +0 -146
- package/commands/scan/platforms/jinjiang.js +0 -143
- package/commands/scan/platforms/qidian.js +0 -102
- package/commands/scan/platforms/qimao.js +0 -127
- package/commands/scan/scraper.js +0 -195
- package/infra.js +0 -160
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin command bridge for the storydb-native generation flow.
|
|
3
|
+
*
|
|
4
|
+
* `fancy-build scale|macro|cast|rhythm|outline|write` now:
|
|
5
|
+
* 1. open the story graph, check upstream, build a compact context
|
|
6
|
+
* 2. close the DB (embedded lock)
|
|
7
|
+
* 3. send a prompt telling the LLM to use the storydb CLI
|
|
8
|
+
* 4. wait for the turn, run stage-check, repair with exact DB errors
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
import { notify, llmFollowup } from '#infra';
|
|
14
|
+
import { openStoryGraph, ensureRootStory } from './client.js';
|
|
15
|
+
import { stageCheck } from './stage.js';
|
|
16
|
+
import { buildStagePrompt, buildRepairPrompt } from './prompts.js';
|
|
17
|
+
import { runStageLoop, waitForTurn } from './driver.js';
|
|
18
|
+
import { readWikiReferences } from '../wiki-references.js';
|
|
19
|
+
import { resolveWorkspace } from '../../../lib/workspace.js';
|
|
20
|
+
|
|
21
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
export const STORYDB_CLI = path.join(__dirname, 'cli.js');
|
|
23
|
+
|
|
24
|
+
const UPSTREAM = {
|
|
25
|
+
scale: null,
|
|
26
|
+
macro: 'scale',
|
|
27
|
+
cast: 'macro',
|
|
28
|
+
rhythm: 'cast',
|
|
29
|
+
outline: 'rhythm',
|
|
30
|
+
writing: 'outline',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
async function withDb(projectRoot, fn) {
|
|
34
|
+
const sdb = await openStoryGraph(projectRoot);
|
|
35
|
+
try {
|
|
36
|
+
return await fn(sdb);
|
|
37
|
+
} finally {
|
|
38
|
+
await sdb.close();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function asArray(value) {
|
|
43
|
+
return Array.isArray(value) ? value : [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function readScale(sdb) {
|
|
47
|
+
const scale = await sdb.one('SELECT * FROM scale:main;');
|
|
48
|
+
const volumes = await sdb.rows(`SELECT * FROM volume WHERE story = story:main ORDER BY chapter_start;`);
|
|
49
|
+
const acts = await sdb.rows(`SELECT * FROM act WHERE story = story:main ORDER BY chapter_start;`);
|
|
50
|
+
return {
|
|
51
|
+
capacity: scale && scale.capacity || null,
|
|
52
|
+
quotas: scale && scale.quotas || null,
|
|
53
|
+
volumes: volumes.map((v) => ({
|
|
54
|
+
volume_id: v.volume_id, title: v.title, chapter_start: v.chapter_start,
|
|
55
|
+
chapter_end: v.chapter_end, goal: v.goal,
|
|
56
|
+
})),
|
|
57
|
+
acts: acts.map((a) => ({
|
|
58
|
+
act_id: a.act_id, volume_id: a.volume_id, chapter_start: a.chapter_start,
|
|
59
|
+
chapter_end: a.chapter_end, goal: a.goal,
|
|
60
|
+
})),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function readMacro(sdb) {
|
|
65
|
+
const world = await sdb.one(`SELECT * FROM world_doc WHERE story = story:main LIMIT 1;`);
|
|
66
|
+
const story = await sdb.one(`SELECT * FROM story_doc WHERE story = story:main LIMIT 1;`);
|
|
67
|
+
const lines = await sdb.rows(`SELECT * FROM line WHERE story = story:main ORDER BY kind, line_id;`);
|
|
68
|
+
const nodes = await sdb.rows(`SELECT * FROM line_node WHERE story = story:main ORDER BY node_id;`);
|
|
69
|
+
const endings = await sdb.rows(`SELECT * FROM ending WHERE story = story:main ORDER BY ending_id;`);
|
|
70
|
+
const roles = await sdb.rows(`SELECT * FROM role_slot WHERE story = story:main ORDER BY role_id;`);
|
|
71
|
+
return {
|
|
72
|
+
world: world && (world.payload || world) || null,
|
|
73
|
+
story: story && (story.payload || story) || null,
|
|
74
|
+
lines: lines.map((l) => ({ line_id: l.line_id, kind: l.kind, name: l.name })),
|
|
75
|
+
nodes: nodes.map((n) => ({ node_id: n.node_id, line_id: n.line_id, volume: n.volume, act: n.act, goal: n.goal })),
|
|
76
|
+
endings: endings.map((e) => ({ ending_id: e.ending_id, level: e.level, type: e.type })),
|
|
77
|
+
roles: roles.map((r) => ({ role_id: r.role_id, name: r.name, slot_function: r.slot_function })),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function readCast(sdb) {
|
|
82
|
+
const rows = await sdb.rows(
|
|
83
|
+
`SELECT entity_id, name, type, status FROM character WHERE story = story:main ORDER BY entity_id;`,
|
|
84
|
+
);
|
|
85
|
+
const factions = await sdb.rows(
|
|
86
|
+
`SELECT entity_id, name, type FROM faction WHERE story = story:main ORDER BY entity_id;`,
|
|
87
|
+
);
|
|
88
|
+
const locations = await sdb.rows(
|
|
89
|
+
`SELECT entity_id, name, type FROM location WHERE story = story:main ORDER BY entity_id;`,
|
|
90
|
+
);
|
|
91
|
+
const foreshadows = await sdb.rows(
|
|
92
|
+
`SELECT entity_id, name, type, status FROM foreshadow WHERE story = story:main ORDER BY entity_id;`,
|
|
93
|
+
);
|
|
94
|
+
const payoffs = await sdb.rows(
|
|
95
|
+
`SELECT entity_id, name, scale, status FROM payoff WHERE story = story:main ORDER BY entity_id;`,
|
|
96
|
+
);
|
|
97
|
+
const conflicts = await sdb.rows(
|
|
98
|
+
`SELECT entity_id, name, type FROM conflict WHERE story = story:main ORDER BY entity_id;`,
|
|
99
|
+
);
|
|
100
|
+
const info = await sdb.rows(
|
|
101
|
+
`SELECT entity_id, info_name FROM information WHERE story = story:main ORDER BY entity_id;`,
|
|
102
|
+
);
|
|
103
|
+
return {
|
|
104
|
+
counts: {
|
|
105
|
+
characters: rows.length,
|
|
106
|
+
factions: factions.length,
|
|
107
|
+
locations: locations.length,
|
|
108
|
+
foreshadows: foreshadows.length,
|
|
109
|
+
payoffs: payoffs.length,
|
|
110
|
+
conflicts: conflicts.length,
|
|
111
|
+
information: info.length,
|
|
112
|
+
},
|
|
113
|
+
characters: rows.map((r) => ({ id: r.entity_id, name: r.name, type: r.type })),
|
|
114
|
+
factions: factions.map((r) => ({ id: r.entity_id, name: r.name })),
|
|
115
|
+
locations: locations.map((r) => ({ id: r.entity_id, name: r.name, type: r.type })),
|
|
116
|
+
foreshadows: foreshadows.map((r) => ({ id: r.entity_id, name: r.name, type: r.type })),
|
|
117
|
+
payoffs: payoffs.map((r) => ({ id: r.entity_id, name: r.name, scale: r.scale })),
|
|
118
|
+
conflicts: conflicts.map((r) => ({ id: r.entity_id, name: r.name, type: r.type })),
|
|
119
|
+
information: info.map((r) => ({ id: r.entity_id, name: r.info_name })),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function readChapterSummary(sdb) {
|
|
124
|
+
const story = await sdb.one('SELECT * FROM story:main;');
|
|
125
|
+
const chapters = await sdb.rows(
|
|
126
|
+
`SELECT chapter_id, display_no, title, volume, act, status FROM chapter WHERE story = story:main ORDER BY display_no;`,
|
|
127
|
+
);
|
|
128
|
+
const outlines = await sdb.rows(`SELECT chapter_id FROM outline WHERE story = story:main;`);
|
|
129
|
+
const texts = await sdb.rows(`SELECT chapter_id, word_count FROM chapter_text WHERE story = story:main;`);
|
|
130
|
+
const compact = (c) => ({
|
|
131
|
+
chapter_id: c.chapter_id, display_no: c.display_no, title: c.title,
|
|
132
|
+
volume: c.volume, act: c.act, status: c.status,
|
|
133
|
+
});
|
|
134
|
+
return {
|
|
135
|
+
target_chapters: story && story.target_chapters || 0,
|
|
136
|
+
chapter_count: chapters.length,
|
|
137
|
+
outline_count: outlines.length,
|
|
138
|
+
text_count: texts.length,
|
|
139
|
+
missing_outlines: chapters.filter((c) => !outlines.some((o) => o.chapter_id === c.chapter_id)).length,
|
|
140
|
+
missing_texts: chapters.filter((c) => !texts.some((t) => t.chapter_id === c.chapter_id)).length,
|
|
141
|
+
first_chapter: chapters.length ? compact(chapters[0]) : null,
|
|
142
|
+
last_chapter: chapters.length ? compact(chapters[chapters.length - 1]) : null,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function buildContext(stage, projectRoot, topicData, sdb, selection = null) {
|
|
147
|
+
let workspaceRoot = projectRoot;
|
|
148
|
+
try { workspaceRoot = resolveWorkspace({ cwd: projectRoot, required: true }); } catch { /* legacy root */ }
|
|
149
|
+
const base = {
|
|
150
|
+
stage,
|
|
151
|
+
projectRoot,
|
|
152
|
+
workspaceRoot,
|
|
153
|
+
wikiReferences: readWikiReferences(workspaceRoot, stage === 'outline' ? 'rhythm' : stage),
|
|
154
|
+
cliPath: STORYDB_CLI,
|
|
155
|
+
topic: topicData || {},
|
|
156
|
+
selection: selection || null,
|
|
157
|
+
};
|
|
158
|
+
if (stage === 'scale') return base;
|
|
159
|
+
const scale = await readScale(sdb);
|
|
160
|
+
if (stage === 'macro') return { ...base, scale };
|
|
161
|
+
const macro = await readMacro(sdb);
|
|
162
|
+
if (stage === 'cast') return { ...base, quotas: scale.quotas, macro, scale };
|
|
163
|
+
if (stage === 'rhythm') {
|
|
164
|
+
const cast = await readCast(sdb);
|
|
165
|
+
const macro = await readMacro(sdb);
|
|
166
|
+
return { ...base, scale, cast, macro };
|
|
167
|
+
}
|
|
168
|
+
const cast = await readCast(sdb);
|
|
169
|
+
const summary = await readChapterSummary(sdb);
|
|
170
|
+
return { ...base, scale, cast: { counts: cast.counts }, summary };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function parseChapterSelection(rawInput, chapters, stage) {
|
|
174
|
+
const text = String(rawInput || '').trim();
|
|
175
|
+
const tokens = text.split(/\s+/).filter(Boolean);
|
|
176
|
+
if (tokens.length && [stage, stage === 'writing' ? 'write' : '', 'build'].includes(tokens[0].toLowerCase())) tokens.shift();
|
|
177
|
+
if (!tokens.length || tokens.some((token) => token.toLowerCase() === 'all')) {
|
|
178
|
+
return { ok: true, all: true, ids: null, displays: [], label: '全部章节' };
|
|
179
|
+
}
|
|
180
|
+
const byDisplay = new Map(chapters.map((row) => [Number(row.display_no), row]));
|
|
181
|
+
const byId = new Map(chapters.map((row) => [String(row.chapter_id).toUpperCase(), row]));
|
|
182
|
+
const ids = new Set();
|
|
183
|
+
const displays = [];
|
|
184
|
+
const add = (row) => {
|
|
185
|
+
if (row && !ids.has(row.chapter_id)) {
|
|
186
|
+
ids.add(row.chapter_id);
|
|
187
|
+
displays.push(Number(row.display_no));
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
for (const token of tokens) {
|
|
191
|
+
const range = /^(?:CH)?(\d+)(?:\s*[-~]\s*(?:CH)?(\d+))?$/i.exec(token);
|
|
192
|
+
if (range) {
|
|
193
|
+
const start = Math.min(Number(range[1]), Number(range[2] || range[1]));
|
|
194
|
+
const end = Math.max(Number(range[1]), Number(range[2] || range[1]));
|
|
195
|
+
for (let no = start; no <= end; no += 1) {
|
|
196
|
+
if (!byDisplay.has(no)) return { ok: false, error: '章号不存在:第 ' + no + ' 章' };
|
|
197
|
+
add(byDisplay.get(no));
|
|
198
|
+
}
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (/^CH[A-Za-z0-9_-]+$/i.test(token)) {
|
|
202
|
+
const row = byId.get(token.toUpperCase());
|
|
203
|
+
if (!row) return { ok: false, error: '章节不存在:' + token };
|
|
204
|
+
add(row);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
return { ok: false, error: '无法识别的章节范围:' + token };
|
|
208
|
+
}
|
|
209
|
+
displays.sort((a, b) => a - b);
|
|
210
|
+
return { ok: true, all: false, ids, displays, label: displays.map((no) => 'CH' + String(no).padStart(3, '0')).join('、') };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function executablePrompt(stage, context) {
|
|
214
|
+
return buildStagePrompt(stage, context)
|
|
215
|
+
.replaceAll('<cli>', context.cliPath)
|
|
216
|
+
.replaceAll('<root>', context.projectRoot);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const STAGE_LABELS = {
|
|
220
|
+
scale: '规模',
|
|
221
|
+
macro: '宏观',
|
|
222
|
+
cast: '实体',
|
|
223
|
+
rhythm: '章节地图',
|
|
224
|
+
outline: '细纲',
|
|
225
|
+
writing: '正文',
|
|
226
|
+
};
|
|
227
|
+
const STAGE_COMMANDS = { scale: 'scale', macro: 'macro', cast: 'cast', rhythm: 'rhythm', outline: 'outline', writing: 'write' };
|
|
228
|
+
const COUNT_LABELS = {
|
|
229
|
+
volumes: '卷', acts: '幕', covered: '覆盖章节', target: '目标章节',
|
|
230
|
+
world: '世界观', story: '故事', lines: '线索', nodes: '主线节点', endings: '结局', roles: '角色功能位',
|
|
231
|
+
characters: '人物', factions: '势力', faction_relations: '势力关系', locations: '地点',
|
|
232
|
+
items: '道具', conflicts: '冲突', foreshadows: '伏笔', climaxes: '高潮', payoffs: '爽点',
|
|
233
|
+
information: '信息差', chapters: '章节', missing_chapters: '缺失章节', casts: '出场关系',
|
|
234
|
+
chapter_locations: '章节地点', chapter_line_nodes: '主线节点落点', chapter_branches: '支线落点',
|
|
235
|
+
chapter_conflicts: '冲突落点', chapter_climaxes: '高潮落点', chapter_items: '道具落点', ending_actions: '结局动作',
|
|
236
|
+
foreshadow_actions: '伏笔动作', payoff_actions: '爽点动作', info_actions: '信息差动作',
|
|
237
|
+
knowledge_events: '知识事件', outlines: '细纲', missing_outlines: '缺失细纲', scenes: '场景',
|
|
238
|
+
outline_casts: '细纲人物', outline_locations: '细纲地点', outline_foreshadows: '细纲伏笔',
|
|
239
|
+
texts: '正文', missing_texts: '缺失正文', words: '总字数', handoffs: '章末交接',
|
|
240
|
+
};
|
|
241
|
+
const SUBTYPE_LABELS = {
|
|
242
|
+
protagonist: '主角', core_supporting: '核心配角', volume_supporting: '卷级配角',
|
|
243
|
+
core_antagonist: '核心反派', volume_antagonist: '卷级反派',
|
|
244
|
+
small: '小', medium: '中', large: '大',
|
|
245
|
+
core: '核心', phase: '阶段', branch: '支线', symbolic: '象征',
|
|
246
|
+
};
|
|
247
|
+
const COUNT_ORDER = [
|
|
248
|
+
'volumes', 'acts', 'covered', 'target', 'world', 'story', 'lines', 'nodes', 'endings', 'roles',
|
|
249
|
+
'characters', 'factions', 'faction_relations', 'locations', 'items', 'conflicts', 'foreshadows',
|
|
250
|
+
'climaxes', 'payoffs', 'information', 'chapters', 'missing_chapters', 'casts',
|
|
251
|
+
'chapter_locations', 'chapter_line_nodes', 'chapter_branches', 'chapter_conflicts', 'chapter_climaxes', 'chapter_items', 'ending_actions',
|
|
252
|
+
'foreshadow_actions', 'payoff_actions', 'info_actions', 'knowledge_events',
|
|
253
|
+
'outlines', 'missing_outlines', 'scenes', 'outline_casts', 'outline_locations', 'outline_foreshadows', 'texts', 'missing_texts', 'words', 'handoffs',
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
function stageLabel(stage) {
|
|
257
|
+
return STAGE_LABELS[stage] || stage;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function stageCommand(stage) {
|
|
261
|
+
return STAGE_COMMANDS[stage] || stage;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function countValue(key, value) {
|
|
265
|
+
if (typeof value === 'number') {
|
|
266
|
+
if (key === 'words') return (COUNT_LABELS[key] || key) + ':' + value.toLocaleString('zh-CN');
|
|
267
|
+
return (COUNT_LABELS[key] || key) + ':' + value;
|
|
268
|
+
}
|
|
269
|
+
if (value && typeof value === 'object') {
|
|
270
|
+
const parts = Object.entries(value)
|
|
271
|
+
.filter(([, n]) => typeof n === 'number' && n > 0)
|
|
272
|
+
.map(([name, n]) => (SUBTYPE_LABELS[name] || name) + ' ' + n);
|
|
273
|
+
if (!parts.length) return null;
|
|
274
|
+
return (COUNT_LABELS[key] || key) + ':' + parts.join(' / ');
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function summaryLine(stage, check) {
|
|
280
|
+
const counts = check && check.counts || {};
|
|
281
|
+
const keys = [...COUNT_ORDER, ...Object.keys(counts)].filter((key, index, all) => all.indexOf(key) === index);
|
|
282
|
+
const parts = [];
|
|
283
|
+
for (const key of keys) {
|
|
284
|
+
if (!(key in counts)) continue;
|
|
285
|
+
const text = countValue(key, counts[key]);
|
|
286
|
+
if (text) parts.push(text);
|
|
287
|
+
}
|
|
288
|
+
return parts.join(' · ');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function nextStageHint(stage) {
|
|
292
|
+
const next = stage === 'scale' ? 'macro'
|
|
293
|
+
: stage === 'macro' ? 'cast'
|
|
294
|
+
: stage === 'cast' ? 'rhythm'
|
|
295
|
+
: stage === 'rhythm' ? 'outline all'
|
|
296
|
+
: stage === 'outline' ? 'write all'
|
|
297
|
+
: 'deslop';
|
|
298
|
+
return '/fancy-build ' + next;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export async function runStoryGraphCommand(opts) {
|
|
302
|
+
const { session, invocation, projectRoot, stage, topicData, rawInput } = opts || {};
|
|
303
|
+
try {
|
|
304
|
+
const context = await withDb(projectRoot, async (sdb) => {
|
|
305
|
+
await ensureRootStory(sdb, {
|
|
306
|
+
title: topicData && (topicData.book_name || topicData.title),
|
|
307
|
+
genre: topicData && topicData.genre,
|
|
308
|
+
platform: topicData && topicData.platform,
|
|
309
|
+
target_chapters: topicData && topicData.target_chapters,
|
|
310
|
+
target_words: topicData && topicData.target_words,
|
|
311
|
+
});
|
|
312
|
+
const upstream = UPSTREAM[stage];
|
|
313
|
+
if (upstream) {
|
|
314
|
+
const report = await stageCheck(sdb, upstream);
|
|
315
|
+
if (!report.ok) {
|
|
316
|
+
return { upstreamError: { stage: upstream, report } };
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const chapters = await sdb.rows(`SELECT chapter_id, display_no FROM chapter WHERE story = story:main ORDER BY display_no;`);
|
|
320
|
+
const selection = parseChapterSelection(rawInput || '', chapters, stage);
|
|
321
|
+
if (!selection.ok) return { selectionError: selection.error };
|
|
322
|
+
return buildContext(stage, projectRoot, topicData, sdb, selection);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
if (context.selectionError) {
|
|
326
|
+
return notify(session, 'error', '❌ ' + stageLabel(stage) + '阶段范围无效:' + context.selectionError);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (context.upstreamError) {
|
|
330
|
+
const { stage: up, report } = context.upstreamError;
|
|
331
|
+
return notify(session, 'error', [
|
|
332
|
+
'❌ ' + stageLabel(stage) + '的上游阶段「' + stageLabel(up) + '」尚未完成。',
|
|
333
|
+
'请先运行:`/fancy-build ' + stageCommand(up) + '`',
|
|
334
|
+
report.errors.length ? '\n问题:\n- ' + report.errors.slice(0, 8).map((e) => e.message).join('\n- ') : '',
|
|
335
|
+
].join('\n'));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const result = await runStageLoop({
|
|
339
|
+
stage,
|
|
340
|
+
context,
|
|
341
|
+
sendPrompt: async (prompt) => llmFollowup(invocation, prompt),
|
|
342
|
+
waitTurn: () => waitForTurn(invocation, {
|
|
343
|
+
timeoutMs: 90 * 60 * 1000,
|
|
344
|
+
idleGraceMs: 60 * 1000,
|
|
345
|
+
signal: invocation.signal,
|
|
346
|
+
}),
|
|
347
|
+
checkStage: () => withDb(projectRoot, (sdb) => stageCheck(sdb, stage, {
|
|
348
|
+
chapterIds: context.selection && context.selection.all === false ? context.selection.ids : null,
|
|
349
|
+
})),
|
|
350
|
+
buildPrompt: (ctx) => executablePrompt(stage, ctx),
|
|
351
|
+
buildRepair: (s, check) => buildRepairPrompt(s, check, {
|
|
352
|
+
cliPath: context.cliPath,
|
|
353
|
+
projectRoot: context.projectRoot,
|
|
354
|
+
}),
|
|
355
|
+
maxRounds: 3,
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
if (result.ok) {
|
|
359
|
+
const check = result.check || {};
|
|
360
|
+
const summary = summaryLine(stage, check);
|
|
361
|
+
return notify(session, 'success', [
|
|
362
|
+
'✅ ' + stageLabel(stage) + '阶段已完成。',
|
|
363
|
+
summary ? '完成情况:' + summary : '',
|
|
364
|
+
check.warnings && check.warnings.length ? '警告:' + check.warnings.length + ' 条(不影响通过)' : '',
|
|
365
|
+
'下一步:`' + nextStageHint(stage) + '`',
|
|
366
|
+
].filter(Boolean).join('\n'));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const check = result.check || {};
|
|
370
|
+
const errors = (check.errors || []).slice(0, 10).map((e) => '- ' + (e.message || e.code));
|
|
371
|
+
return notify(session, 'error', [
|
|
372
|
+
'❌ ' + stageLabel(stage) + '阶段未完成:' + (result.error || '完成度校验未通过'),
|
|
373
|
+
errors.length ? '\n' + errors.join('\n') : '',
|
|
374
|
+
'\n可重新运行 `/fancy-build ' + stageCommand(stage) + '`;插件将再次执行完成度校验。',
|
|
375
|
+
].filter(Boolean).join('\n'));
|
|
376
|
+
} catch (err) {
|
|
377
|
+
return notify(session, 'error', '❌ ' + stageLabel(stage) + '阶段执行失败:' + (err && err.message ? err.message : String(err)));
|
|
378
|
+
}
|
|
379
|
+
}
|