novelws 5.2.0 → 5.3.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +51 -0
  3. package/dist/cli.js +3 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/dashboard.d.ts +3 -0
  6. package/dist/commands/dashboard.d.ts.map +1 -0
  7. package/dist/commands/dashboard.js +42 -0
  8. package/dist/commands/dashboard.js.map +1 -0
  9. package/dist/commands/init.d.ts.map +1 -1
  10. package/dist/commands/init.js +19 -0
  11. package/dist/commands/init.js.map +1 -1
  12. package/dist/commands/upgrade.d.ts.map +1 -1
  13. package/dist/commands/upgrade.js +23 -0
  14. package/dist/commands/upgrade.js.map +1 -1
  15. package/dist/core/config.d.ts +3 -0
  16. package/dist/core/config.d.ts.map +1 -1
  17. package/dist/core/config.js +4 -0
  18. package/dist/core/config.js.map +1 -1
  19. package/dist/server/datasource/db.d.ts +38 -0
  20. package/dist/server/datasource/db.d.ts.map +1 -0
  21. package/dist/server/datasource/db.js +323 -0
  22. package/dist/server/datasource/db.js.map +1 -0
  23. package/dist/server/datasource/fs.d.ts +30 -0
  24. package/dist/server/datasource/fs.d.ts.map +1 -0
  25. package/dist/server/datasource/fs.js +308 -0
  26. package/dist/server/datasource/fs.js.map +1 -0
  27. package/dist/server/datasource/index.d.ts +11 -0
  28. package/dist/server/datasource/index.d.ts.map +1 -0
  29. package/dist/server/datasource/index.js +33 -0
  30. package/dist/server/datasource/index.js.map +1 -0
  31. package/dist/server/index.d.ts +12 -0
  32. package/dist/server/index.d.ts.map +1 -0
  33. package/dist/server/index.js +69 -0
  34. package/dist/server/index.js.map +1 -0
  35. package/dist/server/routes/characters.d.ts +4 -0
  36. package/dist/server/routes/characters.d.ts.map +1 -0
  37. package/dist/server/routes/characters.js +27 -0
  38. package/dist/server/routes/characters.js.map +1 -0
  39. package/dist/server/routes/plots.d.ts +4 -0
  40. package/dist/server/routes/plots.d.ts.map +1 -0
  41. package/dist/server/routes/plots.js +36 -0
  42. package/dist/server/routes/plots.js.map +1 -0
  43. package/dist/server/routes/protagonist.d.ts +4 -0
  44. package/dist/server/routes/protagonist.d.ts.map +1 -0
  45. package/dist/server/routes/protagonist.js +46 -0
  46. package/dist/server/routes/protagonist.js.map +1 -0
  47. package/dist/server/routes/relationships.d.ts +4 -0
  48. package/dist/server/routes/relationships.d.ts.map +1 -0
  49. package/dist/server/routes/relationships.js +27 -0
  50. package/dist/server/routes/relationships.js.map +1 -0
  51. package/dist/server/routes/stats.d.ts +4 -0
  52. package/dist/server/routes/stats.d.ts.map +1 -0
  53. package/dist/server/routes/stats.js +21 -0
  54. package/dist/server/routes/stats.js.map +1 -0
  55. package/dist/server/routes/stories.d.ts +4 -0
  56. package/dist/server/routes/stories.d.ts.map +1 -0
  57. package/dist/server/routes/stories.js +80 -0
  58. package/dist/server/routes/stories.js.map +1 -0
  59. package/dist/server/routes/timeline.d.ts +4 -0
  60. package/dist/server/routes/timeline.d.ts.map +1 -0
  61. package/dist/server/routes/timeline.js +17 -0
  62. package/dist/server/routes/timeline.js.map +1 -0
  63. package/dist/server/types.d.ts +199 -0
  64. package/dist/server/types.d.ts.map +1 -0
  65. package/dist/server/types.js +2 -0
  66. package/dist/server/types.js.map +1 -0
  67. package/package.json +13 -3
  68. package/templates/commands/analyze.md +9 -1
  69. package/templates/commands/expand.md +19 -3
  70. package/templates/commands/write.md +23 -7
  71. package/templates/dot-claude/CLAUDE.md +27 -0
  72. package/templates/scripts/db_context.py +609 -0
  73. package/templates/scripts/db_init_protagonist.py +343 -0
  74. package/templates/scripts/db_sync.py +611 -0
  75. package/templates/scripts/db_volume_switch.py +278 -0
  76. package/templates/scripts/phase_a_init_db.py +428 -0
  77. package/templates/scripts/requirements.txt +1 -0
  78. package/templates/tracking/character-state.json +1 -3
  79. package/templates/tracking/plot-tracker.json +1 -5
  80. package/templates/tracking/relationships.json +1 -3
  81. package/templates/tracking/timeline.json +1 -3
  82. package/templates/volume-outline.md +31 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../../../src/server/routes/relationships.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,CAyBhE"}
@@ -0,0 +1,27 @@
1
+ import { Router } from 'express';
2
+ export function createRelationshipsRouter(ds) {
3
+ const router = Router({ mergeParams: true });
4
+ // GET /api/stories/:story/relationships?vol=X
5
+ router.get('/', async (req, res) => {
6
+ try {
7
+ const vol = req.query.vol ? parseInt(req.query.vol, 10) : undefined;
8
+ const graph = await ds.getRelationships(req.params.story, vol);
9
+ res.json(graph);
10
+ }
11
+ catch (err) {
12
+ res.status(500).json({ error: err.message });
13
+ }
14
+ });
15
+ // GET /api/stories/:story/relationships/history
16
+ router.get('/history', async (req, res) => {
17
+ try {
18
+ const history = await ds.getRelationshipHistory(req.params.story);
19
+ res.json(history);
20
+ }
21
+ catch (err) {
22
+ res.status(500).json({ error: err.message });
23
+ }
24
+ });
25
+ return router;
26
+ }
27
+ //# sourceMappingURL=relationships.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relationships.js","sourceRoot":"","sources":["../../../src/server/routes/relationships.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAGpD,MAAM,UAAU,yBAAyB,CAAC,EAAc;IACtD,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,8CAA8C;IAC9C,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAA+B,EAAE,GAAa,EAAE,EAAE;QACvE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAa,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9E,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAC/D,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,gDAAgD;IAChD,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,GAA+B,EAAE,GAAa,EAAE,EAAE;QAC9E,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,sBAAsB,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAClE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Router } from 'express';
2
+ import type { DataSource } from '../types.js';
3
+ export declare function createStatsRouter(ds: DataSource): Router;
4
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../../src/server/routes/stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,CAmBxD"}
@@ -0,0 +1,21 @@
1
+ import { Router } from 'express';
2
+ export function createStatsRouter(ds) {
3
+ const router = Router();
4
+ // GET /api/stats/dashboard?story=X
5
+ router.get('/dashboard', async (req, res) => {
6
+ try {
7
+ const story = req.query.story;
8
+ if (!story) {
9
+ res.status(400).json({ error: '缺少 story 参数' });
10
+ return;
11
+ }
12
+ const stats = await ds.getDashboardStats(story);
13
+ res.json(stats);
14
+ }
15
+ catch (err) {
16
+ res.status(500).json({ error: err.message });
17
+ }
18
+ });
19
+ return router;
20
+ }
21
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../src/server/routes/stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,MAAM,UAAU,iBAAiB,CAAC,EAAc;IAC9C,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IAExB,mCAAmC;IACnC,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAC1C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAAe,CAAC;YACxC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;gBAC/C,OAAO;YACT,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC;YAChD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Router } from 'express';
2
+ import type { DataSource } from '../types.js';
3
+ export declare function createStoriesRouter(ds: DataSource): Router;
4
+ //# sourceMappingURL=stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stories.d.ts","sourceRoot":"","sources":["../../../src/server/routes/stories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,CA8E1D"}
@@ -0,0 +1,80 @@
1
+ import { Router } from 'express';
2
+ export function createStoriesRouter(ds) {
3
+ const router = Router();
4
+ // GET /api/stories
5
+ router.get('/', async (_req, res) => {
6
+ try {
7
+ const stories = await ds.getStories();
8
+ res.json(stories);
9
+ }
10
+ catch (err) {
11
+ res.status(500).json({ error: err.message });
12
+ }
13
+ });
14
+ // GET /api/stories/:story/overview
15
+ router.get('/:story/overview', async (req, res) => {
16
+ try {
17
+ const overview = await ds.getOverview(req.params.story);
18
+ res.json(overview);
19
+ }
20
+ catch (err) {
21
+ res.status(500).json({ error: err.message });
22
+ }
23
+ });
24
+ // GET /api/stories/:story/volumes
25
+ router.get('/:story/volumes', async (req, res) => {
26
+ try {
27
+ const volumes = await ds.getVolumes(req.params.story);
28
+ res.json(volumes);
29
+ }
30
+ catch (err) {
31
+ res.status(500).json({ error: err.message });
32
+ }
33
+ });
34
+ // GET /api/stories/:story/volumes/:vol/stats
35
+ router.get('/:story/volumes/:vol/stats', async (req, res) => {
36
+ try {
37
+ const vol = parseInt(req.params.vol, 10);
38
+ const chapters = await ds.getChapters(req.params.story, vol);
39
+ const characters = await ds.getCharacters(req.params.story, vol);
40
+ res.json({
41
+ volume: vol,
42
+ chapters: chapters.length,
43
+ words: chapters.reduce((sum, c) => sum + c.words, 0),
44
+ characters: characters.length,
45
+ });
46
+ }
47
+ catch (err) {
48
+ res.status(500).json({ error: err.message });
49
+ }
50
+ });
51
+ // GET /api/stories/:story/chapters?vol=X
52
+ router.get('/:story/chapters', async (req, res) => {
53
+ try {
54
+ const vol = req.query.vol ? parseInt(req.query.vol, 10) : undefined;
55
+ const chapters = await ds.getChapters(req.params.story, vol);
56
+ res.json(chapters);
57
+ }
58
+ catch (err) {
59
+ res.status(500).json({ error: err.message });
60
+ }
61
+ });
62
+ // GET /api/stories/:story/chapters/:ch
63
+ router.get('/:story/chapters/:ch', async (req, res) => {
64
+ try {
65
+ const chNum = parseInt(req.params.ch, 10);
66
+ const chapters = await ds.getChapters(req.params.story);
67
+ const chapter = chapters.find(c => c.globalNumber === chNum);
68
+ if (!chapter) {
69
+ res.status(404).json({ error: '章节不存在' });
70
+ return;
71
+ }
72
+ res.json(chapter);
73
+ }
74
+ catch (err) {
75
+ res.status(500).json({ error: err.message });
76
+ }
77
+ });
78
+ return router;
79
+ }
80
+ //# sourceMappingURL=stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stories.js","sourceRoot":"","sources":["../../../src/server/routes/stories.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Router } from 'express';
2
+ import type { DataSource } from '../types.js';
3
+ export declare function createTimelineRouter(ds: DataSource): Router;
4
+ //# sourceMappingURL=timeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.d.ts","sourceRoot":"","sources":["../../../src/server/routes/timeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,CAe3D"}
@@ -0,0 +1,17 @@
1
+ import { Router } from 'express';
2
+ export function createTimelineRouter(ds) {
3
+ const router = Router({ mergeParams: true });
4
+ // GET /api/stories/:story/timeline?vol=X
5
+ router.get('/', async (req, res) => {
6
+ try {
7
+ const vol = req.query.vol ? parseInt(req.query.vol, 10) : undefined;
8
+ const events = await ds.getTimeline(req.params.story, vol);
9
+ res.json(events);
10
+ }
11
+ catch (err) {
12
+ res.status(500).json({ error: err.message });
13
+ }
14
+ });
15
+ return router;
16
+ }
17
+ //# sourceMappingURL=timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../../src/server/routes/timeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAGpD,MAAM,UAAU,oBAAoB,CAAC,EAAc;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,yCAAyC;IACzC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,GAA+B,EAAE,GAAa,EAAE,EAAE;QACvE,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,GAAa,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC9E,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;YAC3D,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,199 @@
1
+ /** 故事基础信息 */
2
+ export interface Story {
3
+ name: string;
4
+ path: string;
5
+ }
6
+ /** 故事总览 */
7
+ export interface StoryOverview {
8
+ name: string;
9
+ totalVolumes: number;
10
+ totalChapters: number;
11
+ totalWords: number;
12
+ currentVolume: number;
13
+ }
14
+ /** 卷信息 */
15
+ export interface Volume {
16
+ number: number;
17
+ title: string;
18
+ chapters: number;
19
+ words: number;
20
+ progress: number;
21
+ }
22
+ /** 章节信息 */
23
+ export interface Chapter {
24
+ globalNumber: number;
25
+ volumeNumber: number;
26
+ chapterInVolume: number;
27
+ title: string;
28
+ words: number;
29
+ pov: string;
30
+ participants: string[];
31
+ hasSynopsis: boolean;
32
+ hasContent: boolean;
33
+ }
34
+ /** 角色信息 */
35
+ export interface Character {
36
+ name: string;
37
+ aliases: string[];
38
+ role: string;
39
+ firstVolume: number;
40
+ firstChapter: number;
41
+ cultivation: string;
42
+ faction: string;
43
+ status: string;
44
+ }
45
+ /** 角色状态快照(按卷) */
46
+ export interface CharacterState {
47
+ volume: number;
48
+ cultivation: string;
49
+ location: string;
50
+ summary: string;
51
+ lastChapter: number;
52
+ }
53
+ /** 关系图节点 */
54
+ export interface RelationshipNode {
55
+ id: string;
56
+ name: string;
57
+ faction: string;
58
+ role: string;
59
+ appearances: number;
60
+ }
61
+ /** 关系图边 */
62
+ export interface RelationshipEdge {
63
+ source: string;
64
+ target: string;
65
+ type: string;
66
+ status: string;
67
+ lastChapter: number;
68
+ }
69
+ /** 关系网络图 */
70
+ export interface RelationshipGraph {
71
+ nodes: RelationshipNode[];
72
+ edges: RelationshipEdge[];
73
+ }
74
+ /** 关系变迁事件 */
75
+ export interface RelationshipEvent {
76
+ chapter: number;
77
+ source: string;
78
+ target: string;
79
+ type: string;
80
+ oldStatus: string;
81
+ newStatus: string;
82
+ }
83
+ /** 时间线事件 */
84
+ export interface TimelineEvent {
85
+ id: number;
86
+ chapter: number;
87
+ storyTime: string;
88
+ location: string;
89
+ description: string;
90
+ tags: string[];
91
+ }
92
+ /** 情节线 */
93
+ export interface PlotThread {
94
+ name: string;
95
+ type: string;
96
+ status: string;
97
+ description: string;
98
+ keyEvents: string[];
99
+ }
100
+ /** 伏笔 */
101
+ export interface Foreshadow {
102
+ code: string;
103
+ description: string;
104
+ plantedChapter: number;
105
+ hintedChapters: number[];
106
+ resolvedChapter: number | null;
107
+ status: string;
108
+ importance: string;
109
+ }
110
+ /** 伏笔矩阵单元格 */
111
+ export interface ForeshadowCell {
112
+ chapter: number;
113
+ action: 'plant' | 'hint' | 'resolve' | null;
114
+ }
115
+ /** 伏笔矩阵 */
116
+ export interface ForeshadowMatrix {
117
+ chapters: number[];
118
+ rows: Array<{
119
+ code: string;
120
+ description: string;
121
+ cells: ForeshadowCell[];
122
+ }>;
123
+ }
124
+ /** 卷级统计 */
125
+ export interface VolumeStat {
126
+ volume: number;
127
+ title: string;
128
+ words: number;
129
+ chapters: number;
130
+ progress: number;
131
+ }
132
+ /** 仪表盘聚合统计 */
133
+ export interface DashboardStats {
134
+ totalWords: number;
135
+ totalChapters: number;
136
+ totalVolumes: number;
137
+ totalCharacters: number;
138
+ activePlotThreads: number;
139
+ unresolvedForeshadowing: number;
140
+ volumeStats: VolumeStat[];
141
+ }
142
+ /** 主角技能 */
143
+ export interface ProtagonistSkill {
144
+ name: string;
145
+ category: string;
146
+ level: string;
147
+ description: string;
148
+ acquiredChapter: number;
149
+ useCount: number;
150
+ status: string;
151
+ }
152
+ /** 主角道具 */
153
+ export interface ProtagonistItem {
154
+ name: string;
155
+ type: string;
156
+ quantity: number;
157
+ quality: string;
158
+ description: string;
159
+ acquiredChapter: number;
160
+ status: string;
161
+ }
162
+ /** 修炼进度节点 */
163
+ export interface CultivationNode {
164
+ chapter: number;
165
+ level: string;
166
+ progressPct: number;
167
+ breakthroughType: string | null;
168
+ detail: string;
169
+ }
170
+ /** 主角总览 */
171
+ export interface ProtagonistOverview {
172
+ currentLevel: string;
173
+ currentProgress: number;
174
+ totalSkills: number;
175
+ activeSkills: number;
176
+ totalItems: number;
177
+ heldItems: number;
178
+ }
179
+ /** 数据源抽象接口 */
180
+ export interface DataSource {
181
+ getStories(): Promise<Story[]>;
182
+ getOverview(story: string): Promise<StoryOverview>;
183
+ getVolumes(story: string): Promise<Volume[]>;
184
+ getChapters(story: string, vol?: number): Promise<Chapter[]>;
185
+ getCharacters(story: string, vol?: number): Promise<Character[]>;
186
+ getCharacterArc(story: string, name: string): Promise<CharacterState[]>;
187
+ getRelationships(story: string, vol?: number): Promise<RelationshipGraph>;
188
+ getRelationshipHistory(story: string): Promise<RelationshipEvent[]>;
189
+ getTimeline(story: string, vol?: number): Promise<TimelineEvent[]>;
190
+ getPlotThreads(story: string): Promise<PlotThread[]>;
191
+ getForeshadowing(story: string): Promise<Foreshadow[]>;
192
+ getForeshadowingMatrix(story: string): Promise<ForeshadowMatrix>;
193
+ getDashboardStats(story: string): Promise<DashboardStats>;
194
+ getProtagonistOverview(story: string): Promise<ProtagonistOverview>;
195
+ getProtagonistSkills(story: string): Promise<ProtagonistSkill[]>;
196
+ getProtagonistInventory(story: string): Promise<ProtagonistItem[]>;
197
+ getCultivationCurve(story: string): Promise<CultivationNode[]>;
198
+ }
199
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "novelws",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "type": "module",
5
5
  "description": "Claude Code 专用的 AI 小说创作工具 - 深度集成 Slash Commands 和 Agent Skills",
6
6
  "keywords": [
@@ -33,22 +33,29 @@
33
33
  "test:watch": "jest --config jest.config.cjs --watch",
34
34
  "test:coverage": "jest --config jest.config.cjs --coverage",
35
35
  "test:unit": "jest --config jest.config.cjs tests/unit",
36
- "test:integration": "jest --config jest.config.cjs tests/integration"
36
+ "test:integration": "jest --config jest.config.cjs tests/integration",
37
+ "build:dashboard": "cd dashboard && npm run build",
38
+ "dashboard": "node dist/cli.js dashboard"
37
39
  },
38
40
  "dependencies": {
39
41
  "@commander-js/extra-typings": "^12.0.0",
40
42
  "chalk": "^5.3.0",
43
+ "express": "^5.2.1",
41
44
  "fs-extra": "^11.2.0",
42
45
  "inquirer": "^9.2.12",
43
46
  "js-yaml": "^4.1.0",
44
- "ora": "^8.0.1"
47
+ "open": "^11.0.0",
48
+ "ora": "^8.0.1",
49
+ "pg": "^8.18.0"
45
50
  },
46
51
  "devDependencies": {
52
+ "@types/express": "^5.0.6",
47
53
  "@types/fs-extra": "^11.0.4",
48
54
  "@types/inquirer": "^9.0.9",
49
55
  "@types/jest": "^30.0.0",
50
56
  "@types/js-yaml": "^4.0.9",
51
57
  "@types/node": "^20.10.0",
58
+ "@types/pg": "^8.16.0",
52
59
  "jest": "^30.2.0",
53
60
  "ts-jest": "^29.4.6",
54
61
  "tsx": "^4.7.0",
@@ -71,5 +78,8 @@
71
78
  "homepage": "https://github.com/binlee1990/novel-writer-skills#readme",
72
79
  "bugs": {
73
80
  "url": "https://github.com/binlee1990/novel-writer-skills/issues"
81
+ },
82
+ "overrides": {
83
+ "yoctocolors-cjs": "2.1.2"
74
84
  }
75
85
  }
@@ -2,7 +2,7 @@
2
2
  description: 质量检查:对比概要与正文,检测一致性和AI味
3
3
  argument-hint: [章节号] [--range start-end]
4
4
  recommended-model: claude-sonnet-4-5-20250929
5
- allowed-tools: Read(//stories/**), Read(//resources/anti-ai.md), Bash(ls:*)
5
+ allowed-tools: Read(//stories/**), Read(//resources/anti-ai.md), Bash(ls:*), Bash(python:scripts/*)
6
6
  ---
7
7
 
8
8
  用户输入:$ARGUMENTS
@@ -26,6 +26,14 @@ allowed-tools: Read(//stories/**), Read(//resources/anti-ai.md), Bash(ls:*)
26
26
  - 对应概要:`stories/<story>/volumes/vol-XXX/content/chapter-YYY-synopsis.md`
27
27
  - 卷级 tracking:`stories/<story>/volumes/vol-XXX/tracking/character-state.json`、`stories/<story>/volumes/vol-XXX/tracking/plot-tracker.json`
28
28
 
29
+ **DB 增强模式**:如果 `resources/config.json` 中 `database.enabled = true`,可用 DB 上下文替代 tracking 文件读取:
30
+
31
+ ```
32
+ python scripts/db_context.py --chapter <全局章节号> --mode analyze
33
+ ```
34
+
35
+ 输出包含本章伏笔检查清单、角色状态基准、时间线连续性和一致性警告。输出还包含主角能力基准(技能习得时间线 + 道具持有状态),用于检测正文中是否使用了尚未习得的技能或不持有的道具。
36
+
29
37
  ## 检查项(5项)
30
38
 
31
39
  ### 1. 概要符合度
@@ -2,7 +2,7 @@
2
2
  description: 将章节概要扩写为 3000-5000 字正文
3
3
  argument-hint: [章节号] [--batch N]
4
4
  recommended-model: claude-opus-4-6
5
- allowed-tools: Read(//stories/**), Write(//stories/**/content/**), Read(//stories/**/tracking/**), Write(//stories/**/tracking/**), Read(//resources/style-reference.md), Read(//resources/anti-ai.md), Read(//resources/constitution.md), Bash(ls:*)
5
+ allowed-tools: Read(//stories/**), Write(//stories/**/content/**), Read(//stories/**/tracking/**), Write(//stories/**/tracking/**), Read(//resources/style-reference.md), Read(//resources/anti-ai.md), Read(//resources/constitution.md), Bash(ls:*), Bash(python:scripts/*)
6
6
  ---
7
7
 
8
8
  用户输入:$ARGUMENTS
@@ -38,6 +38,14 @@ allowed-tools: Read(//stories/**), Write(//stories/**/content/**), Read(//storie
38
38
 
39
39
  所有资源必须从文件系统读取,不复用对话中的缓存。总上下文控制在 3500 字以内。
40
40
 
41
+ **DB 增强模式**:如果 `resources/config.json` 中 `database.enabled = true`,可用 DB 上下文替代第 3 层的 tracking 文件读取:
42
+
43
+ ```
44
+ python scripts/db_context.py --chapter <全局章节号> --mode expand
45
+ ```
46
+
47
+ 输出包含本章伏笔、活跃角色状态、角色关系、前序衔接,直接替代手动读取 tracking JSON。输出还包含主角当前修为、可用技能和道具清单,确保扩写中主角能力使用不超出已有设定。第 1、2 层仍从文件系统加载。
48
+
41
49
  ### 第 1 层 — 全局视角
42
50
 
43
51
  1. **specification.md 摘要**:读取 `stories/<story>/specification.md`,提取 100 字核心摘要(类型 + 主角 + 核心冲突)
@@ -100,13 +108,21 @@ allowed-tools: Read(//stories/**), Write(//stories/**/content/**), Read(//storie
100
108
  - 对话中透露的新信息 → 更新 `volumes/vol-XXX/tracking/` 中的 character-state 或 relationships
101
109
  - 新的场景细节 → 如有重要设定变化,更新相关 tracking
102
110
 
103
- ### 5. 批量模式
111
+ ### 5. DB 同步(可选)
112
+
113
+ 如果 `resources/config.json` 中 `database.enabled = true`,在 tracking 更新完成后运行:
114
+
115
+ ```
116
+ python scripts/db_sync.py --vol <当前卷号>
117
+ ```
118
+
119
+ ### 6. 批量模式
104
120
 
105
121
  如果指定了 `--batch N`,重复步骤 2-4 共 N 次。每章完成后输出进度和字数。
106
122
 
107
123
  **批量模式资源隔离**:每章都必须重新从文件系统加载所有资源。批量模式中每章视为独立的扩写任务。如果批量过程中跨卷,需要切换到下一卷的 tracking 和 content 目录。
108
124
 
109
- ### 6. 后续建议
125
+ ### 7. 后续建议
110
126
 
111
127
  单章完成:「第X章扩写完成(XXXX字,vol-XXX)。继续 /expand [X+1] 或使用 /analyze X 检查质量。」
112
128
 
@@ -1,15 +1,15 @@
1
1
  ---
2
- description: 逐章生成剧情概要(200-500字),同步更新 tracking
2
+ description: 逐章生成剧情概要(200-500字),严格控制字数,同步更新 tracking
3
3
  argument-hint: [章节号] [--batch N]
4
4
  recommended-model: claude-opus-4-6
5
- allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*)
5
+ allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*), Bash(python:scripts/*)
6
6
  ---
7
7
 
8
8
  用户输入:$ARGUMENTS
9
9
 
10
10
  ## 目标
11
11
 
12
- 为指定章节生成 200-500 字的纯剧情概要,同步生成 tracking 骨架数据。
12
+ 为指定章节生成 200-500 字的纯剧情概要(严格控制字数),同步生成 tracking 骨架数据。
13
13
 
14
14
  ## 参数解析
15
15
 
@@ -37,6 +37,12 @@ allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*
37
37
 
38
38
  当目标章节属于新卷时(上一卷已完成):
39
39
 
40
+ **路径 A — DB 模式**(`resources/config.json` 中 `database.enabled = true`):
41
+
42
+ 运行 `python scripts/db_volume_switch.py --vol <新卷号>` 自动生成 volume-summary.md。输出还包含主角当前修炼进度、技能清单和道具状态,用于概要中主角能力描写的一致性。
43
+
44
+ **路径 B — 文件模式**(默认):
45
+
40
46
  1. 读取上一卷 `volumes/vol-NNN/tracking/` 的 4 个 JSON 文件
41
47
  2. 读取上一卷最后一章 synopsis 的章末钩子
42
48
  3. 读取 `creative-plan.md` 中下一卷的大纲段落
@@ -46,7 +52,7 @@ allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*
46
52
  - 活跃伏笔:从 plot-tracker.json 提取 status=planted/hinted 的条目
47
53
  - 关键关系:从 relationships.json 提取活跃角色间的关系
48
54
  - 待续悬念:上一卷末章的章末钩子
49
- 5. volume-summary.md 控制在 500-1000
55
+ 5. volume-summary.md 控制在 500-2000 字(严格控制字数)
50
56
 
51
57
  ## 资源加载(卷级)
52
58
 
@@ -74,7 +80,7 @@ allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*
74
80
 
75
81
  ### 4. 生成概要
76
82
 
77
- 为当前章节生成 200-500 字纯剧情概要,包含:
83
+ 为当前章节生成 200-500 字纯剧情概要(严格控制字数),包含:
78
84
 
79
85
  - **本章标题**:简短的章节标题
80
86
  - **核心事件**:本章发生的主要事件(1-3个)
@@ -105,11 +111,21 @@ allowed-tools: Read(//stories/**), Write(//stories/**), Bash(ls:*), Bash(mkdir:*
105
111
  **timeline.json**:
106
112
  - 添加本章事件到 events(chapter, time, event)
107
113
 
108
- ### 6. 批量模式
114
+ ### 6. DB 同步(可选)
115
+
116
+ 如果 `resources/config.json` 中 `database.enabled = true`,在 tracking 更新完成后运行:
117
+
118
+ ```
119
+ python scripts/db_sync.py --vol <当前卷号>
120
+ ```
121
+
122
+ 将本卷 tracking 数据同步到 PostgreSQL。
123
+
124
+ ### 7. 批量模式
109
125
 
110
126
  如果指定了 `--batch N`,重复步骤 3-5 共 N 次。如果批量过程中跨卷,自动触发卷切换。
111
127
 
112
- ### 7. 后续建议
128
+ ### 8. 后续建议
113
129
 
114
130
  单章完成:「第X章概要已生成(vol-XXX)。继续 /write [X+1] 或 /write --batch 20 批量生成。」
115
131
 
@@ -60,6 +60,33 @@ stories/<story>/
60
60
  - /write 完成后自动更新卷级 tracking 骨架
61
61
  - /expand 完成后补充卷级 tracking 细节
62
62
 
63
+ ## DB 增强(可选)
64
+
65
+ 项目支持 PostgreSQL 双轨并行模式:DB 可用时增强上下文精度,不可用时回退到纯文件系统流程。
66
+
67
+ ### 配置
68
+
69
+ 编辑 `resources/config.json` 中的 `database` 字段:
70
+ - `enabled: true` 启用 DB 模式
71
+ - 配置 host、port、dbname、user、password
72
+ - schema 默认为 `novelws`
73
+
74
+ ### scripts/ 目录
75
+
76
+ | 脚本 | 用途 |
77
+ |------|------|
78
+ | `phase_a_init_db.py` | 初始化数据库(建表 + 视图) |
79
+ | `db_sync.py` | 文件系统 tracking → DB 同步(幂等) |
80
+ | `db_context.py` | 从 DB 生成精确上下文(write/expand/analyze 模式) |
81
+ | `db_volume_switch.py` | 从 DB 生成 volume-summary.md(卷切换) |
82
+
83
+ ### 双轨机制
84
+
85
+ - /write、/expand、/analyze 命令会检测 `database.enabled`
86
+ - DB 可用时:用 `db_context.py` 生成精确上下文,完成后用 `db_sync.py` 同步
87
+ - DB 不可用或脚本执行失败时:自动回退到纯文件系统流程,不影响正常创作
88
+ - 文件系统 tracking 始终是主数据源,DB 是增强层
89
+
63
90
  ## 会话级资源复用
64
91
 
65
92
  不同命令的资源复用策略不同: