dsh-novel-write-engine 0.2.0 → 0.2.2
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/CHANGELOG.md +55 -1
- package/README.md +145 -75
- package/briefs/P6.md +1 -0
- package/briefs/P8.md +2 -0
- package/lib/client.js +304 -31
- package/lib/index.d.ts +471 -24
- package/lib/index.js +2730 -314
- package/lib/types/client/dto.d.ts +35 -3
- package/lib/types/client/panel.d.ts +1 -1
- package/lib/types/client/viewmodel.d.ts +36 -2
- package/lib/types/constants.d.ts +15 -2
- package/lib/types/types.d.ts +140 -3
- package/package.json +1 -1
- package/presets/novel-write-engine.patch.yml +1 -1
- package/skills/entry.md +12 -4
|
@@ -13,6 +13,8 @@ export interface BlockingDto {
|
|
|
13
13
|
export interface SummaryDto {
|
|
14
14
|
schemaVersion: number;
|
|
15
15
|
panelId: string;
|
|
16
|
+
/** Absolute project root, so a stale title cannot hide a wrong book. */
|
|
17
|
+
root: string;
|
|
16
18
|
title: string;
|
|
17
19
|
genre: string;
|
|
18
20
|
platform: string;
|
|
@@ -45,7 +47,12 @@ export interface SummaryDto {
|
|
|
45
47
|
total: number;
|
|
46
48
|
planned: number;
|
|
47
49
|
archived: number;
|
|
50
|
+
target?: number | null;
|
|
51
|
+
missing_cards?: number;
|
|
52
|
+
planned_coverage?: number | null;
|
|
53
|
+
archived_coverage?: number | null;
|
|
48
54
|
coverage: number;
|
|
55
|
+
card_coverage?: number;
|
|
49
56
|
volumes: Array<{
|
|
50
57
|
title: string;
|
|
51
58
|
start: number;
|
|
@@ -62,6 +69,7 @@ export interface SummaryDto {
|
|
|
62
69
|
rules: number;
|
|
63
70
|
glossary: number;
|
|
64
71
|
backstory: number;
|
|
72
|
+
facts?: number;
|
|
65
73
|
};
|
|
66
74
|
};
|
|
67
75
|
quality?: {
|
|
@@ -102,11 +110,35 @@ export interface GatesDto {
|
|
|
102
110
|
blocking: BlockingDto[];
|
|
103
111
|
explanations: string[];
|
|
104
112
|
}
|
|
113
|
+
export interface ProjectOptionDto {
|
|
114
|
+
root: string;
|
|
115
|
+
title: string;
|
|
116
|
+
active: boolean;
|
|
117
|
+
registered: boolean;
|
|
118
|
+
state: 'ok' | 'damaged' | 'missing' | 'unreadable';
|
|
119
|
+
}
|
|
120
|
+
export interface PanelSelectionDto {
|
|
121
|
+
root: string | null;
|
|
122
|
+
source: 'query' | 'active' | 'single' | 'config' | 'none';
|
|
123
|
+
}
|
|
124
|
+
export interface PanelMissingDto {
|
|
125
|
+
root: string;
|
|
126
|
+
title: string;
|
|
127
|
+
code: 'ENGINE_PROJECT_MISSING' | 'ENGINE_PROJECT_NOT_FOUND';
|
|
128
|
+
reason: 'missing' | 'not-project' | 'damaged' | 'not_registered' | 'unreadable';
|
|
129
|
+
hint: string;
|
|
130
|
+
}
|
|
105
131
|
export interface PanelPayloadDto {
|
|
106
132
|
schemaVersion: number;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
133
|
+
projects: ProjectOptionDto[];
|
|
134
|
+
selection: PanelSelectionDto;
|
|
135
|
+
summary?: SummaryDto;
|
|
136
|
+
chapters?: ChaptersDto;
|
|
137
|
+
gates?: GatesDto;
|
|
138
|
+
resume?: {
|
|
139
|
+
text: string;
|
|
140
|
+
};
|
|
141
|
+
missing?: PanelMissingDto;
|
|
110
142
|
error?: string;
|
|
111
143
|
empty?: true;
|
|
112
144
|
}
|
|
@@ -4,7 +4,7 @@ import { buildView, type PanelView } from './viewmodel.js';
|
|
|
4
4
|
* Pure view: given a view model, return the element tree. Keeping the hooks in
|
|
5
5
|
* `NovelPanel` makes every visual state renderable without a browser.
|
|
6
6
|
*/
|
|
7
|
-
export declare function panelElement(view: PanelView, loading: boolean, onRefresh: () => void): ReactElement;
|
|
7
|
+
export declare function panelElement(view: PanelView, loading: boolean, onRefresh: () => void, onSelectProject?: (root: string | null) => void): ReactElement;
|
|
8
8
|
/** Stateful wrapper: fetch once, refresh on demand and after returning to the tab. */
|
|
9
9
|
export declare function NovelPanel(): ReactElement;
|
|
10
10
|
/** Sidebar icon: a small book mark, inheriting the current text colour. */
|
|
@@ -20,11 +20,40 @@ export interface PanelChapter {
|
|
|
20
20
|
details: string;
|
|
21
21
|
stageLabel: string;
|
|
22
22
|
}
|
|
23
|
+
export interface PanelProject {
|
|
24
|
+
root: string;
|
|
25
|
+
title: string;
|
|
26
|
+
label: string;
|
|
27
|
+
active: boolean;
|
|
28
|
+
registered: boolean;
|
|
29
|
+
state: 'ok' | 'damaged' | 'missing' | 'unreadable';
|
|
30
|
+
selected: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface PanelMissing {
|
|
33
|
+
root: string;
|
|
34
|
+
title: string;
|
|
35
|
+
code: string;
|
|
36
|
+
reason: string;
|
|
37
|
+
hint: string;
|
|
38
|
+
}
|
|
23
39
|
/** Everything the panel renders, derived from the wire payload alone. */
|
|
24
40
|
export interface PanelView {
|
|
25
|
-
state: 'ready' | 'loading' | 'empty' | 'error' | 'outdated';
|
|
41
|
+
state: 'ready' | 'loading' | 'empty' | 'error' | 'outdated' | 'choose' | 'missing';
|
|
26
42
|
title: string;
|
|
27
43
|
subtitle: string;
|
|
44
|
+
/** Absolute root of the selected project, when there is one. */
|
|
45
|
+
root: string;
|
|
46
|
+
projects: PanelProject[];
|
|
47
|
+
selectedRoot: string | null;
|
|
48
|
+
selection: {
|
|
49
|
+
root: string | null;
|
|
50
|
+
source: string;
|
|
51
|
+
};
|
|
52
|
+
missing?: PanelMissing;
|
|
53
|
+
resumeText: string;
|
|
54
|
+
/** True when the selected project exists but its definition is damaged. */
|
|
55
|
+
damaged: boolean;
|
|
56
|
+
volumeNumber?: number;
|
|
28
57
|
stageLabel: string;
|
|
29
58
|
/** The whole book reached its configured target; stage `done` alone is only one chapter. */
|
|
30
59
|
finished: boolean;
|
|
@@ -53,6 +82,10 @@ export interface PanelView {
|
|
|
53
82
|
outlineTotal: number;
|
|
54
83
|
archivedCards: number;
|
|
55
84
|
coverage: number;
|
|
85
|
+
missingCards: number;
|
|
86
|
+
plannedCoverage: number | null;
|
|
87
|
+
archivedCoverage: number | null;
|
|
88
|
+
cardCoverage: number;
|
|
56
89
|
volumes: Array<{
|
|
57
90
|
title: string;
|
|
58
91
|
range: string;
|
|
@@ -66,6 +99,7 @@ export interface PanelView {
|
|
|
66
99
|
rules: number;
|
|
67
100
|
glossary: number;
|
|
68
101
|
backstory: number;
|
|
102
|
+
facts?: number;
|
|
69
103
|
};
|
|
70
104
|
};
|
|
71
105
|
quality: {
|
|
@@ -92,7 +126,7 @@ export interface PanelView {
|
|
|
92
126
|
revision: string;
|
|
93
127
|
next: string;
|
|
94
128
|
}
|
|
95
|
-
export declare const PANEL_SCHEMA_VERSION =
|
|
129
|
+
export declare const PANEL_SCHEMA_VERSION = 4;
|
|
96
130
|
/**
|
|
97
131
|
* Project the wire payload into strings. Version mismatch wins over everything
|
|
98
132
|
* else: a stale panel must never render data from another schema as if it were
|
package/lib/types/constants.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export declare const PLUGIN_NAME = "dsh-novel-write-engine";
|
|
|
4
4
|
export declare const PANEL_ID = "novel-workshop";
|
|
5
5
|
export declare const PANEL_ROUTE = "/api/novel-write-engine/panel";
|
|
6
6
|
export declare const SKILL_NAME = "novel-write-engine";
|
|
7
|
-
export declare const REMOTE_VERSION =
|
|
7
|
+
export declare const REMOTE_VERSION = 4;
|
|
8
8
|
/** The panel is a recent-history view; the wire payload and UI use one cap. */
|
|
9
9
|
export declare const PANEL_CHAPTER_LIMIT = 30;
|
|
10
10
|
/** Project-relative locations. Every write goes through these constants. */
|
|
@@ -18,10 +18,16 @@ export declare const REVIEW_DIR = "reviews";
|
|
|
18
18
|
export declare const CHECK_DIR = "checks";
|
|
19
19
|
export declare const EXPORT_DIR = "exports";
|
|
20
20
|
export declare const BACKUP_DIR = "backups";
|
|
21
|
+
export declare const HISTORY_DIR = "history";
|
|
22
|
+
/** Brief receipts are append-only evidence, separate from content transactions. */
|
|
23
|
+
export declare const BRIEF_RECEIPTS_FILE = "state/briefs.yaml";
|
|
21
24
|
export declare const OUTLINE_DIR = "outline";
|
|
22
25
|
export declare const LOCK_FILE = ".engine.lock";
|
|
23
26
|
export declare const STAGING_DIR = ".staging";
|
|
24
27
|
export declare const REGISTRY_FILE = "projects.yaml";
|
|
28
|
+
export declare const SESSION_BINDINGS_FILE = "session-bindings.yaml";
|
|
29
|
+
export declare const METADATA_LOCK_FILE = ".metadata.lock";
|
|
30
|
+
export declare const ARCHIVE_DIGESTS_FILE = "state/archive-digests.json";
|
|
25
31
|
/** Files owned by the engine under `state/`. */
|
|
26
32
|
export declare const STATE_FILES: {
|
|
27
33
|
readonly pipeline: "pipeline.yaml";
|
|
@@ -43,6 +49,13 @@ export declare const planWorldFile: (chapter: number) => string;
|
|
|
43
49
|
export declare const planChapterFile: (chapter: number) => string;
|
|
44
50
|
export declare const reviewFile: (chapter: number) => string;
|
|
45
51
|
export declare const checkFile: (chapter: number) => string;
|
|
52
|
+
export declare const reviewHistoryFile: (chapter: number, revision: number) => string;
|
|
53
|
+
export declare const checkHistoryFile: (chapter: number, revision: number) => string;
|
|
54
|
+
export declare const chapterHistoryFile: (chapter: number, revision: number) => string;
|
|
55
|
+
export declare const deprecatedHistoryFile: (chapter: number, revision: number) => string;
|
|
56
|
+
export declare const planHistoryFile: (chapter: number, revision: number) => string;
|
|
57
|
+
export declare const HISTORY_KINDS: readonly ["reviews", "checks", "chapters", "deprecated", "plans"];
|
|
58
|
+
export type HistoryKind = (typeof HISTORY_KINDS)[number];
|
|
46
59
|
/** Limits that are structural, not taste. */
|
|
47
60
|
export declare const LIMITS: {
|
|
48
61
|
/** P4 <-> P5/P6 retries before a forced rollback. */
|
|
@@ -76,7 +89,7 @@ export declare const INITIAL_STAGE: Stage;
|
|
|
76
89
|
/** The order used to render the nine-phase progress strip. */
|
|
77
90
|
export declare const PHASE_STRIP: readonly ["P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P9"];
|
|
78
91
|
/** Tool names, kept here so the arch test can assert one namespace. */
|
|
79
|
-
export declare const TOOL_NAMES: readonly ["novel_init", "novel_status", "novel_brief", "novel_plan_world", "novel_plan_chapter", "novel_write_draft", "novel_review", "novel_revise", "novel_check", "novel_archive", "novel_word_count", "novel_state", "novel_amend", "novel_rollback", "novel_diff", "novel_export", "novel_audit", "novel_pipeline", "novel_blueprint", "novel_outline", "novel_outline_import", "novel_worldbook", "novel_search", "novel_doctor", "novel_report", "novel_guide", "novel_reopen"];
|
|
92
|
+
export declare const TOOL_NAMES: readonly ["novel_init", "novel_status", "novel_brief", "novel_plan_world", "novel_plan_chapter", "novel_write_draft", "novel_review", "novel_revise", "novel_check", "novel_archive", "novel_word_count", "novel_state", "novel_amend", "novel_rollback", "novel_diff", "novel_export", "novel_audit", "novel_pipeline", "novel_blueprint", "novel_outline", "novel_outline_import", "novel_worldbook", "novel_search", "novel_doctor", "novel_report", "novel_guide", "novel_reopen", "novel_restore"];
|
|
80
93
|
export type ToolName = (typeof TOOL_NAMES)[number];
|
|
81
94
|
/** The ten high-frequency tools kept when `tools.mode = core`. */
|
|
82
95
|
export declare const CORE_TOOLS: readonly ToolName[];
|
package/lib/types/types.d.ts
CHANGED
|
@@ -20,8 +20,8 @@ export declare const SCENE_TYPES: readonly ["战斗/动作章", "日常过渡章
|
|
|
20
20
|
export type SceneType = (typeof SCENE_TYPES)[number];
|
|
21
21
|
/** A violation is blocking unless the rule pack declares it advisory. */
|
|
22
22
|
export type Severity = 'blocker' | 'advisory';
|
|
23
|
-
/** All thirty-
|
|
24
|
-
export declare const OPERATIONS: readonly ["initProject", "archiveChapter", "amend", "rollback", "reopenChapter", "submitWorldPlan", "submitChapterPlan", "submitDraft", "submitReview", "submitRevision", "runCheck", "submitBookPlan", "submitOutline", "importOutline", "submitWorldbook", "getStatus", "getBrief", "countWords", "queryState", "diff", "auditHistory", "advise", "search", "doctor", "report", "guide", "export", "panelSummary", "panelChapters", "panelGates", "stats"];
|
|
23
|
+
/** All thirty-two service operations (22 inherited + 9 book-level + restoreBackup). */
|
|
24
|
+
export declare const OPERATIONS: readonly ["initProject", "archiveChapter", "amend", "rollback", "reopenChapter", "submitWorldPlan", "submitChapterPlan", "submitDraft", "submitReview", "submitRevision", "runCheck", "submitBookPlan", "submitOutline", "importOutline", "submitWorldbook", "getStatus", "getBrief", "countWords", "queryState", "diff", "auditHistory", "advise", "search", "doctor", "report", "guide", "export", "panelSummary", "panelChapters", "panelGates", "stats", "restoreBackup"];
|
|
25
25
|
export type Operation = (typeof OPERATIONS)[number];
|
|
26
26
|
/** Structured, non-fatal note attached to a successful result. */
|
|
27
27
|
export interface Warning {
|
|
@@ -58,6 +58,9 @@ export interface ReadingProof {
|
|
|
58
58
|
/** Error families map one-to-one to the three repair actions a caller can take. */
|
|
59
59
|
export declare const ERROR_CODES: {
|
|
60
60
|
readonly PROJECT_NOT_FOUND: "ENGINE_PROJECT_NOT_FOUND";
|
|
61
|
+
readonly PROJECT_AMBIGUOUS: "ENGINE_PROJECT_AMBIGUOUS";
|
|
62
|
+
readonly PROJECT_MISSING: "ENGINE_PROJECT_MISSING";
|
|
63
|
+
readonly PROJECT_IDENTITY_MISMATCH: "ENGINE_PROJECT_IDENTITY_MISMATCH";
|
|
61
64
|
readonly PROJECT_EXISTS: "ENGINE_PROJECT_EXISTS";
|
|
62
65
|
readonly STAGE_ORDER: "ENGINE_STAGE_ORDER";
|
|
63
66
|
readonly GATE_BLOCKED: "ENGINE_GATE_BLOCKED";
|
|
@@ -161,6 +164,7 @@ export interface CheckDocument {
|
|
|
161
164
|
content_hash: string;
|
|
162
165
|
machine: {
|
|
163
166
|
violations: Violation[];
|
|
167
|
+
advisories?: Violation[];
|
|
164
168
|
metrics: WordMetrics;
|
|
165
169
|
};
|
|
166
170
|
semantic: {
|
|
@@ -171,6 +175,8 @@ export interface CheckDocument {
|
|
|
171
175
|
note: string;
|
|
172
176
|
}>;
|
|
173
177
|
};
|
|
178
|
+
/** Advisory codes the author has explicitly acknowledged for this content. */
|
|
179
|
+
acknowledged?: string[];
|
|
174
180
|
verdict: 'pass' | 'fail';
|
|
175
181
|
}
|
|
176
182
|
/** Every persisted document whose damage status/panel can report. */
|
|
@@ -189,6 +195,8 @@ export interface WordMetrics {
|
|
|
189
195
|
numbers: number;
|
|
190
196
|
chinese_punct: number;
|
|
191
197
|
english_punct: number;
|
|
198
|
+
/** All punctuation counted by the engine; total = cjk + words + numbers + punctuation. */
|
|
199
|
+
punctuation_chars: number;
|
|
192
200
|
non_ws_chars: number;
|
|
193
201
|
total_lines: number;
|
|
194
202
|
non_empty_lines: number;
|
|
@@ -211,6 +219,10 @@ export interface ChapterRecord {
|
|
|
211
219
|
status: 'draft' | 'reviewed';
|
|
212
220
|
content_hash: string;
|
|
213
221
|
summary: ChapterSummary;
|
|
222
|
+
/** How hard this chapter was before it was archived. */
|
|
223
|
+
rounds?: RoundCounters;
|
|
224
|
+
final_check?: 'pass' | 'fail';
|
|
225
|
+
archived_at?: string;
|
|
214
226
|
/** Keys allocated by the archive transaction; lets a cross-process retry replay. */
|
|
215
227
|
assigned_ids?: Record<string, string>;
|
|
216
228
|
/** Digest of the archive request that produced this row. */
|
|
@@ -225,6 +237,7 @@ export interface ChapterSummary {
|
|
|
225
237
|
key: string;
|
|
226
238
|
title: string;
|
|
227
239
|
description: string;
|
|
240
|
+
anchor?: string;
|
|
228
241
|
}>;
|
|
229
242
|
advanced: Array<{
|
|
230
243
|
key: string;
|
|
@@ -258,6 +271,8 @@ export interface RoundCounters {
|
|
|
258
271
|
review: number;
|
|
259
272
|
revise: number;
|
|
260
273
|
check: number;
|
|
274
|
+
/** Lifetime rollback count on a chapter record; never stored in the live pipeline. */
|
|
275
|
+
rollbacks?: number;
|
|
261
276
|
}
|
|
262
277
|
/** How one ledger entity changed in this chapter. */
|
|
263
278
|
export type DeltaAction = 'created' | 'updated' | 'no_change';
|
|
@@ -289,6 +304,8 @@ export interface ForeshadowDelta {
|
|
|
289
304
|
action: DeltaAction;
|
|
290
305
|
reason?: string;
|
|
291
306
|
title?: string;
|
|
307
|
+
/** Verbatim manuscript quote used for verification; title stays a readable label. */
|
|
308
|
+
anchor?: string;
|
|
292
309
|
description?: string;
|
|
293
310
|
note?: string;
|
|
294
311
|
new_info?: boolean;
|
|
@@ -331,6 +348,28 @@ export interface ArchiveHandoff {
|
|
|
331
348
|
act?: string;
|
|
332
349
|
act_progress?: string;
|
|
333
350
|
}
|
|
351
|
+
/** One issued task-book receipt; the brief itself is rendered on demand. */
|
|
352
|
+
export interface BriefReceipt {
|
|
353
|
+
chapter: number;
|
|
354
|
+
stage: BriefStage;
|
|
355
|
+
digest: string;
|
|
356
|
+
issued_at: string;
|
|
357
|
+
}
|
|
358
|
+
/** `state/briefs.yaml`: append-only task-book evidence outside content transactions. */
|
|
359
|
+
export interface BriefReceiptsDocument {
|
|
360
|
+
schema_version: 1;
|
|
361
|
+
receipts: BriefReceipt[];
|
|
362
|
+
}
|
|
363
|
+
/** The active draft's own metadata; P2 plan stays immutable after submission. */
|
|
364
|
+
export interface DraftState {
|
|
365
|
+
chapter: number;
|
|
366
|
+
title: string;
|
|
367
|
+
scene_type: SceneType;
|
|
368
|
+
narrator: string;
|
|
369
|
+
summary: ChapterSummary;
|
|
370
|
+
content_hash: string;
|
|
371
|
+
updated_at: string;
|
|
372
|
+
}
|
|
334
373
|
/** Pipeline document (`state/pipeline.yaml`). */
|
|
335
374
|
export interface PipelineState {
|
|
336
375
|
schema_version: 1;
|
|
@@ -343,6 +382,8 @@ export interface PipelineState {
|
|
|
343
382
|
digest: string;
|
|
344
383
|
}>;
|
|
345
384
|
blocking: BlockingItem[];
|
|
385
|
+
/** Current draft summary/title; never written into the P2 plan file. */
|
|
386
|
+
draft?: DraftState;
|
|
346
387
|
/** Hash of the review document last written by submitReview/submitRevision. */
|
|
347
388
|
review_digest?: string;
|
|
348
389
|
last_action: {
|
|
@@ -422,6 +463,7 @@ export interface ForeshadowsDocument {
|
|
|
422
463
|
status: 'planted' | 'dormant' | 'advanced' | 'resolved';
|
|
423
464
|
resolved_at: number | null;
|
|
424
465
|
description: string;
|
|
466
|
+
anchor?: string;
|
|
425
467
|
advance_log: Array<{
|
|
426
468
|
chapter: number;
|
|
427
469
|
note: string;
|
|
@@ -614,6 +656,14 @@ export interface WorldbookBackstory {
|
|
|
614
656
|
time: string;
|
|
615
657
|
event: string;
|
|
616
658
|
}
|
|
659
|
+
/** One declared numeric canon, checked against the manuscript on request. */
|
|
660
|
+
export interface WorldbookFact {
|
|
661
|
+
alias: string;
|
|
662
|
+
value: number;
|
|
663
|
+
unit: string;
|
|
664
|
+
tolerance?: number;
|
|
665
|
+
note?: string;
|
|
666
|
+
}
|
|
617
667
|
/** Worldbuilding plan, persisted in `outline/worldbook.yaml`. */
|
|
618
668
|
export interface WorldbookDocument {
|
|
619
669
|
schema_version: 1;
|
|
@@ -626,6 +676,8 @@ export interface WorldbookDocument {
|
|
|
626
676
|
characters: WorldbookCharacter[];
|
|
627
677
|
glossary: WorldbookGlossary[];
|
|
628
678
|
backstory: WorldbookBackstory[];
|
|
679
|
+
/** Optional numeric canon; old documents omit it. */
|
|
680
|
+
facts?: WorldbookFact[];
|
|
629
681
|
themes: string[];
|
|
630
682
|
}
|
|
631
683
|
/** One hit from `novel_search`; the scope names the source family. */
|
|
@@ -663,6 +715,22 @@ export interface DoctorReport {
|
|
|
663
715
|
infos: number;
|
|
664
716
|
};
|
|
665
717
|
}
|
|
718
|
+
/** One canonical outline-progress calculation shared by every adapter. */
|
|
719
|
+
export interface OutlineProgress {
|
|
720
|
+
present: boolean;
|
|
721
|
+
target: number | null;
|
|
722
|
+
total: number;
|
|
723
|
+
planned: number;
|
|
724
|
+
archived: number;
|
|
725
|
+
missingCards: number;
|
|
726
|
+
plannedCoverage: number | null;
|
|
727
|
+
archivedCoverage: number | null;
|
|
728
|
+
/** Compatibility alias: planned coverage when a target exists, otherwise card coverage. */
|
|
729
|
+
coverage: number;
|
|
730
|
+
/** Fraction of archived chapters that have a matching archived card. */
|
|
731
|
+
cardCoverage: number;
|
|
732
|
+
volumes: number;
|
|
733
|
+
}
|
|
666
734
|
/** Book-wide quality, outline, cast and pacing aggregates. */
|
|
667
735
|
export interface BookReport {
|
|
668
736
|
progress: {
|
|
@@ -672,13 +740,22 @@ export interface BookReport {
|
|
|
672
740
|
average_words: number;
|
|
673
741
|
percent: number;
|
|
674
742
|
remaining_words: number | null;
|
|
743
|
+
remaining_words_min: number | null;
|
|
744
|
+
remaining_words_average: number | null;
|
|
745
|
+
remaining_words_basis: 'actual_average' | 'minimum_target' | 'none';
|
|
675
746
|
};
|
|
676
747
|
outline: {
|
|
677
748
|
present: boolean;
|
|
678
749
|
strict: boolean;
|
|
750
|
+
target: number | null;
|
|
751
|
+
total: number;
|
|
679
752
|
planned: number;
|
|
680
753
|
archived: number;
|
|
754
|
+
missing_cards: number;
|
|
755
|
+
planned_coverage: number | null;
|
|
756
|
+
archived_coverage: number | null;
|
|
681
757
|
coverage: number;
|
|
758
|
+
card_coverage: number;
|
|
682
759
|
volumes: Array<{
|
|
683
760
|
title: string;
|
|
684
761
|
start: number;
|
|
@@ -701,6 +778,7 @@ export interface BookReport {
|
|
|
701
778
|
rules: number;
|
|
702
779
|
glossary: number;
|
|
703
780
|
backstory: number;
|
|
781
|
+
facts?: number;
|
|
704
782
|
};
|
|
705
783
|
quality: {
|
|
706
784
|
average: number;
|
|
@@ -715,6 +793,22 @@ export interface BookReport {
|
|
|
715
793
|
samples: number;
|
|
716
794
|
}>;
|
|
717
795
|
};
|
|
796
|
+
/** Per-chapter effort survives archive (pipeline rounds are reset). */
|
|
797
|
+
effort: {
|
|
798
|
+
reviews: number;
|
|
799
|
+
revisions: number;
|
|
800
|
+
checks: number;
|
|
801
|
+
rollbacks: number;
|
|
802
|
+
chapters: Array<{
|
|
803
|
+
num: number;
|
|
804
|
+
reviews: number;
|
|
805
|
+
revisions: number;
|
|
806
|
+
checks: number;
|
|
807
|
+
rollbacks: number;
|
|
808
|
+
final_check: 'pass' | 'fail' | null;
|
|
809
|
+
archived_at: string | null;
|
|
810
|
+
}>;
|
|
811
|
+
};
|
|
718
812
|
pacing: {
|
|
719
813
|
scene_types: Record<string, number>;
|
|
720
814
|
hook_strength: Record<string, number>;
|
|
@@ -805,10 +899,23 @@ export interface ReviewInput {
|
|
|
805
899
|
total: number;
|
|
806
900
|
proof: ReadingProof;
|
|
807
901
|
}
|
|
902
|
+
/** One exact, unique replacement used by patch-mode revisions. */
|
|
903
|
+
export interface RevisionEdit {
|
|
904
|
+
old_text: string;
|
|
905
|
+
new_text: string;
|
|
906
|
+
/** 1-based occurrence; defaults to requiring exactly one match. */
|
|
907
|
+
occurrence?: number;
|
|
908
|
+
}
|
|
909
|
+
/** Patch-mode revision; content and patch are mutually exclusive. */
|
|
910
|
+
export interface RevisionPatch {
|
|
911
|
+
expected_hash: string;
|
|
912
|
+
edits: RevisionEdit[];
|
|
913
|
+
}
|
|
808
914
|
/** Revision submission payload. */
|
|
809
915
|
export interface RevisionInput {
|
|
810
916
|
chapter: number;
|
|
811
|
-
content
|
|
917
|
+
content?: string;
|
|
918
|
+
patch?: RevisionPatch;
|
|
812
919
|
summary: ChapterSummary;
|
|
813
920
|
}
|
|
814
921
|
/** Semantic check items supplied by the model. */
|
|
@@ -880,6 +987,15 @@ export interface ChapterStatusRow {
|
|
|
880
987
|
stage: Stage;
|
|
881
988
|
passed: boolean;
|
|
882
989
|
}
|
|
990
|
+
/** Where an omitted project root came from. */
|
|
991
|
+
export type SelectionSource = 'explicit' | 'session' | 'workspace' | 'active' | 'config' | 'single';
|
|
992
|
+
/** One resolved project choice, before the project is loaded. */
|
|
993
|
+
export interface Selection {
|
|
994
|
+
root: string;
|
|
995
|
+
source: SelectionSource;
|
|
996
|
+
title?: string;
|
|
997
|
+
created_at?: string;
|
|
998
|
+
}
|
|
883
999
|
/** The `getStatus` payload. */
|
|
884
1000
|
export interface StatusReport {
|
|
885
1001
|
project: {
|
|
@@ -888,6 +1004,11 @@ export interface StatusReport {
|
|
|
888
1004
|
platform: Platform;
|
|
889
1005
|
root: string;
|
|
890
1006
|
};
|
|
1007
|
+
/** Why this project was chosen; lets the model notice a wrong book. */
|
|
1008
|
+
selection?: {
|
|
1009
|
+
source: SelectionSource;
|
|
1010
|
+
root: string;
|
|
1011
|
+
};
|
|
891
1012
|
/** The chapter requested for metrics; may be historical. */
|
|
892
1013
|
chapter: number;
|
|
893
1014
|
/** The chapter the pipeline is currently working on. */
|
|
@@ -910,10 +1031,15 @@ export interface StatusReport {
|
|
|
910
1031
|
strict: boolean;
|
|
911
1032
|
outline: {
|
|
912
1033
|
present: boolean;
|
|
1034
|
+
target: number | null;
|
|
913
1035
|
total: number;
|
|
914
1036
|
planned: number;
|
|
915
1037
|
archived: number;
|
|
1038
|
+
missing_cards: number;
|
|
1039
|
+
planned_coverage: number | null;
|
|
1040
|
+
archived_coverage: number | null;
|
|
916
1041
|
coverage: number;
|
|
1042
|
+
card_coverage: number;
|
|
917
1043
|
volumes: number;
|
|
918
1044
|
current: OutlineChapter | null;
|
|
919
1045
|
};
|
|
@@ -925,6 +1051,7 @@ export interface StatusReport {
|
|
|
925
1051
|
rules: number;
|
|
926
1052
|
glossary: number;
|
|
927
1053
|
backstory: number;
|
|
1054
|
+
facts?: number;
|
|
928
1055
|
};
|
|
929
1056
|
};
|
|
930
1057
|
next: {
|
|
@@ -945,6 +1072,8 @@ export interface AdviceReport {
|
|
|
945
1072
|
}
|
|
946
1073
|
/** One history row derived from the journal. */
|
|
947
1074
|
export interface AuditRow {
|
|
1075
|
+
/** Stable chronological cursor; absent only on hand-built test rows. */
|
|
1076
|
+
seq?: number;
|
|
948
1077
|
at: string;
|
|
949
1078
|
op: string;
|
|
950
1079
|
chapter: number;
|
|
@@ -952,6 +1081,14 @@ export interface AuditRow {
|
|
|
952
1081
|
txid: string;
|
|
953
1082
|
status: 'committed' | 'aborted';
|
|
954
1083
|
files: string[];
|
|
1084
|
+
/** Machine code and human reason for a refused attempt. */
|
|
1085
|
+
code?: string;
|
|
1086
|
+
reason?: string;
|
|
1087
|
+
hint?: string;
|
|
1088
|
+
stage?: string;
|
|
1089
|
+
/** True when no transaction started: the gate/input refused the call. */
|
|
1090
|
+
attempt?: boolean;
|
|
1091
|
+
blocking?: BlockingItem[];
|
|
955
1092
|
}
|
|
956
1093
|
/** One diff hunk between two revisions of a chapter. */
|
|
957
1094
|
export interface DiffReport {
|
package/package.json
CHANGED
package/skills/entry.md
CHANGED
|
@@ -16,6 +16,13 @@ description: 引擎版小说创作流程——状态、门控、字数与写盘
|
|
|
16
16
|
5. **不编设定。** 人物、地点、伏笔、资源状态拿不准就 `novel_state` 查。
|
|
17
17
|
6. **归档是一次事务,且必须核销。** `novel_archive` 必须带 `state_delta`:本章每个角色/伏笔/事件/新设定/关系逐条声明 `created|updated|no_change`;不要手工拼章节索引或交接文件。
|
|
18
18
|
|
|
19
|
+
## 项目选择(0.2.2)
|
|
20
|
+
|
|
21
|
+
- 第一次调用先 `novel_status`,看 `selection.root` 与 `selection.source`;不要凭记忆猜项目。
|
|
22
|
+
- 没有绑定且工作区/注册表有多本书时,引擎返回 `ENGINE_PROJECT_AMBIGUOUS` 并列出候选;这时显式传一次绝对 `project_root`,成功后它会成为当前会话的持久绑定,后续省略即沿用。不要用全局 `active` 猜。
|
|
23
|
+
- 用户要切换时,用 `/novel use <路径|书名|.>`;`/novel current` 查绑定,`/novel unbind` 只清会话绑定,`/novel scan` 只发现/登记、不选择。
|
|
24
|
+
- `novel_word_count` 的纯文本模式与 `novel_diff` 的 `before`+`after` 文本模式不读项目、不绑定;项目模式才传 `project_root`。
|
|
25
|
+
|
|
19
26
|
## 书级规划(0.2.0)
|
|
20
27
|
|
|
21
28
|
新项目先 `novel_guide`;推荐但不强制:`novel_blueprint`(蓝图/卷)→ `novel_outline`(章卡)→ `novel_worldbook`(设定集)。已有大纲可用 `novel_outline_import` 导入 Markdown/YAML/JSON。只要存在章卡,P2/P3 就按章卡校验标题、场景、字数与 cast;严格模式还要求章卡必须存在。要改已归档章先 `novel_reopen`(必须带 reason)。
|
|
@@ -50,21 +57,22 @@ description: 引擎版小说创作流程——状态、门控、字数与写盘
|
|
|
50
57
|
| `novel_report` | 全书报告 |
|
|
51
58
|
| `novel_guide` | 上手引导与下一步 |
|
|
52
59
|
| `novel_reopen` | 从归档备份重开紧邻的上一章 |
|
|
60
|
+
| `novel_restore` | 从标记备份恢复活动章 |
|
|
53
61
|
| `novel_brief` | 取阶段任务书与上下文 |
|
|
54
62
|
| `novel_plan_world` | P1 世界观计划 |
|
|
55
63
|
| `novel_plan_chapter` | P2 章节计划 |
|
|
56
64
|
| `novel_write_draft` | P3 初稿 |
|
|
57
65
|
| `novel_review` | P4 十五维评分 |
|
|
58
|
-
| `novel_revise` | P5
|
|
59
|
-
| `novel_check` | P6
|
|
66
|
+
| `novel_revise` | P5 修订(支持 exact patch) |
|
|
67
|
+
| `novel_check` | P6 一致性校验(advisory 可 acknowledge) |
|
|
60
68
|
| `novel_archive` | 归档事务 |
|
|
61
69
|
| `novel_word_count` | 字数口径 |
|
|
62
|
-
| `novel_state` |
|
|
70
|
+
| `novel_state` | 只读台账/计划/评审/history/backups/briefs |
|
|
63
71
|
| `novel_amend` | 受控改状态文档 |
|
|
64
72
|
| `novel_rollback` | 回退阶段 |
|
|
65
73
|
| `novel_diff` | 与备份/文本对比 |
|
|
66
74
|
| `novel_export` | md/txt/epub/outline/worldbook/proposal |
|
|
67
|
-
| `novel_audit` |
|
|
75
|
+
| `novel_audit` | 事务与失败 attempt 审计 |
|
|
68
76
|
| `novel_pipeline` | 推荐下一步与参数 |
|
|
69
77
|
|
|
70
78
|
## 工具怎么用
|