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.
@@ -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
- summary: SummaryDto;
108
- chapters: ChaptersDto;
109
- gates: GatesDto;
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 = 2;
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
@@ -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 = 2;
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[];
@@ -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-one service operations (22 inherited from 0.1.x, 9 book-level). */
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: string;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-novel-write-engine",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Novel writing engine for DeepSeek Harness: state machine, gates, rulepacks and ledgers moved from prompts into code.",
5
5
  "keywords": [
6
6
  "dsh-plugin",
@@ -4,7 +4,7 @@
4
4
  config:
5
5
  id: novel-write-engine
6
6
  name: 网文工坊
7
- description: 网文创作专用会话:27 个小说工具全开,无文件写权限,门控与字数由引擎裁决。
7
+ description: 网文创作专用会话:28 个小说工具全开,无文件写权限,门控与字数由引擎裁决。
8
8
  order: 20
9
9
  plugins:
10
10
  - id: persona
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
  ## 工具怎么用