novelws 5.2.0 → 5.4.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 (91) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +53 -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 +51 -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 +26 -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 +6 -0
  16. package/dist/core/config.d.ts.map +1 -1
  17. package/dist/core/config.js +8 -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 +10 -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/dashboard/assets/ChaptersView-CkYRzkTt.css +1 -0
  72. package/templates/dashboard/assets/CharactersView-BuH15mT-.css +1 -0
  73. package/templates/dashboard/assets/DashboardView-qD5yG8Hj.css +1 -0
  74. package/templates/dashboard/assets/PlotsView-Cnx9_j0t.css +1 -0
  75. package/templates/dashboard/assets/ProtagonistView-DfWCFC3K.css +1 -0
  76. package/templates/dashboard/assets/RelationshipsView-DMtu4xH0.css +1 -0
  77. package/templates/dashboard/assets/TimelineView-CovxzAeu.css +1 -0
  78. package/templates/dashboard/assets/index-nD7kmLMb.css +1 -0
  79. package/templates/dashboard/index.html +13 -0
  80. package/templates/dot-claude/CLAUDE.md +27 -0
  81. package/templates/scripts/db_context.py +609 -0
  82. package/templates/scripts/db_init_protagonist.py +343 -0
  83. package/templates/scripts/db_sync.py +611 -0
  84. package/templates/scripts/db_volume_switch.py +278 -0
  85. package/templates/scripts/phase_a_init_db.py +428 -0
  86. package/templates/scripts/requirements.txt +1 -0
  87. package/templates/tracking/character-state.json +1 -3
  88. package/templates/tracking/plot-tracker.json +1 -5
  89. package/templates/tracking/relationships.json +1 -3
  90. package/templates/tracking/timeline.json +1 -3
  91. 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.4.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",
@@ -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
 
@@ -0,0 +1 @@
1
+ .view-header[data-v-df160007]{display:flex;justify-content:space-between;align-items:center}.filters[data-v-df160007]{display:flex;gap:10px}.chapter-card[data-v-df160007]{margin-bottom:12px}.ch-header[data-v-df160007]{display:flex;justify-content:space-between;align-items:center}.ch-number[data-v-df160007]{font-weight:700;font-size:14px}.ch-meta[data-v-df160007]{margin-top:6px;font-size:12px;color:#909399;display:flex;gap:12px}.ch-participants[data-v-df160007],.ch-badges[data-v-df160007]{margin-top:6px}.more[data-v-df160007]{font-size:12px;color:#909399}
@@ -0,0 +1 @@
1
+ .view-header[data-v-d7299ae6]{display:flex;justify-content:space-between;align-items:center}.filters[data-v-d7299ae6]{display:flex;gap:10px}.char-grid[data-v-d7299ae6]{margin-top:16px}.char-card[data-v-d7299ae6]{cursor:pointer;margin-bottom:16px;text-align:center}.char-avatar[data-v-d7299ae6]{width:60px;height:60px;border-radius:50%;background:#409eff;color:#fff;font-size:24px;line-height:60px;margin:0 auto 10px}.char-name[data-v-d7299ae6]{font-size:16px;font-weight:700;margin-bottom:6px}.faction-tag[data-v-d7299ae6]{margin-left:4px}.char-meta[data-v-d7299ae6]{margin-top:8px;font-size:12px;color:#909399;display:flex;justify-content:center;align-items:center;gap:8px}.meta[data-v-d7299ae6]{font-size:12px;color:#909399}
@@ -0,0 +1 @@
1
+ .stat-cards[data-v-736fa3cb]{margin-top:16px}.stat-card[data-v-736fa3cb]{text-align:center}.stat-value[data-v-736fa3cb]{font-size:28px;font-weight:700;color:#303133}.stat-label[data-v-736fa3cb]{font-size:13px;color:#909399;margin-top:4px}
@@ -0,0 +1 @@
1
+ .kanban[data-v-63b8f4a4]{display:flex;gap:16px}.kanban-column[data-v-63b8f4a4]{flex:1;min-width:0}.kanban-title[data-v-63b8f4a4]{display:flex;align-items:center;gap:8px;margin-bottom:10px}.kanban-count[data-v-63b8f4a4]{font-size:12px;color:#909399}.kanban-items[data-v-63b8f4a4]{display:flex;flex-direction:column;gap:8px}.kanban-item[data-v-63b8f4a4]{cursor:default}.plot-name[data-v-63b8f4a4]{font-weight:700;margin-bottom:4px}.plot-type[data-v-63b8f4a4]{font-size:12px;color:#909399}.plot-desc[data-v-63b8f4a4]{font-size:13px;margin-top:4px;color:#606266}.foreshadow-header[data-v-63b8f4a4]{display:flex;justify-content:space-between;align-items:center}.fs-stats[data-v-63b8f4a4]{display:flex;gap:8px}.matrix-wrapper[data-v-63b8f4a4]{overflow-x:auto}.matrix-table[data-v-63b8f4a4]{border-collapse:collapse;font-size:12px}.matrix-table th[data-v-63b8f4a4],.matrix-table td[data-v-63b8f4a4]{border:1px solid #ebeef5;padding:4px 6px;text-align:center}.matrix-label[data-v-63b8f4a4]{text-align:left;white-space:nowrap;max-width:100px;overflow:hidden;text-overflow:ellipsis}.matrix-ch[data-v-63b8f4a4]{min-width:28px;font-size:11px;color:#909399}.matrix-cell[data-v-63b8f4a4]{min-width:28px}.dot[data-v-63b8f4a4]{font-size:14px}.dot-plant[data-v-63b8f4a4]{color:#67c23a}.dot-hint[data-v-63b8f4a4]{color:#e6a23c}.dot-resolve[data-v-63b8f4a4]{color:#f56c6c}.matrix-legend[data-v-63b8f4a4]{margin-top:8px;display:flex;gap:16px;font-size:12px;color:#606266}
@@ -0,0 +1 @@
1
+ .stat-cards[data-v-c59ce1be]{margin-top:16px}.stat-card[data-v-c59ce1be]{text-align:center}.stat-value[data-v-c59ce1be]{font-size:28px;font-weight:700;color:#303133}.stat-label[data-v-c59ce1be]{font-size:13px;color:#909399;margin-top:4px}
@@ -0,0 +1 @@
1
+ .view-header[data-v-8f042607]{display:flex;justify-content:space-between;align-items:center}.filters[data-v-8f042607]{display:flex;gap:10px}.graph-card[data-v-8f042607]{margin-top:16px}.network-container[data-v-8f042607]{height:500px;border:1px solid #ebeef5;border-radius:4px}
@@ -0,0 +1 @@
1
+ .view-header[data-v-39de3e34]{display:flex;justify-content:space-between;align-items:center}.filters[data-v-39de3e34]{display:flex;gap:10px}.event-card[data-v-39de3e34]{cursor:pointer}.event-header[data-v-39de3e34]{display:flex;justify-content:space-between;align-items:center}.event-desc[data-v-39de3e34]{font-weight:500}.event-detail[data-v-39de3e34]{margin-top:8px}