@narrative-os/cli 0.1.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.
- package/dist/commands/bible.d.ts +1 -0
- package/dist/commands/bible.js +49 -0
- package/dist/commands/clone.d.ts +1 -0
- package/dist/commands/clone.js +37 -0
- package/dist/commands/config.d.ts +9 -0
- package/dist/commands/config.js +72 -0
- package/dist/commands/continue.d.ts +1 -0
- package/dist/commands/continue.js +54 -0
- package/dist/commands/delete.d.ts +1 -0
- package/dist/commands/delete.js +32 -0
- package/dist/commands/export.d.ts +1 -0
- package/dist/commands/export.js +99 -0
- package/dist/commands/generate.d.ts +1 -0
- package/dist/commands/generate.js +72 -0
- package/dist/commands/hint.d.ts +5 -0
- package/dist/commands/hint.js +68 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +27 -0
- package/dist/commands/list.d.ts +1 -0
- package/dist/commands/list.js +21 -0
- package/dist/commands/memories.d.ts +1 -0
- package/dist/commands/memories.js +59 -0
- package/dist/commands/read.d.ts +1 -0
- package/dist/commands/read.js +42 -0
- package/dist/commands/regenerate.d.ts +1 -0
- package/dist/commands/regenerate.js +60 -0
- package/dist/commands/state.d.ts +1 -0
- package/dist/commands/state.js +76 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +51 -0
- package/dist/commands/validate.d.ts +1 -0
- package/dist/commands/validate.js +93 -0
- package/dist/config/store.d.ts +24 -0
- package/dist/config/store.js +183 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +130 -0
- package/package.json +49 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readCommand = readCommand;
|
|
4
|
+
const store_js_1 = require("../config/store.js");
|
|
5
|
+
function readCommand(storyId, chapterNumber) {
|
|
6
|
+
const story = (0, store_js_1.loadStory)(storyId);
|
|
7
|
+
if (!story) {
|
|
8
|
+
console.error(`Story not found: ${storyId}`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const { bible, chapters } = story;
|
|
12
|
+
if (chapters.length === 0) {
|
|
13
|
+
console.log(`Story "${bible.title}" has no chapters yet.`);
|
|
14
|
+
console.log(`Run "nos generate ${storyId}" to write Chapter 1.`);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (chapterNumber) {
|
|
18
|
+
// Read specific chapter
|
|
19
|
+
const chapter = chapters.find(ch => ch.number === chapterNumber);
|
|
20
|
+
if (!chapter) {
|
|
21
|
+
console.error(`Chapter ${chapterNumber} not found.`);
|
|
22
|
+
console.log(`Available chapters: 1-${chapters.length}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
console.log(`\n# Chapter ${chapter.number}: ${chapter.title}`);
|
|
26
|
+
console.log(`Word count: ${chapter.wordCount}`);
|
|
27
|
+
console.log('─'.repeat(60));
|
|
28
|
+
console.log('');
|
|
29
|
+
console.log(chapter.content);
|
|
30
|
+
console.log('');
|
|
31
|
+
console.log('─'.repeat(60));
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
// List all chapters
|
|
35
|
+
console.log(`\nStory: ${bible.title}`);
|
|
36
|
+
console.log(`Total chapters: ${chapters.length}\n`);
|
|
37
|
+
for (const chapter of chapters) {
|
|
38
|
+
console.log(` Chapter ${chapter.number}: ${chapter.title} (${chapter.wordCount} words)`);
|
|
39
|
+
}
|
|
40
|
+
console.log(`\nUse "nos read ${storyId} <chapter-number>" to read a specific chapter.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function regenerateCommand(storyId: string, chapterNumber: number): Promise<void>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.regenerateCommand = regenerateCommand;
|
|
4
|
+
const engine_1 = require("@narrative-os/engine");
|
|
5
|
+
const store_js_1 = require("../config/store.js");
|
|
6
|
+
async function regenerateCommand(storyId, chapterNumber) {
|
|
7
|
+
const story = (0, store_js_1.loadStory)(storyId);
|
|
8
|
+
if (!story) {
|
|
9
|
+
console.error(`Story not found: ${storyId}`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const { bible, state, chapters, canon } = story;
|
|
13
|
+
// Find the chapter to regenerate
|
|
14
|
+
const chapterIndex = chapters.findIndex(ch => ch.number === chapterNumber);
|
|
15
|
+
if (chapterIndex === -1) {
|
|
16
|
+
console.error(`Chapter ${chapterNumber} not found.`);
|
|
17
|
+
console.log(`Available chapters: 1-${chapters.length}`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
console.log(`Regenerating Chapter ${chapterNumber}...`);
|
|
21
|
+
console.log(`Current title: ${chapters[chapterIndex].title}\n`);
|
|
22
|
+
// Load or initialize vector store
|
|
23
|
+
const vectorStore = (0, engine_1.getVectorStore)(storyId);
|
|
24
|
+
const existingData = (0, store_js_1.loadVectorStore)(storyId);
|
|
25
|
+
if (existingData) {
|
|
26
|
+
await vectorStore.load(existingData);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
await vectorStore.initialize();
|
|
30
|
+
}
|
|
31
|
+
// Create context - use state as it was BEFORE this chapter
|
|
32
|
+
const context = {
|
|
33
|
+
bible,
|
|
34
|
+
state: {
|
|
35
|
+
...state,
|
|
36
|
+
currentChapter: chapterNumber - 1, // Pretend we're at previous chapter
|
|
37
|
+
},
|
|
38
|
+
chapterNumber,
|
|
39
|
+
targetWordCount: 1500,
|
|
40
|
+
};
|
|
41
|
+
try {
|
|
42
|
+
const result = await (0, engine_1.generateChapter)(context, { canon, vectorStore });
|
|
43
|
+
// Replace the old chapter
|
|
44
|
+
const newChapters = [...chapters];
|
|
45
|
+
newChapters[chapterIndex] = result.chapter;
|
|
46
|
+
(0, store_js_1.saveStory)(bible, state, newChapters, canon);
|
|
47
|
+
(0, store_js_1.saveVectorStore)(storyId, vectorStore.serialize());
|
|
48
|
+
console.log(`\n✅ Chapter ${chapterNumber} regenerated!`);
|
|
49
|
+
console.log(`New title: ${result.chapter.title}`);
|
|
50
|
+
console.log(`Words: ${result.chapter.wordCount}`);
|
|
51
|
+
if (result.violations.length > 0) {
|
|
52
|
+
console.log(`⚠️ Canon violations: ${result.violations.length}`);
|
|
53
|
+
}
|
|
54
|
+
console.log(`\nSummary: ${result.summary.summary}`);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
console.error('Regeneration failed:', error);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function stateCommand(storyId: string): void;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stateCommand = stateCommand;
|
|
4
|
+
const store_js_1 = require("../config/store.js");
|
|
5
|
+
function stateCommand(storyId) {
|
|
6
|
+
const story = (0, store_js_1.loadStory)(storyId);
|
|
7
|
+
const structuredState = (0, store_js_1.loadStructuredState)(storyId);
|
|
8
|
+
if (!story) {
|
|
9
|
+
console.error(`Story not found: ${storyId}`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const { bible, state } = story;
|
|
13
|
+
console.log(`\n# Story State: ${bible.title}`);
|
|
14
|
+
console.log('─'.repeat(60));
|
|
15
|
+
console.log(`\n## Progress`);
|
|
16
|
+
console.log(` Current Chapter: ${state.currentChapter} / ${state.totalChapters}`);
|
|
17
|
+
const progress = Math.round((state.currentChapter / state.totalChapters) * 100);
|
|
18
|
+
console.log(` Progress: ${progress}%`);
|
|
19
|
+
console.log(` Current Tension: ${(state.currentTension * 100).toFixed(0)}%`);
|
|
20
|
+
if (structuredState) {
|
|
21
|
+
console.log(`\n## Characters`);
|
|
22
|
+
console.log('─'.repeat(60));
|
|
23
|
+
const characters = Object.values(structuredState.characters);
|
|
24
|
+
if (characters.length === 0) {
|
|
25
|
+
console.log(' No character states tracked.');
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
for (const char of characters) {
|
|
29
|
+
console.log(`\n ${char.name}`);
|
|
30
|
+
console.log(` Emotional State: ${char.emotionalState}`);
|
|
31
|
+
console.log(` Location: ${char.location}`);
|
|
32
|
+
console.log(` Goals: ${char.goals.join(', ')}`);
|
|
33
|
+
if (char.knowledge.length > 0) {
|
|
34
|
+
console.log(` Knowledge: ${char.knowledge.length} facts`);
|
|
35
|
+
}
|
|
36
|
+
if (Object.keys(char.relationships).length > 0) {
|
|
37
|
+
console.log(` Relationships: ${Object.entries(char.relationships).map(([k, v]) => `${k} (${v})`).join(', ')}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
console.log(`\n## Plot Threads`);
|
|
42
|
+
console.log('─'.repeat(60));
|
|
43
|
+
const threads = Object.values(structuredState.plotThreads);
|
|
44
|
+
if (threads.length === 0) {
|
|
45
|
+
console.log(' No plot threads tracked.');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
for (const thread of threads) {
|
|
49
|
+
console.log(`\n ${thread.name}`);
|
|
50
|
+
console.log(` Status: ${thread.status}`);
|
|
51
|
+
console.log(` Tension: ${(thread.tension * 100).toFixed(0)}%`);
|
|
52
|
+
console.log(` Last Chapter: ${thread.lastChapter}`);
|
|
53
|
+
console.log(` Summary: ${thread.summary.substring(0, 60)}...`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (structuredState.unresolvedQuestions.length > 0) {
|
|
57
|
+
console.log(`\n## Unresolved Questions`);
|
|
58
|
+
console.log('─'.repeat(60));
|
|
59
|
+
for (const q of structuredState.unresolvedQuestions) {
|
|
60
|
+
console.log(` • ${q}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (structuredState.recentEvents.length > 0) {
|
|
64
|
+
console.log(`\n## Recent Events`);
|
|
65
|
+
console.log('─'.repeat(60));
|
|
66
|
+
for (const e of structuredState.recentEvents.slice(-5)) {
|
|
67
|
+
console.log(` • ${e.substring(0, 80)}...`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log('\n No structured state available.');
|
|
73
|
+
console.log(' Generate some chapters to track state.');
|
|
74
|
+
}
|
|
75
|
+
console.log('');
|
|
76
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function statusCommand(storyId?: string): void;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.statusCommand = statusCommand;
|
|
4
|
+
const store_js_1 = require("../config/store.js");
|
|
5
|
+
function statusCommand(storyId) {
|
|
6
|
+
if (!storyId) {
|
|
7
|
+
const stories = (0, store_js_1.listStories)();
|
|
8
|
+
if (stories.length === 0) {
|
|
9
|
+
console.log('No stories found.\n');
|
|
10
|
+
console.log('💡 Get started:');
|
|
11
|
+
console.log(' • Create a new story: nos init --title "My Story"');
|
|
12
|
+
console.log(' • Or with options: nos init --help');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
console.log('Stories:\n');
|
|
16
|
+
for (const story of stories) {
|
|
17
|
+
const progress = Math.round((story.currentChapter / story.totalChapters) * 100);
|
|
18
|
+
console.log(` ${story.id}`);
|
|
19
|
+
console.log(` Title: ${story.title}`);
|
|
20
|
+
console.log(` Progress: ${story.currentChapter}/${story.totalChapters} (${progress}%)`);
|
|
21
|
+
console.log('');
|
|
22
|
+
}
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const story = (0, store_js_1.loadStory)(storyId);
|
|
26
|
+
if (!story) {
|
|
27
|
+
console.error(`Story not found: ${storyId}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const { bible, state, chapters } = story;
|
|
31
|
+
const progress = Math.round((state.currentChapter / state.totalChapters) * 100);
|
|
32
|
+
console.log(`Story: ${bible.title}`);
|
|
33
|
+
console.log(`ID: ${bible.id}`);
|
|
34
|
+
console.log(`Theme: ${bible.theme}`);
|
|
35
|
+
console.log(`Genre: ${bible.genre}`);
|
|
36
|
+
console.log(`Setting: ${bible.setting}`);
|
|
37
|
+
console.log(`\nProgress: ${state.currentChapter}/${state.totalChapters} (${progress}%)`);
|
|
38
|
+
console.log(`Current Tension: ${(state.currentTension * 100).toFixed(0)}%`);
|
|
39
|
+
if (chapters.length > 0) {
|
|
40
|
+
console.log('\nChapters:');
|
|
41
|
+
for (const ch of chapters) {
|
|
42
|
+
console.log(` ${ch.number}. ${ch.title} (${ch.wordCount} words)`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (state.chapterSummaries.length > 0) {
|
|
46
|
+
console.log('\nRecent Summaries:');
|
|
47
|
+
for (const sum of state.chapterSummaries.slice(-3)) {
|
|
48
|
+
console.log(` Ch ${sum.chapterNumber}: ${sum.summary.substring(0, 80)}...`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function validateCommand(storyId: string): Promise<void>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateCommand = validateCommand;
|
|
4
|
+
const engine_1 = require("@narrative-os/engine");
|
|
5
|
+
const store_js_1 = require("../config/store.js");
|
|
6
|
+
async function validateCommand(storyId) {
|
|
7
|
+
const story = (0, store_js_1.loadStory)(storyId);
|
|
8
|
+
if (!story) {
|
|
9
|
+
console.error(`Story not found: ${storyId}`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const { bible, state, chapters, canon, structuredState } = story;
|
|
13
|
+
console.log(`\nValidating Story: ${bible.title}`);
|
|
14
|
+
console.log('─'.repeat(60));
|
|
15
|
+
// Load constraint graph if exists
|
|
16
|
+
const constraintGraph = (0, engine_1.createConstraintGraph)();
|
|
17
|
+
const graphData = (0, store_js_1.loadConstraintGraph)(storyId);
|
|
18
|
+
if (graphData) {
|
|
19
|
+
constraintGraph.load(graphData);
|
|
20
|
+
}
|
|
21
|
+
// Load vector store if exists
|
|
22
|
+
const vectorStore = (0, engine_1.getVectorStore)(storyId);
|
|
23
|
+
const vectorData = (0, store_js_1.loadVectorStore)(storyId);
|
|
24
|
+
if (vectorData) {
|
|
25
|
+
await vectorStore.load(vectorData);
|
|
26
|
+
}
|
|
27
|
+
console.log(`\n## Story Statistics`);
|
|
28
|
+
console.log(` Chapters: ${chapters.length} / ${state.totalChapters}`);
|
|
29
|
+
console.log(` Canon Facts: ${canon.facts.length}`);
|
|
30
|
+
console.log(` Constraint Graph: ${constraintGraph.getStats().nodes} nodes, ${constraintGraph.getStats().edges} edges`);
|
|
31
|
+
console.log(` Vector Memories: ${vectorStore.getAllMemories().length}`);
|
|
32
|
+
// Validate each chapter
|
|
33
|
+
if (chapters.length > 0 && structuredState) {
|
|
34
|
+
console.log(`\n## Chapter Validation`);
|
|
35
|
+
console.log('─'.repeat(60));
|
|
36
|
+
const validator = new engine_1.Validator(constraintGraph);
|
|
37
|
+
let totalViolations = 0;
|
|
38
|
+
for (const chapter of chapters) {
|
|
39
|
+
const result = validator.quickValidate({
|
|
40
|
+
chapter,
|
|
41
|
+
bible,
|
|
42
|
+
structuredState,
|
|
43
|
+
canon,
|
|
44
|
+
previousChapters: chapters.filter(ch => ch.number < chapter.number),
|
|
45
|
+
constraintGraph,
|
|
46
|
+
});
|
|
47
|
+
const errors = result.violations.filter(v => v.severity === 'error').length;
|
|
48
|
+
const warnings = result.violations.filter(v => v.severity === 'warning').length;
|
|
49
|
+
if (errors > 0 || warnings > 0) {
|
|
50
|
+
console.log(`\n Chapter ${chapter.number}: ${chapter.title}`);
|
|
51
|
+
if (errors > 0)
|
|
52
|
+
console.log(` ❌ ${errors} error(s)`);
|
|
53
|
+
if (warnings > 0)
|
|
54
|
+
console.log(` ⚠️ ${warnings} warning(s)`);
|
|
55
|
+
totalViolations += result.violations.length;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (totalViolations === 0) {
|
|
59
|
+
console.log(' ✅ All chapters pass validation!');
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(`\n Total: ${totalViolations} violation(s) found`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Check for common issues
|
|
66
|
+
console.log(`\n## Consistency Checks`);
|
|
67
|
+
console.log('─'.repeat(60));
|
|
68
|
+
const issues = [];
|
|
69
|
+
// Check for chapters without summaries
|
|
70
|
+
const noSummary = chapters.filter(ch => !ch.summary || ch.summary.length < 10);
|
|
71
|
+
if (noSummary.length > 0) {
|
|
72
|
+
issues.push(`${noSummary.length} chapter(s) missing summaries`);
|
|
73
|
+
}
|
|
74
|
+
// Check for very short chapters
|
|
75
|
+
const shortChapters = chapters.filter(ch => ch.wordCount < 500);
|
|
76
|
+
if (shortChapters.length > 0) {
|
|
77
|
+
issues.push(`${shortChapters.length} chapter(s) unusually short (< 500 words)`);
|
|
78
|
+
}
|
|
79
|
+
// Check for canon facts without chapters
|
|
80
|
+
const orphanedFacts = canon.facts.filter(f => f.chapterEstablished > state.currentChapter);
|
|
81
|
+
if (orphanedFacts.length > 0) {
|
|
82
|
+
issues.push(`${orphanedFacts.length} canon fact(s) from future chapters`);
|
|
83
|
+
}
|
|
84
|
+
if (issues.length === 0) {
|
|
85
|
+
console.log(' ✅ No issues found!');
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
for (const issue of issues) {
|
|
89
|
+
console.log(` ⚠️ ${issue}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
console.log('\n');
|
|
93
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { StoryBible, StoryState, Chapter, CanonStore, StoryStructuredState } from '@narrative-os/engine';
|
|
2
|
+
export declare function saveStory(bible: StoryBible, state: StoryState, chapters: Chapter[], canon?: CanonStore, structuredState?: StoryStructuredState): void;
|
|
3
|
+
export declare function loadStory(storyId: string): {
|
|
4
|
+
bible: StoryBible;
|
|
5
|
+
state: StoryState;
|
|
6
|
+
chapters: Chapter[];
|
|
7
|
+
canon: CanonStore;
|
|
8
|
+
structuredState: StoryStructuredState | null;
|
|
9
|
+
} | null;
|
|
10
|
+
export declare function listStories(): {
|
|
11
|
+
id: string;
|
|
12
|
+
title: string;
|
|
13
|
+
currentChapter: number;
|
|
14
|
+
totalChapters: number;
|
|
15
|
+
}[];
|
|
16
|
+
export declare function saveVectorStore(storyId: string, data: string): void;
|
|
17
|
+
export declare function loadVectorStore(storyId: string): string | null;
|
|
18
|
+
export declare function saveStructuredState(storyId: string, state: StoryStructuredState): void;
|
|
19
|
+
export declare function loadStructuredState(storyId: string): StoryStructuredState | null;
|
|
20
|
+
export declare function initializeStructuredState(storyId: string, bible: StoryBible): StoryStructuredState;
|
|
21
|
+
export declare function saveConstraintGraph(storyId: string, data: string): void;
|
|
22
|
+
export declare function loadConstraintGraph(storyId: string): string | null;
|
|
23
|
+
export declare function saveWorldState(storyId: string, data: string): void;
|
|
24
|
+
export declare function loadWorldState(storyId: string): string | null;
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.saveStory = saveStory;
|
|
4
|
+
exports.loadStory = loadStory;
|
|
5
|
+
exports.listStories = listStories;
|
|
6
|
+
exports.saveVectorStore = saveVectorStore;
|
|
7
|
+
exports.loadVectorStore = loadVectorStore;
|
|
8
|
+
exports.saveStructuredState = saveStructuredState;
|
|
9
|
+
exports.loadStructuredState = loadStructuredState;
|
|
10
|
+
exports.initializeStructuredState = initializeStructuredState;
|
|
11
|
+
exports.saveConstraintGraph = saveConstraintGraph;
|
|
12
|
+
exports.loadConstraintGraph = loadConstraintGraph;
|
|
13
|
+
exports.saveWorldState = saveWorldState;
|
|
14
|
+
exports.loadWorldState = loadWorldState;
|
|
15
|
+
const fs_1 = require("fs");
|
|
16
|
+
const path_1 = require("path");
|
|
17
|
+
const os_1 = require("os");
|
|
18
|
+
const engine_1 = require("@narrative-os/engine");
|
|
19
|
+
const DATA_DIR = (0, path_1.join)((0, os_1.homedir)(), '.narrative-os');
|
|
20
|
+
const STORIES_DIR = (0, path_1.join)(DATA_DIR, 'stories');
|
|
21
|
+
function ensureDirs() {
|
|
22
|
+
if (!(0, fs_1.existsSync)(DATA_DIR))
|
|
23
|
+
(0, fs_1.mkdirSync)(DATA_DIR, { recursive: true });
|
|
24
|
+
if (!(0, fs_1.existsSync)(STORIES_DIR))
|
|
25
|
+
(0, fs_1.mkdirSync)(STORIES_DIR, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
function saveStory(bible, state, chapters, canon, structuredState) {
|
|
28
|
+
ensureDirs();
|
|
29
|
+
const storyDir = (0, path_1.join)(STORIES_DIR, bible.id);
|
|
30
|
+
if (!(0, fs_1.existsSync)(storyDir))
|
|
31
|
+
(0, fs_1.mkdirSync)(storyDir, { recursive: true });
|
|
32
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(storyDir, 'bible.json'), JSON.stringify(bible, null, 2));
|
|
33
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(storyDir, 'state.json'), JSON.stringify(state, null, 2));
|
|
34
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(storyDir, 'chapters.json'), JSON.stringify(chapters, null, 2));
|
|
35
|
+
const canonToSave = canon || (0, engine_1.extractCanonFromBible)(bible);
|
|
36
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(storyDir, 'canon.json'), JSON.stringify(canonToSave, null, 2));
|
|
37
|
+
// Save structured state if provided
|
|
38
|
+
if (structuredState) {
|
|
39
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(storyDir, 'structured-state.json'), JSON.stringify(structuredState, null, 2));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function loadStory(storyId) {
|
|
43
|
+
const storyDir = (0, path_1.join)(STORIES_DIR, storyId);
|
|
44
|
+
if (!(0, fs_1.existsSync)(storyDir))
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
const bible = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(storyDir, 'bible.json'), 'utf-8'));
|
|
48
|
+
const state = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(storyDir, 'state.json'), 'utf-8'));
|
|
49
|
+
const chapters = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(storyDir, 'chapters.json'), 'utf-8'));
|
|
50
|
+
let canon;
|
|
51
|
+
const canonPath = (0, path_1.join)(storyDir, 'canon.json');
|
|
52
|
+
if ((0, fs_1.existsSync)(canonPath)) {
|
|
53
|
+
canon = JSON.parse((0, fs_1.readFileSync)(canonPath, 'utf-8'));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
canon = (0, engine_1.extractCanonFromBible)(bible);
|
|
57
|
+
}
|
|
58
|
+
// Load structured state
|
|
59
|
+
let structuredState = null;
|
|
60
|
+
const structuredPath = (0, path_1.join)(storyDir, 'structured-state.json');
|
|
61
|
+
if ((0, fs_1.existsSync)(structuredPath)) {
|
|
62
|
+
structuredState = JSON.parse((0, fs_1.readFileSync)(structuredPath, 'utf-8'));
|
|
63
|
+
}
|
|
64
|
+
return { bible, state, chapters, canon, structuredState };
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function listStories() {
|
|
71
|
+
ensureDirs();
|
|
72
|
+
const stories = [];
|
|
73
|
+
for (const dir of (0, fs_1.existsSync)(STORIES_DIR) ? (0, fs_1.readdirSync)(STORIES_DIR) : []) {
|
|
74
|
+
const storyPath = (0, path_1.join)(STORIES_DIR, dir);
|
|
75
|
+
const statePath = (0, path_1.join)(storyPath, 'state.json');
|
|
76
|
+
const biblePath = (0, path_1.join)(storyPath, 'bible.json');
|
|
77
|
+
if ((0, fs_1.existsSync)(statePath) && (0, fs_1.existsSync)(biblePath)) {
|
|
78
|
+
try {
|
|
79
|
+
const state = JSON.parse((0, fs_1.readFileSync)(statePath, 'utf-8'));
|
|
80
|
+
const bible = JSON.parse((0, fs_1.readFileSync)(biblePath, 'utf-8'));
|
|
81
|
+
stories.push({
|
|
82
|
+
id: dir,
|
|
83
|
+
title: bible.title,
|
|
84
|
+
currentChapter: state.currentChapter,
|
|
85
|
+
totalChapters: state.totalChapters,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch { }
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return stories;
|
|
92
|
+
}
|
|
93
|
+
// Vector store persistence
|
|
94
|
+
function saveVectorStore(storyId, data) {
|
|
95
|
+
ensureDirs();
|
|
96
|
+
const storyDir = (0, path_1.join)(STORIES_DIR, storyId);
|
|
97
|
+
if (!(0, fs_1.existsSync)(storyDir))
|
|
98
|
+
(0, fs_1.mkdirSync)(storyDir, { recursive: true });
|
|
99
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(storyDir, 'vector-store.json'), data);
|
|
100
|
+
}
|
|
101
|
+
function loadVectorStore(storyId) {
|
|
102
|
+
const storyDir = (0, path_1.join)(STORIES_DIR, storyId);
|
|
103
|
+
const vectorPath = (0, path_1.join)(storyDir, 'vector-store.json');
|
|
104
|
+
if (!(0, fs_1.existsSync)(vectorPath))
|
|
105
|
+
return null;
|
|
106
|
+
try {
|
|
107
|
+
return (0, fs_1.readFileSync)(vectorPath, 'utf-8');
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Structured state persistence
|
|
114
|
+
function saveStructuredState(storyId, state) {
|
|
115
|
+
ensureDirs();
|
|
116
|
+
const storyDir = (0, path_1.join)(STORIES_DIR, storyId);
|
|
117
|
+
if (!(0, fs_1.existsSync)(storyDir))
|
|
118
|
+
(0, fs_1.mkdirSync)(storyDir, { recursive: true });
|
|
119
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(storyDir, 'structured-state.json'), JSON.stringify(state, null, 2));
|
|
120
|
+
}
|
|
121
|
+
function loadStructuredState(storyId) {
|
|
122
|
+
const storyDir = (0, path_1.join)(STORIES_DIR, storyId);
|
|
123
|
+
const statePath = (0, path_1.join)(storyDir, 'structured-state.json');
|
|
124
|
+
if (!(0, fs_1.existsSync)(statePath))
|
|
125
|
+
return null;
|
|
126
|
+
try {
|
|
127
|
+
return JSON.parse((0, fs_1.readFileSync)(statePath, 'utf-8'));
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Initialize structured state from bible if it doesn't exist
|
|
134
|
+
function initializeStructuredState(storyId, bible) {
|
|
135
|
+
let state = loadStructuredState(storyId);
|
|
136
|
+
if (!state) {
|
|
137
|
+
state = (0, engine_1.createStructuredState)(storyId);
|
|
138
|
+
state = (0, engine_1.initializeCharactersFromBible)(state, bible);
|
|
139
|
+
state = (0, engine_1.initializePlotThreadsFromBible)(state, bible);
|
|
140
|
+
saveStructuredState(storyId, state);
|
|
141
|
+
}
|
|
142
|
+
return state;
|
|
143
|
+
}
|
|
144
|
+
// Constraint graph persistence
|
|
145
|
+
function saveConstraintGraph(storyId, data) {
|
|
146
|
+
ensureDirs();
|
|
147
|
+
const storyDir = (0, path_1.join)(STORIES_DIR, storyId);
|
|
148
|
+
if (!(0, fs_1.existsSync)(storyDir))
|
|
149
|
+
(0, fs_1.mkdirSync)(storyDir, { recursive: true });
|
|
150
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(storyDir, 'constraint-graph.json'), data);
|
|
151
|
+
}
|
|
152
|
+
function loadConstraintGraph(storyId) {
|
|
153
|
+
const storyDir = (0, path_1.join)(STORIES_DIR, storyId);
|
|
154
|
+
const graphPath = (0, path_1.join)(storyDir, 'constraint-graph.json');
|
|
155
|
+
if (!(0, fs_1.existsSync)(graphPath))
|
|
156
|
+
return null;
|
|
157
|
+
try {
|
|
158
|
+
return (0, fs_1.readFileSync)(graphPath, 'utf-8');
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// World state persistence
|
|
165
|
+
function saveWorldState(storyId, data) {
|
|
166
|
+
ensureDirs();
|
|
167
|
+
const storyDir = (0, path_1.join)(STORIES_DIR, storyId);
|
|
168
|
+
if (!(0, fs_1.existsSync)(storyDir))
|
|
169
|
+
(0, fs_1.mkdirSync)(storyDir, { recursive: true });
|
|
170
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(storyDir, 'world-state.json'), data);
|
|
171
|
+
}
|
|
172
|
+
function loadWorldState(storyId) {
|
|
173
|
+
const storyDir = (0, path_1.join)(STORIES_DIR, storyId);
|
|
174
|
+
const worldPath = (0, path_1.join)(storyDir, 'world-state.json');
|
|
175
|
+
if (!(0, fs_1.existsSync)(worldPath))
|
|
176
|
+
return null;
|
|
177
|
+
try {
|
|
178
|
+
return (0, fs_1.readFileSync)(worldPath, 'utf-8');
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const init_js_1 = require("./commands/init.js");
|
|
6
|
+
const generate_js_1 = require("./commands/generate.js");
|
|
7
|
+
const status_js_1 = require("./commands/status.js");
|
|
8
|
+
const config_js_1 = require("./commands/config.js");
|
|
9
|
+
const list_js_1 = require("./commands/list.js");
|
|
10
|
+
const delete_js_1 = require("./commands/delete.js");
|
|
11
|
+
const clone_js_1 = require("./commands/clone.js");
|
|
12
|
+
const export_js_1 = require("./commands/export.js");
|
|
13
|
+
const read_js_1 = require("./commands/read.js");
|
|
14
|
+
const bible_js_1 = require("./commands/bible.js");
|
|
15
|
+
const state_js_1 = require("./commands/state.js");
|
|
16
|
+
const hint_js_1 = require("./commands/hint.js");
|
|
17
|
+
(0, config_js_1.applyConfig)();
|
|
18
|
+
const program = new commander_1.Command();
|
|
19
|
+
program
|
|
20
|
+
.name('nos')
|
|
21
|
+
.description('Narrative OS - AI-powered story generation')
|
|
22
|
+
.version('0.1.0');
|
|
23
|
+
// Show welcome when no args provided
|
|
24
|
+
if (process.argv.length <= 2) {
|
|
25
|
+
(0, hint_js_1.showWelcome)();
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
// Configuration
|
|
29
|
+
program
|
|
30
|
+
.command('config')
|
|
31
|
+
.description('Configure LLM provider and API key')
|
|
32
|
+
.action(config_js_1.configCommand);
|
|
33
|
+
// Story Management
|
|
34
|
+
program
|
|
35
|
+
.command('init')
|
|
36
|
+
.description('Create a new story')
|
|
37
|
+
.option('-t, --title <title>', 'Story title')
|
|
38
|
+
.option('--theme <theme>', 'Story theme')
|
|
39
|
+
.option('-g, --genre <genre>', 'Genre')
|
|
40
|
+
.option('-s, --setting <setting>', 'Setting')
|
|
41
|
+
.option('--tone <tone>', 'Tone')
|
|
42
|
+
.option('-p, --premise <premise>', 'Story premise')
|
|
43
|
+
.option('-c, --chapters <number>', 'Target chapter count', '5')
|
|
44
|
+
.action(init_js_1.initCommand);
|
|
45
|
+
program
|
|
46
|
+
.command('list')
|
|
47
|
+
.alias('ls')
|
|
48
|
+
.description('List all stories')
|
|
49
|
+
.action(list_js_1.listCommand);
|
|
50
|
+
program
|
|
51
|
+
.command('status [story-id]')
|
|
52
|
+
.description('Show story status (or list all stories)')
|
|
53
|
+
.action(status_js_1.statusCommand);
|
|
54
|
+
program
|
|
55
|
+
.command('delete <story-id>')
|
|
56
|
+
.description('Delete a story')
|
|
57
|
+
.option('-f, --force', 'Skip confirmation')
|
|
58
|
+
.action((storyId, options) => {
|
|
59
|
+
(0, delete_js_1.deleteCommand)(storyId, options.force);
|
|
60
|
+
});
|
|
61
|
+
program
|
|
62
|
+
.command('clone <story-id> <new-title>')
|
|
63
|
+
.description('Clone a story as a template')
|
|
64
|
+
.action(clone_js_1.cloneCommand);
|
|
65
|
+
// Generation
|
|
66
|
+
program
|
|
67
|
+
.command('generate <story-id>')
|
|
68
|
+
.alias('gen')
|
|
69
|
+
.description('Generate the next chapter')
|
|
70
|
+
.action(generate_js_1.generateCommand);
|
|
71
|
+
program
|
|
72
|
+
.command('continue <story-id>')
|
|
73
|
+
.description('Generate all remaining chapters')
|
|
74
|
+
.action(async (storyId) => {
|
|
75
|
+
const { continueCommand } = await import('./commands/continue.js');
|
|
76
|
+
await continueCommand(storyId);
|
|
77
|
+
});
|
|
78
|
+
program
|
|
79
|
+
.command('regenerate <story-id> <chapter-number>')
|
|
80
|
+
.alias('regen')
|
|
81
|
+
.description('Regenerate a specific chapter')
|
|
82
|
+
.action(async (storyId, chapterNum) => {
|
|
83
|
+
const { regenerateCommand } = await import('./commands/regenerate.js');
|
|
84
|
+
await regenerateCommand(storyId, parseInt(chapterNum));
|
|
85
|
+
});
|
|
86
|
+
// Reading & Export
|
|
87
|
+
program
|
|
88
|
+
.command('read <story-id> [chapter-number]')
|
|
89
|
+
.description('Read chapter content (or list chapters)')
|
|
90
|
+
.action((storyId, chapterNum) => {
|
|
91
|
+
(0, read_js_1.readCommand)(storyId, chapterNum ? parseInt(chapterNum) : undefined);
|
|
92
|
+
});
|
|
93
|
+
program
|
|
94
|
+
.command('export <story-id>')
|
|
95
|
+
.description('Export story to file')
|
|
96
|
+
.option('-f, --format <format>', 'Export format (markdown|txt)', 'markdown')
|
|
97
|
+
.option('-o, --output <file>', 'Output filename')
|
|
98
|
+
.action((storyId, options) => {
|
|
99
|
+
(0, export_js_1.exportCommand)(storyId, options.format, options.output);
|
|
100
|
+
});
|
|
101
|
+
// Bible & State
|
|
102
|
+
program
|
|
103
|
+
.command('bible <story-id>')
|
|
104
|
+
.description('View story bible (characters, setting, etc.)')
|
|
105
|
+
.action(bible_js_1.bibleCommand);
|
|
106
|
+
program
|
|
107
|
+
.command('state <story-id>')
|
|
108
|
+
.description('View structured story state')
|
|
109
|
+
.action(state_js_1.stateCommand);
|
|
110
|
+
program
|
|
111
|
+
.command('memories <story-id> [query]')
|
|
112
|
+
.description('Search narrative memories')
|
|
113
|
+
.action(async (storyId, query) => {
|
|
114
|
+
const { memoriesCommand } = await import('./commands/memories.js');
|
|
115
|
+
await memoriesCommand(storyId, query);
|
|
116
|
+
});
|
|
117
|
+
program
|
|
118
|
+
.command('validate <story-id>')
|
|
119
|
+
.description('Validate story consistency')
|
|
120
|
+
.action(async (storyId) => {
|
|
121
|
+
const { validateCommand } = await import('./commands/validate.js');
|
|
122
|
+
await validateCommand(storyId);
|
|
123
|
+
});
|
|
124
|
+
program
|
|
125
|
+
.command('hint [story-id]')
|
|
126
|
+
.description('Show helpful hints and suggestions')
|
|
127
|
+
.action((storyId) => {
|
|
128
|
+
(0, hint_js_1.showHint)({ storyId });
|
|
129
|
+
});
|
|
130
|
+
program.parse();
|