dsh-novel-write-engine 0.2.1 → 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;
@@ -108,11 +110,35 @@ export interface GatesDto {
108
110
  blocking: BlockingDto[];
109
111
  explanations: string[];
110
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
+ }
111
131
  export interface PanelPayloadDto {
112
132
  schemaVersion: number;
113
- summary: SummaryDto;
114
- chapters: ChaptersDto;
115
- 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;
116
142
  error?: string;
117
143
  empty?: true;
118
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;
@@ -97,7 +126,7 @@ export interface PanelView {
97
126
  revision: string;
98
127
  next: string;
99
128
  }
100
- export declare const PANEL_SCHEMA_VERSION = 3;
129
+ export declare const PANEL_SCHEMA_VERSION = 4;
101
130
  /**
102
131
  * Project the wire payload into strings. Version mismatch wins over everything
103
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 = 3;
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. */
@@ -25,6 +25,9 @@ export declare const OUTLINE_DIR = "outline";
25
25
  export declare const LOCK_FILE = ".engine.lock";
26
26
  export declare const STAGING_DIR = ".staging";
27
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";
28
31
  /** Files owned by the engine under `state/`. */
29
32
  export declare const STATE_FILES: {
30
33
  readonly pipeline: "pipeline.yaml";
@@ -20,7 +20,7 @@ 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). */
23
+ /** All thirty-two service operations (22 inherited + 9 book-level + restoreBackup). */
24
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. */
@@ -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";
@@ -984,6 +987,15 @@ export interface ChapterStatusRow {
984
987
  stage: Stage;
985
988
  passed: boolean;
986
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
+ }
987
999
  /** The `getStatus` payload. */
988
1000
  export interface StatusReport {
989
1001
  project: {
@@ -992,6 +1004,11 @@ export interface StatusReport {
992
1004
  platform: Platform;
993
1005
  root: string;
994
1006
  };
1007
+ /** Why this project was chosen; lets the model notice a wrong book. */
1008
+ selection?: {
1009
+ source: SelectionSource;
1010
+ root: string;
1011
+ };
995
1012
  /** The chapter requested for metrics; may be historical. */
996
1013
  chapter: number;
997
1014
  /** The chapter the pipeline is currently working on. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-novel-write-engine",
3
- "version": "0.2.1",
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",
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)。